本指南假设您对 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 - 运行此容器,如果需要会先在本地构建镜像
-it - 附加一个终端会话,以便我们可以看到运行情况
-p 1880:1880 - 将本地端口 1880 连接到容器内部暴露的端口 1880
-v node_red_data:/data - 将名为 node_red_data 的 Docker 卷挂载到容器的 /data 目录,以便对流所做的任何更改都能持久保存
--name mynodered - 为此容器指定一个友好的本地名称
nodered/node-red - 基于的镜像 - 目前是 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://{主机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。
例如:假设您在树莓派 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 run 命令和 docker compose 文件在系统之间更加灵活和可互换。
注意:目前 Docker 的架构检测存在一个错误,对 arm32v6 会失败 - 例如树莓派 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 stack 或 docker-compose 运行。有关 Docker stack 和 Docker compose 的更多信息,请参阅官方 Docker 页面。
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 # 将您的流需要的任何额外节点添加到您自己的 package.json 中。
flows.json # Node-RED 存储您的流的常规位置
flows_cred.json # 您的流可能需要的凭据
settings.js # 您的设置文件
注意:如果您想将 /data 卷挂载到外部,则此方法不适用。如果您需要使用外部卷进行持久化,请将您的设置和流文件复制到该卷中。
以下 Dockerfile 基于基础的 Node-RED Docker 镜像构建,但另外将您自己的文件移动到该镜像中的相应位置:
FROM nodered/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 settings.js /data/settings.js
COPY flows_cred.json /data/flows_cred.json
COPY flows.json /data/flows.json
注意:package.json 文件必须在 scripts 部分包含一个 start 选项。例如,默认容器是这样的:
"scripts": {
"start": "node $NODE_OPTIONS node_modules/node-red/red.js $FLOWS",
...
Dockerfile 顺序和构建速度
虽然不是必须的,但最好尽早执行 COPY package... npm install... 步骤,因为尽管 flows.json 在您使用 Node-RED 工作时会频繁更改,但您的 package.json 只有在您更改项目中包含哪些模块时才会更改。并且由于当 package.json 更改时需要进行的 npm install 步骤有时可能很耗时,因此最好在 Dockerfile 中尽早执行这些耗时且通常不变的步骤,以便可以重用这些构建镜像,从而使后续的整体构建速度更快。
凭据、密钥和环境变量
当然,您绝不想在任何地方硬编码凭据,因此如果您的 Node-RED 项目需要使用凭据,上面的 Dockerfile 将允许您在 settings.js 中使用这个…
module.exports = {
credentialSecret: process.env.NODE_RED_CREDENTIAL_SECRET
}
…然后在 Docker 中运行时,向您的运行命令添加一个环境变量…
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
-e NODE_RED_ENABLE_PROJECTS=false
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://{主机 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 等添加到代理运行命令中。
树莓派 - 原生 GPIO 支持
| v1.0 - 重大变更:树莓派的原生 GPIO 支持已被弃用 | | — | 原生 GPIO 的替代品是 node-red-node-pi-gpiod。
原生 GPIO 支持的缺点是:
- 您的 Docker 容器需要部署在您想要控制 gpio 的同一 Docker 节点/主机上。
- 获得对 Docker 节点/主机的
/dev/mem 的访问权限
privileged=true 不支持 docker stack 命令
node-red-node-pi-gpiod 修复了所有这些缺点。使用 node-red-node-pi-gpiod,可以从单个 Node-RED 容器与多个树莓派的 gpio 交互,并且多个容器可以访问同一 Pi 上的不同 gpio。
快速迁移到 node-red-node-pi-gpiod 的步骤
- 通过 Node-RED 选项板安装
node-red-node-pi-gpiod。
- 在主机 Pi 上安装并运行 PiGPIOd 守护进程。详细的安装说明请参考
node-red-node-pi-gpiod 的 README。
- 将所有原生 gpio 节点替换为 pi gpiod 节点。
- 配置 pi gpiod 节点以连接到 PiGPIOd 守护进程。通常主机机器的 IP 是 172.17.0.1 端口 8888 - 但并不总是如此。您可以使用
docker exec -it mynodered ip route show default | awk '/default/ {print $3}' 来检查。
注意:有一个贡献的 gpiod 项目,如果需要,可以在其自己的容器中运行 gpiod,而不是在主机上运行。
串行端口 - Dialout - 添加组
要访问主机串行端口,您可能需要将容器添加到 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
参考:
访问主机设备
如果您想从容器内部访问主机上的设备,例如串行端口,请使用以下命令行标志来传递访问权限。
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