在Docker中运行

本指南假设您对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实例。

    欢迎使用Node-RED
    ===================

    10 Oct 12:57:10 - [info] Node-RED版本:v1.2.0
    10 Oct 12:57:10 - [info] Node.js版本:v10.22.1
    10 Oct 12:57:10 - [info] Linux 4.19.76-linuxkit x64 LE
    10 Oct 12:57:11 - [info] 正在加载调色板节点
    10 Oct 12:57:16 - [info] 设置文件:/data/settings.js
    10 Oct 12:57:16 - [info] 上下文存储:'default' [module=memory]
    10 Oct 12:57:16 - [info] 用户目录:/data
    10 Oct 12:57:16 - [warn] 项目已禁用:editorTheme.projects.enabled=false
    10 Oct 12:57:16 - [info] 流程文件:/data/flows.json
    10 Oct 12:57:16 - [info] 正在创建新流程文件
    10 Oct 12:57:17 - [warn]

    ---------------------------------------------------------------------
    您的流程凭证文件使用系统生成的密钥进行了加密。

    如果由于任何原因丢失系统生成的密钥,您的凭证文件将无法恢复,您将不得不删除它并重新输入凭证。

    您应使用设置文件中的'credentialSecret'选项设置自己的密钥。然后,Node-RED将在下次您部署更改时使用您选择的密钥重新加密您的凭证文件。
    ---------------------------------------------------------------------

    10 Oct 12:57:17 - [info] 正在启动流程
    10 Oct 12:57:17 - [info] 流程已启动
    10 Oct 12:57:17 - [info] 服务器现在运行在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 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强制完成。

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

使用命名数据卷

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 stackdocker-compose运行的Docker Compose文件示例。 有关更多信息,请参考官方Docker页面中的Docker stackDocker compose

################################################################################
# Node-RED Stack或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镜像
  • 设置时区为欧洲/阿姆斯特丹
  • 将容器端口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

# 将package.json复制到WORKDIR,以便npm构建所有
# 您添加的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

# 将_您_ 的Node-RED项目文件放置到位
# 注意:这仅在您不将/data作为外部卷挂载时有效。
# 如果您需要使用外部卷进行持久化,则应将设置和流程文件复制到该卷中。
COPY settings.js /data/settings.js
COPY flows_cred.json /data/flows_cred.json
COPY flows.json /data/flows.json

注意package.json文件必须在脚本部分包含一个start选项。例如,默认容器是这样的:

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

Dockerfile顺序和构建速度

虽然不是必须的,但最好在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中运行时,您在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端口,除非您希望这样……因为我们在下面进行魔法处理)

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

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

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

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

然后像下面这样的简单流程显示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守护进程。有关详细的安装说明,请参阅node-red-node-pi-gpiod README
  3. pi gpiod节点替换所有原生gpio节点。
  4. 配置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

常见问题和提示

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

用户权限错误

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

如果您在打开文件或访问主机设备时看到权限被拒绝的错误,请尝试以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