在 Docker 下运行

本指南假设您对 Docker 和 Docker 命令行有基本的了解。它描述了 Node-RED 在 Docker 下运行的多种方式,并支持多种架构(amd64、arm32v6、arm32v7、arm64v8 和 s390x)。

自 Node-RED 1.0 版本起,Docker Hub 上的存储库已重命名为 nodered/node-red

快速启动

要以最简单的形式在 Docker 中运行,只需运行

    docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered nodered/node-red

让我们剖析一下这个命令

    docker run              - run this container, initially building locally if necessary
    -it                     - attach a terminal session so we can see what is going on
    -p 1880:1880            - connect local port 1880 to the exposed internal port 1880
    -v node_red_data:/data  - mount a docker named volume called `node_red_data` to the container /data directory so any changes made to flows are persisted
    --name mynodered        - give this machine a friendly local name
    nodered/node-red        - the image to base it on - currently Node-RED v1.2.0

运行该命令后,会显示一个终端窗口,其中包含一个正在运行的 Node-RED 实例。

    Welcome to Node-RED
    ===================

    10 Oct 12:57:10 - [info] Node-RED version: v1.2.0
    10 Oct 12:57:10 - [info] Node.js  version: v10.22.1
    10 Oct 12:57:10 - [info] Linux 4.19.76-linuxkit x64 LE
    10 Oct 12:57:11 - [info] Loading palette nodes
    10 Oct 12:57:16 - [info] Settings file  : /data/settings.js
    10 Oct 12:57:16 - [info] Context store  : 'default' [module=memory]
    10 Oct 12:57:16 - [info] User directory : /data
    10 Oct 12:57:16 - [warn] Projects disabled : editorTheme.projects.enabled=false
    10 Oct 12:57:16 - [info] Flows file     : /data/flows.json
    10 Oct 12:57:16 - [info] Creating new flow file
    10 Oct 12:57:17 - [warn]

    ---------------------------------------------------------------------
    Your flow credentials file is encrypted using a system-generated key.

    If the system-generated key is lost for any reason, your credentials
    file will not be recoverable, you will have to delete it and re-enter
    your credentials.

    You should set your own key using the 'credentialSecret' option in
    your settings file. Node-RED will then re-encrypt your credentials
    file using your chosen key the next time you deploy a change.
    ---------------------------------------------------------------------

    10 Oct 12:57:17 - [info] Starting flows
    10 Oct 12:57:17 - [info] Started flows
    10 Oct 12:57:17 - [info] Server now running at http://127.0.0.1:1880/

    [...]

然后您可以浏览到 http://{host-ip}:1880 以获取熟悉的 Node-RED 桌面。

这样做的好处是,通过给它一个名称(mynodered),我们可以更轻松地操作它,并且通过固定主机端口,我们知道我们处于熟悉的环境中。当然,这意味着我们一次只能运行一个实例……但我们一步一个脚印地来。

如果我们对所看到的一切感到满意,我们可以使用 Ctrl-p Ctrl-q 分离终端 - 容器将继续在后台运行。

要重新附加到终端(以查看日志),请运行

docker attach mynodered

如果需要重新启动容器(例如在重启或 Docker 守护进程重启后)

docker start mynodered

并在需要时再次停止它

docker stop mynodered

镜像变体

Node-RED 镜像基于 官方 Node JS Alpine Linux 镜像,以使其尽可能小。使用 Alpine Linux 减少了构建镜像的大小,但删除了原生模块编译所需的标准依赖项。如果您想添加具有原生依赖项的依赖项,请在运行容器时使用缺失的包扩展 Node-RED 镜像,或构建新镜像,请参阅 docker-custom,其中扩展了 Node-RED Docker 项目中的 README.md

有关详细的镜像、标签和清单信息,请参阅 Github 项目 README

例如:假设您正在 Raspberry PI 3B 上运行,其架构为 arm32v7。那么只需运行以下命令即可拉取镜像(标签为 1.2.0-10-arm32v7),并运行容器。

docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered nodered/node-red:latest

相同的命令可用于在 amd64 系统上运行,因为 Docker 会发现它正在 amd64 主机上运行并拉取具有匹配标签(1.2.0-10-amd64)的镜像。

这样做的好处是您不需要知道/指定您正在运行的架构,并使 docker 运行命令和 docker compose 文件在系统之间更灵活和可交换。

注意:目前 Docker 的架构检测存在一个错误,对于 arm32v6(例如 Raspberry Pi Zero 或 1)会失败。对于这些设备,您目前需要指定完整的镜像标签,例如

docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered nodered/node-red:1.2.0-10-arm32v6

