在Docker中运行Node-RED

在Docker中运行Node-RED

本指南假定你对Docker和 Docker命令行 已有基本了解。它介绍在Docker中运行Node-RED的多种方式,并支持多种架构: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://{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。

例如,你运行的是采用 arm32v7 架构的Raspberry Pi 3B,只需运行下面命令拉取镜像(标签为 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 的问题,例如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 stack 或 docker-compose 运行。更多信息见Docker官方的 Docker stack 和 Docker compose 文档。

################################################################################
# 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 --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选项。例如默认容器如下:

    "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.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端口;下面会使用内部通信。

接着启动Node-RED 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容器可以与多个Raspberry Pi的GPIO交互,也可以让多个容器访问同一Pi上的不同GPIO。

迁移到 node-red-node-pi-gpiod 的简要步骤

  1. 通过Node-RED调色板安装 node-red-node-pi-gpiod。
  2. 在宿主Pi上安装并运行 PiGPIOd daemon。详细安装方法见该模块的 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与添加组

访问宿主机串口时,可能需要将容器加入 dialout 组。在启动命令中添加 --group-add dialout 即可启用,例如:

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

常见问题与提示

以下列出用户报告的常见问题与可能的解决方法。

用户权限错误

权限详情见 Wiki。

如果打开文件或访问宿主机设备时出现 permission denied 错误,原文建议尝试以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


原文:Running under Docker,Node-RED项目。中文译文。原文与示例由Node-RED项目维护;来源仓库采用 Apache License 2.0。文中的版本号、日志和命令示例保留原文范围。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容