自 Node-RED v3.1.0 起,我们还提供了基于 Debian 的镜像,适用于那些在 Alpine 上无法很好地工作的原生组件节点。

管理用户数据

一旦您使用 Docker 运行 Node-RED,我们需要确保即使容器被销毁,任何添加的节点或流都不会丢失。此用户数据可以通过将数据目录挂载到容器外部的卷来持久化。这可以通过绑定挂载或命名数据卷来完成。

Node-RED 使用容器内的 /data 目录来存储用户配置数据。

使用主机目录进行持久化(绑定挂载)

要将容器内的 Node-RED 用户目录保存到容器外的主机目录,您可以使用以下命令。为了允许访问此主机目录,容器内的 Node-RED 用户(默认 uid=1000)必须与主机目录的所有者具有相同的 uid。

docker run -it -p 1880:1880 -v /home/pi/.node-red:/data --name mynodered nodered/node-red

在此示例中,主机 /home/pi/.node-red 目录绑定到容器 /data 目录。

注意:从版本 0.20 迁移到 1.0 的用户需要确保任何现有的 /data 目录具有正确的权限。自 1.0 版起,这需要是 1000:1000。这可以通过命令 sudo chown -R 1000:1000 path/to/your/node-red/data 强制执行

有关权限的详细信息,请参阅wiki

使用命名数据卷

Docker 还支持使用命名数据卷来存储容器外部的持久或共享数据。

创建一个新的命名数据卷以持久化我们的用户数据并使用此卷运行新容器。

$ docker volume create --name node_red_data
$ docker volume ls
DRIVER              VOLUME NAME
local               node_red_data
$ docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered nodered/node-red

如果需要从挂载的卷备份数据,可以在容器运行时访问它。

$ docker cp  mynodered:/data  /your/backup/directory

使用 Node-RED 创建和部署一些示例流后,我们现在可以销毁容器并启动一个新实例而不会丢失用户数据。

$ docker stop mynodered
$ docker rm mynodered
$ docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered nodered/node-red

更新

由于 /data 现在已保留在容器外部,因此更新基本容器镜像现在变得非常简单

$ docker pull nodered/node-red
$ docker stop mynodered
$ docker rm mynodered
$ docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered nodered/node-red

Docker Stack / Docker Compose

下面是一个 Docker Compose 文件的示例,可以通过 docker stackdocker-compose 运行。有关 Docker stackDocker compose 的更多信息,请参阅官方 Docker 页面。

################################################################################
# Node-RED Stack or Compose
################################################################################
# docker stack deploy node-red --compose-file docker-compose-node-red.yml
# docker-compose -f docker-compose-node-red.yml -p myNoderedProject up
################################################################################
version: "3.7"

services:
  node-red:
    image: nodered/node-red:latest
    environment:
      - TZ=Europe/Amsterdam
    ports:
      - "1880:1880"
    networks:
      - node-red-net
    volumes:
      - node-red-data:/data

volumes:
  node-red-data:

networks:
  node-red-net:

以上 compose 文件

  • 创建一个 Node-RED 服务
  • 拉取最新的 Node-RED 镜像
  • 将时区设置为 Europe/Amsterdam
  • 将容器端口 1880 映射到主机端口 1880
  • 创建一个 node-red-net 网络并将容器附加到此网络
  • 将容器内的 /data 目录持久化到 Docker 中的 node-red-data

复制本地资源的 Dockerfile

有时,使用本地目录中的文件填充 Node-RED Docker 镜像会很有用(例如,如果您希望整个项目都保留在 git 存储库中)。为此,您希望您的本地目录看起来像这样

Dockerfile
README.md
package.json     # add any extra nodes your flow needs into your own package.json.
flows.json       # the normal place Node-RED store your flows
flows_cred.json  # credentials your flows may need
settings.js      # your settings file

注意:如果您希望将 /data 卷挂载到外部,则此方法不适用。如果您需要使用外部卷进行持久化,请改为将您的设置和流文件复制到该卷。

以下 Dockerfile 基于基本的 Node-RED Docker 镜像构建,但另外将您自己的文件移动到该镜像中

FROM nodered/node-red

# Copy package.json to the WORKDIR so npm builds all
# of your added nodes modules for Node-RED
WORKDIR /data
COPY package.json /data
RUN npm install --unsafe-perm --no-update-notifier --no-fund --only=production
WORKDIR /usr/src/node-red

# Copy _your_ Node-RED project files into place
# NOTE: This will only work if you DO NOT later mount /data as an external volume.
#       If you need to use an external volume for persistence then
#       copy your settings and flows files to that volume instead.
COPY settings.js /data/settings.js
COPY flows_cred.json /data/flows_cred.json
COPY flows.json /data/flows.json

注意package.json 文件必须在脚本部分包含一个启动选项。例如,默认容器如下所示

    "scripts": {
        "start": "node $NODE_OPTIONS node_modules/node-red/red.js $FLOWS",
        ...

Dockerfile 顺序和构建速度

虽然不是必需的,但最好尽早执行 COPY package... npm install... 步骤,因为尽管您在 Node-RED 中工作时 flows.json 会频繁更改,但您的 package.json 只会在您更改项目中的模块时更改。而且,由于当 package.json 更改时需要执行的 npm install 步骤有时会很耗时,因此最好在 Dockerfile 中尽早执行耗时且通常不变的步骤,以便可以重用这些构建镜像,从而使后续的整体构建更快。

凭据、秘密和环境变量

当然,您永远不想在任何地方硬编码凭据,因此如果您需要在 Node-RED 项目中使用凭据,上面的 Dockerfile 将允许您在 settings.js 中包含此内容…

module.exports = {
  credentialSecret: process.env.NODE_RED_CREDENTIAL_SECRET // add exactly this
}

…然后当您在 Docker 中运行时,您会向 run 命令添加一个环境变量…

docker run -e "NODE_RED_CREDENTIAL_SECRET=your_secret_goes_here"

构建和运行

您通常构建此 Dockerfile

docker build -t your-image-name:your-tag .

要在本地运行以进行开发,其中更改会立即写入,并且只写入您正在使用的本地目录,请 cd 到项目目录,然后运行

docker run --rm -e "NODE_RED_CREDENTIAL_SECRET=your_secret_goes_here" -p 1880:1880 -v `pwd`:/data --name a-container-name your-image-name

启动

环境变量可以传递到容器中以配置 Node-RED 的运行时。

流程配置文件使用环境变量 (FLOWS) 设置,默认为“flows.json”。这可以在运行时使用以下命令行标志进行更改。

docker run -it -p 1880:1880 -v node_red_data:/data -e FLOWS=my_flows.json nodered/node-red

注意:如果设置 -e FLOWS="",则流文件可以通过 settings.js 文件中的 flowFile 属性设置。

其他有用的环境变量包括

  • -e NODE_RED_ENABLE_SAFE_MODE=false # 设置为 true 会在安全(未运行)模式下启动 Node-RED
  • -e NODE_RED_ENABLE_PROJECTS=false # 设置为 true 会在启用项目功能的情况下启动 Node-RED

Node.js 运行时参数可以使用环境变量 (NODE_OPTIONS) 传递到容器中。例如,要固定 Node.js 垃圾回收器使用的堆大小,您将使用以下命令。

docker run -it -p 1880:1880 -v node_red_data:/data -e NODE_OPTIONS="--max_old_space_size=128" nodered/node-red

无头运行

要无头运行(即在后台),只需将之前大多数命令中的 -it 替换为 -d,例如

docker run -d -p 1880:1880 -v node_red_data:/data --name mynodered nodered/node-red

容器 Shell

一旦它无头运行,您可以使用以下命令重新获得对容器的访问权限。

$ docker exec -it mynodered /bin/bash
bash-4.4$

将在容器内提供命令行 - 您可以在其中运行所需的 npm install 命令 - 例如

bash-4.4$ npm install node-red-dashboard
bash-4.4$ exit
$ docker stop mynodered
$ docker start mynodered

刷新浏览器页面现在应该会在调色板中显示新添加的节点。

多个实例

运行

docker run -d -p 1880 nodered/node-red

将创建本地运行的机器实例。注意:我们没有指定名称。

此容器将有一个 ID 号,并将在随机端口上运行……要找出哪个端口,请运行 docker ps

$ docker ps
CONTAINER ID  IMAGE             COMMAND                 CREATED         STATUS        PORTS                    NAMES
860258cab092  nodered/node-red  "npm start -- --user…"  10 seconds ago  Up 9 seconds  0.0.0.0:32768->1880/tcp  dazzling_euler

您现在可以将浏览器指向报告的 TCP 端口上的主机,因此在上面的示例中,浏览到 http://{host ip}:32768

链接容器

您可以使用 Docker 用户定义桥接在 Docker 运行时中“内部”链接容器。

在使用桥接之前,需要创建它。以下命令将创建一个名为 iot 的新桥接

docker network create iot

然后所有需要通信的容器都需要使用 –network 命令行选项添加到同一个桥接中

docker run -itd --network iot --name mybroker eclipse-mosquitto mosquitto -c /mosquitto-no-auth.conf

(除非您想将端口 1883 全局公开……因为我们下面会施展魔法)

然后运行 nodered docker,也添加到同一个桥接中

docker run -itd -p 1880:1880 --network iot --name mynodered nodered/node-red

在同一用户定义桥接上的容器可以利用桥接提供的内置名称解析,并使用容器名称(使用 –name 选项指定)作为目标主机名。

在上面的示例中,Node-RED 应用程序可以使用主机名mybroker访问代理。

然后像下面这样的简单流显示 mqtt 节点连接到代理

    [{"id":"c51cbf73.d90738","type":"mqtt in","z":"3fa278ec.8cbaf","name":"","topic":"test","broker":"5673f1d5.dd5f1","x":290,"y":240,"wires":[["7781c73.639b8b8"]]},{"id":"7008d6ef.b6ee38","type":"mqtt out","z":"3fa278ec.8cbaf","name":"","topic":"test","qos":"","retain":"","broker":"5673f1d5.dd5f1","x":517,"y":131,"wires":[]},{"id":"ef5b970c.7c864","type":"inject","z":"3fa278ec.8cbaf","name":"","repeat":"","crontab":"","once":false,"topic":"","payload":"","payloadType":"date","x":290,"y":153,"wires":[["7008d6ef.b6ee38"]]},{"id":"7781c73.639b8b8","type":"debug","z":"3fa278ec.8cbaf","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":505,"y":257,"wires":[]},{"id":"5673f1d5.dd5f1","type":"mqtt-broker","z":"","name":"","broker":"mybroker","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"15","cleansession":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","closeTopic":"","closeRetain":"false","closePayload":"","willTopic":"","willQos":"0","willRetain":"false","willPayload":""}]

这样,内部代理就不会暴露在 docker 主机之外 - 当然,如果您希望计算机外部的其他系统能够使用代理,您可以将 -p 1883:1883 等添加到代理运行命令中。

Raspberry PI - 原生 GPIO 支持

| v1.0 - 破坏性更改:已删除 Raspberry PI 的原生 GPIO 支持 | | — | 原生 GPIO 的替代是 node-red-node-pi-gpiod

原生 GPIO 支持的缺点是

  • 您的 Docker 容器需要部署在您想要控制 gpio 的同一 Docker 节点/主机上。
  • 访问 Docker 节点/主机的 /dev/mem
  • docker stack 命令不支持 privileged=true

node-red-node-pi-gpiod 修复了所有这些缺点。使用 node-red-node-pi-gpiod,可以从单个 Node-RED 容器与多个 Raspberry Pi 的 gpio 交互,并且多个容器可以访问同一 Pi 上的不同 gpio。

快速迁移到 node-red-node-pi-gpiod 的步骤

  1. 通过 Node-RED 调色板安装 node-red-node-pi-gpiod
  2. 在主机 Pi 上安装并运行 PiGPIOd daemon。有关详细的安装说明,请参阅 node-red-node-pi-gpiod README
  3. 将所有原生 gpio 节点替换为 pi gpiod 节点。
  4. 配置 pi gpiod 节点以连接到 PiGPIOd daemon。通常,主机将具有 IP 172.17.0.1 端口 8888 - 但并非总是如此。您可以使用 docker exec -it mynodered ip route show default | awk '/default/ {print $3}' 进行检查。

注意:有一个贡献的 gpiod 项目,如果需要,它可以在自己的容器中运行 gpiod 而不是在主机上。

串口 - 拨号 - 添加组

要访问主机串口,您可能需要将容器添加到 dialout 组。这可以通过在启动命令中添加 --group-add dialout 来启用。例如

docker run -it -p 1880:1880 -v node_red_data:/data --group-add dialout --name mynodered nodered/node-red

常见问题和提示

以下是用户报告的常见问题列表及其可能的解决方案。

用户权限错误

有关权限的详细信息,请参阅wiki

如果您看到打开文件或访问主机设备时出现权限被拒绝错误,请尝试以 root 用户身份运行容器。

docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered -u node-red:dialout nodered/node-red

参考资料

https://github.com/node-red/node-red-docker/issues/15

https://github.com/node-red/node-red-docker/issues/8

访问主机设备

如果您想在容器内访问主机上的设备,例如串口,请使用以下命令行标志进行访问。

docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered --device=/dev/ttyACM0 nodered/node-red

参考:https://github.com/node-red/node-red/issues/15

设置时区

如果要修改默认时区,请使用 TZ 环境变量和相关时区

docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered -e TZ=America/New_York nodered/node-red

或在 docker-compose 文件中

  node-red:
    environment:
      - TZ=America/New_York

参考:https://groups.google.com/forum/#!topic/node-red/ieo5IVFAo2o