Skip to content

Commit 7f51cd1

Browse files
authored
Merge pull request #772 from Paraphraser/20240713-nodered-doc-master
2024-07-13 Node-RED documentation - master branch
2 parents c84db62 + 1927fa1 commit 7f51cd1

File tree

1 file changed

+190
-32
lines changed

1 file changed

+190
-32
lines changed

Diff for: docs/Containers/Node-RED.md

100755100644
+190-32
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ Periodically, the source code is recompiled and pushed to [nodered/node-red](htt
4545

4646
### IOTstack menu { #iotstackMenu }
4747

48-
When you select Node-RED in the IOTstack menu, the *template service definition* is copied into the *Compose* file.
48+
When you select Node-RED in the IOTstack menu, the *template service definition* is copied into the *Compose* file.
4949

50-
> Under old menu, it is also copied to the *working service definition* and then not really used.
50+
> Under old menu, it is also copied to the *working service definition* and then not really used.
5151
5252
You choose add-on nodes from a supplementary menu. We recommend accepting the default nodes, and adding others that you think you are likely to need. Node-RED will not build if you do not select at least one add-on node.
5353

@@ -56,7 +56,7 @@ Key points:
5656
* Under new menu, you must press the right arrow to access the supplementary menu. Under old menu, the list of add-on nodes is displayed automatically.
5757
* Do not be concerned if you can't find an add-on node you need in the list. You can also add nodes via Manage Palette once Node-RED is running. See [component management](#componentManagement).
5858

59-
Choosing add-on nodes in the menu causes the *Dockerfile* to be created.
59+
Choosing add-on nodes in the menu causes the *Dockerfile* to be created.
6060

6161
### IOTstack first run { #iotstackFirstRun }
6262

@@ -67,7 +67,7 @@ $ cd ~/IOTstack
6767
$ docker-compose up -d
6868
```
6969

70-
`docker-compose` reads the *Compose* file. When it arrives at the `nodered` service definition, it finds:
70+
`docker-compose` reads the *Compose* file. When it arrives at the `nodered` service definition, it finds <a name="serviceBuildFragment"><a>:
7171

7272
``` yaml linenums="1"
7373
nodered:
@@ -87,11 +87,11 @@ Note:
8787
build: ./services/nodered/.
8888
```
8989

90-
The older syntax meant all local customisations (version-pinning and adding extra packages) needed manual edits to the *Dockerfile*. Those edits would be overwritten each time the menu was re-run to alter the selected add-on nodes. The newer multi-line syntax avoids that problem.
90+
The older syntax meant all local customisations (version-pinning and adding extra packages) needed manual edits to the *Dockerfile*. Those edits would be overwritten each time the menu was re-run to alter the selected add-on nodes. The newer multi-line syntax avoids that problem.
9191

9292
See also [updating to July 2022 syntax](#july2022syntax).
9393

94-
In either case, the path `./services/nodered/.` tells `docker-compose` to look for:
94+
In either case, the path `./services/nodered/.` tells `docker-compose` to look for:
9595

9696
```
9797
~/IOTstack/services/nodered/Dockerfile
@@ -106,25 +106,25 @@ Notes:
106106

107107
> Acknowledgement: Successful installation of the SQLite node is thanks to @fragolinux.
108108

109-
When you run the `docker images` command after Node-RED has been built, you *may* see two rows for Node-RED:
109+
When you run the `docker images` command after Node-RED has been built, you will see something like this:
110110

111111
``` console
112112
$ docker images
113-
REPOSITORY TAG IMAGE ID CREATED SIZE
114-
iotstack_nodered latest b0b21a97b8bb 4 days ago 462MB
115-
nodered/node-red latest deb99584fa75 5 days ago 385MB
113+
REPOSITORY TAG IMAGE ID CREATED SIZE
114+
iotstack-nodered latest 9feeb87019cd 11 days ago 945MB
116115
```
117116

118-
* `nodered/node-red` is the *base image*; and
119-
* `iotstack_nodered` is the *local image*. The *local* image is the one that is instantiated to become the running container.
117+
The image name `iotstack-nodered` is the concatenation of two components:
120118

121-
You *may* see the same pattern in Portainer, which reports the *base image* as "unused":
119+
1. The `docker-compose` *project* name. This is the all-lower-case representation of the name of the folder containing `docker-compose.yml`. In a default clone of IOTstack, the folder name is `IOTstack` so the project name is `iotstack`.
120+
2. The name of the service definition which, for Node-RED is `nodered`.
122121

123-
![nodered-portainer-unused-image](./images/nodered-portainer-unused-image.png)
122+
When you install Node-RED for the first time, the entire process of downloading a *base* image from Dockerhub, building a *local* image by running your local Dockerfile ❷, and then instantiating that *local* image as your running container, is all completely automatic.
124123

125-
You should not remove the *base* image, even though it appears to be unused.
124+
However, *after* that first build, your *local* image is essentially frozen and it needs special action on your part to keep it up-to-date. See [maintaining Node-RED](#maintainNodeRed) and, in particular:
126125

127-
> Whether you see one or two rows depends on the version of Docker you are using and how your version of `docker-compose` builds local images.
126+
* [Re-building the local image](#rebuildNodeRed) if you change the Dockerfile; and
127+
* [Upgrading Node-RED](#upgradeNodeRed) if you want to reconstruct your *local* image based on an updated *base* image which has become available on [DockerHub](https://hub.docker.com/r/nodered/node-red/tags).
128128

129129
## Securing Node-RED { #securingNodeRed }
130130

@@ -267,9 +267,9 @@ The third method is *portable*, meaning a flow can conceptually refer to "this"
267267
$ docker network inspect bridge | jq .[0].IPAM.Config[0].Gateway
268268
"172.17.0.1"
269269
```
270-
270+
271271
> If `jq` is not installed on your system, you can install it by running `sudo apt install -y jq`.
272-
272+
273273
If you use this method, your flows can refer to "this" host using the IP address "172.17.0.1".
274274

275275
* Method 2
@@ -282,7 +282,7 @@ The third method is *portable*, meaning a flow can conceptually refer to "this"
282282
```
283283

284284
If you use this method, your flows can refer to "this" host using the domain name "host.docker.internal".
285-
285+
286286
Generally the second method is recommended for IOTstack. That is because your flows will continue to work even if the 172.17.0.1 IP address changes. However, it does come with the disadvantage that, if you publish a flow containing this domain name, the flow will not work unless the recipient also adds the `extra_hosts` clause.
287287

288288
## GPIO Access { #accessGPIO }
@@ -428,12 +428,12 @@ To communicate with your Raspberry Pi's GPIO you need to do the following:
428428
4. Drag a `pi gpio` node onto the canvas. Configure it according to your needs.
429429

430430
The `Host` field should be set to one of:
431-
431+
432432
* `172.17.0.1`; or
433433
* `host.docker.internal`
434434

435435
See also [Bridge network - default gateway](#defaultBridge).
436-
436+
437437
Don't try to use 127.0.0.1 because that is the loopback address of the Node-RED container.
438438

439439
## Serial Devices { #accessSerial }
@@ -501,7 +501,7 @@ You have three basic options:
501501
```
502502

503503
In the above:
504-
504+
505505
* "188" is the major number for ttyUSB0 and you should substitute accordingly if your device has a different major number.
506506

507507
* the "*" is a wildcard for the minor number.
@@ -546,7 +546,7 @@ Historically, `/dev/ttyAMA0` referred to the Raspberry Pi's serial port. The sit
546546
```
547547

548548
And, if that isn't sufficiently confusing, the location of `config.txt` depends on the OS version:
549-
549+
550550
* Bullseye (and earlier): `/boot/config.txt`
551551
* Bookworm: `/boot/firmware/config.txt`
552552

@@ -560,7 +560,7 @@ Rolling all that together, if you want access to the hardware serial port from N
560560
devices:
561561
- /dev/serial0:/dev/«internalDevice»
562562
```
563-
563+
564564
where `«internalDevice»` is whatever device the add-on node you're using is expecting, such as `ttyAMA0`.
565565

566566
4. Recreate the Node-RED container by running:
@@ -569,7 +569,7 @@ Rolling all that together, if you want access to the hardware serial port from N
569569
$ cd ~/IOTstack
570570
$ docker-compose up -d nodered
571571
```
572-
572+
573573
### Bluetooth device { #bluetoothSupport }
574574

575575
If you enable the `node-red-contrib-generic-ble` add on node, you will also need to make the following changes:
@@ -585,19 +585,19 @@ If you enable the `node-red-contrib-generic-ble` add on node, you will also need
585585
```
586586
dtparam=krnbt=off
587587
```
588-
588+
589589
You then need to reboot. This adds the Bluetooth device to `/dev`.
590590

591591
2. Find the the Node-RED service definition in your `docker-compose.yml`:
592592

593593
* Add the following mapping to the `volumes:` clause:
594-
594+
595595
```yaml
596596
- /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket
597597
```
598-
598+
599599
* Add the following `devices:` clause:
600-
600+
601601
```yaml
602602
devices:
603603
- "/dev/serial1:/dev/serial1"
@@ -618,7 +618,7 @@ Notes:
618618
* Historically, `/dev/ttyAMA0` meant the serial interface. Subsequently, it came to mean the Bluetooth interface but only where Bluetooth hardware was present, otherwise it still meant the serial interface.
619619

620620
On Bookworm and later, if it is present, `/dev/ttyAMA1` means the Bluetooth Interface.
621-
621+
622622
On Bullseye and later, `/dev/serial1` is a symbolic link pointing to whichever of `/dev/ttyAMA0` or `/dev/ttyAMA1` means the Bluetooth interface. This means that `/dev/serial1` is the most reliable way of referring to the Bluetooth Interface. That's why it appears in the `devices:` clause above.
623623

624624
## Sharing files between Node-RED and the Raspberry Pi { #fileSharing }
@@ -1104,7 +1104,7 @@ To rebuild your *local* image:
11041104
``` console
11051105
$ cd ~/IOTstack
11061106
$ docker-compose up --build -d nodered
1107-
$ docker system prune
1107+
$ docker system prune -f
11081108
```
11091109

11101110
Think of these commands as "re-running the *Dockerfile*". The only time a *base* image will be downloaded from *DockerHub is when a *base* image with a tag matching the value of `DOCKERHUB_TAG` can't be found on your Raspberry Pi.
@@ -1139,7 +1139,7 @@ Once a new version appears on [*DockerHub*](https://hub.docker.com), you can upg
11391139
$ cd ~/IOTstack
11401140
$ docker-compose build --no-cache --pull nodered
11411141
$ docker-compose up -d nodered
1142-
$ docker system prune
1142+
$ docker system prune -f
11431143
```
11441144

11451145
Breaking it down into parts:
@@ -1553,3 +1553,161 @@ All remaining lines of your original *Dockerfile* should be left as-is.
15531553
### Applying the new syntax { #july2022build }
15541554

15551555
Run the [re-building the local Node-RED image](#rebuildNodeRed) commands.
1556+
1557+
## Alpine vs Debian { #linuxDistro }
1558+
1559+
The first part of IOTstack's default service definition for Node-RED is shown at [IOTstack first run](#serviceBuildFragment). Although it is not immediately obvious, this results in a container which is based on the Alpine Linux distribution. You can confirm this by running:
1560+
1561+
``` console
1562+
$ docker exec nodered grep "PRETTY_NAME" /etc/os-release
1563+
PRETTY_NAME="Alpine Linux v3.20"
1564+
```
1565+
1566+
Historically, Node-RED has been distributed on on [Dockerhub](https://hub.docker.com/r/nodered/node-red/tags) as two distinct sets of Node-RED images:
1567+
1568+
* Those based on the Alpine Linux distribution; and
1569+
* Those based on the Debian Linux distribution.
1570+
1571+
In general, Node-RED images have tracked Alpine releases more consistently than they have Debian. For example, at the time of writing (July 2024):
1572+
1573+
Image Tag | Distro | Image OS | Current
1574+
----------------|--------|---------------|--------
1575+
`latest` | Alpine | v3.20 | [v3.20](https://alpinelinux.org/releases/)
1576+
`latest-debian` | Debian | 11 (bullseye) | [12 (bookworm)](https://www.debian.org/releases/)
1577+
1578+
In addition, Node-RED images based on Alpine have offered a greater range of options when it comes to the embedded version of Node.js. At the time of writing:
1579+
1580+
* image variants based on Alpine Linux include `latest-18`, `latest-20` and `latest-22`, implying a choice of Node.js versions 18, 20 and 22, with version 20 being the default; while
1581+
* the single image variant for Debian Linux is `latest-debian` which comes with Node.js version 20.
1582+
1583+
Naturally, this situation could change at any time! This information is only here to make the point that, historically, Node-RED images based on Debian have lagged behind Alpine and have only supported a single version of Node.js. This is also the main reason why IOTstack defaults to Alpine images.
1584+
1585+
However, there may be circumstances where you decide it is appropriate to run a Node-RED image based on Debian. The purpose of this section is not to explore scenarios nor weigh the pros and cons, merely to explain how to adapt your Node-RED service definition to accomplish it. Proceed as follows:
1586+
1587+
1. Make a copy of your existing Dockerfile:
1588+
1589+
``` console
1590+
$ cd ~/IOTstack/services/nodered
1591+
$ cp Dockerfile Debian.Dockerfile
1592+
```
1593+
1594+
The reason for making a copy is to preserve your existing (Alpine-aware) Dockerfile so you can easily switch back if you break something.
1595+
1596+
2. Open `Debian.Dockerfile` in a text editor and make the following changes:
1597+
1598+
* Find the line:
1599+
1600+
``` Dockerfile linenums="4"
1601+
ARG DOCKERHUB_TAG=latest
1602+
```
1603+
1604+
**Replace** that line with:
1605+
1606+
``` Dockerfile linenums="4"
1607+
ARG DOCKERHUB_TAG=latest-debian
1608+
```
1609+
1610+
* Find the line:
1611+
1612+
``` Dockerfile linenums="15"
1613+
RUN apk update && apk add --no-cache eudev-dev ${EXTRA_PACKAGES}
1614+
```
1615+
1616+
**Replace** that line with:
1617+
1618+
``` Dockerfile linenums="15"
1619+
RUN apt update && apt install -y udev ${EXTRA_PACKAGES}
1620+
```
1621+
1622+
`apk` is the Alpine package manager whereas `apt` is the Debian package manager.
1623+
1624+
* Save your work.
1625+
1626+
3. Make a copy of your existing compose file:
1627+
1628+
``` console
1629+
$ cd ~/IOTstack
1630+
$ cp docker-compose.yml docker-compose.yml.bak
1631+
```
1632+
1633+
The reason for making a copy is to preserve your existing (Alpine-aware) service definition so you can easily switch back if you break something.
1634+
1635+
4. Open `docker-compose.yml ` in a text editor and make the following changes:
1636+
1637+
* Change the Node-RED `build` clause so that it looks like this:
1638+
1639+
``` yaml linenums="3"
1640+
build:
1641+
context: ./services/nodered/.
1642+
dockerfile: Debian.Dockerfile
1643+
args:
1644+
- DOCKERHUB_TAG=latest-debian
1645+
- EXTRA_PACKAGES=
1646+
```
1647+
1648+
There are two key edits:
1649+
1650+
1. **Insert** the `dockerfile` line (as line 5).
1651+
2. **Change** the right hand side of the `DOCKERHUB_TAG` argument from `latest` to `latest-debian` (line 7).
1652+
1653+
* If you have any `EXTRA_PACKAGES` specified, you will need to allow for any package-name differences between Alpine and Debian. For example, suppose you are using this list of extra packages with Alpine:
1654+
1655+
``` yaml linenums="8"
1656+
- EXTRA_PACKAGES=mosquitto-clients bind-tools tcpdump tree
1657+
```
1658+
1659+
The `mosquitto-clients`, `tcpdump` and `tree` packages have the same names in the `apk` (Alpine) package manager as they do in `apt` (Debian) whereas `bind-tools` is named `dnsutils` in the Debian repositories. Thus the extra packages list for a Debian build would need to be:
1660+
1661+
``` yaml linenums="8"
1662+
- EXTRA_PACKAGES=mosquitto-clients dnsutils tcpdump tree
1663+
```
1664+
1665+
* Save your work.
1666+
1667+
5. Rebuild Node-RED:
1668+
1669+
``` console
1670+
$ cd ~/IOTstack
1671+
$ docker-compose build --no-cache --pull nodered
1672+
```
1673+
1674+
If the build process reports any errors, go back and check your work.
1675+
1676+
6. Start the new container:
1677+
1678+
``` console
1679+
$ docker-compose up -d nodered
1680+
```
1681+
1682+
7. Check that the new container is running properly and hasn't gone into a restart loop:
1683+
1684+
``` console
1685+
$ docker ps -a --format "table {{.Names}}\t{{.RunningFor}}\t{{.Status}}\t{{.Size}}" --filter name=nodered
1686+
NAMES CREATED STATUS SIZE
1687+
nodered 32 seconds ago Up 31 seconds (healthy) 0B (virtual 945MB)
1688+
```
1689+
1690+
Providing the STATUS column reports "healthy" after roughly 30 seconds of runtime, it is usually safe to assume that the container is behaving normally.
1691+
1692+
8. Verify the base Linux distribution being used by the container:
1693+
1694+
``` console
1695+
$ docker exec nodered grep "PRETTY_NAME" /etc/os-release
1696+
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
1697+
```
1698+
1699+
9. Check your Node-RED and Node.js versions:
1700+
1701+
``` console
1702+
$ docker exec nodered npm version --json | jq -r '[.["node-red-docker"],.["node"]] | @tsv'
1703+
4.0.2 20.15.0
1704+
```
1705+
1706+
Interpretation - the container is running:
1707+
1708+
* Node-RED version 4.0.2, with
1709+
* Node.js version 20.15.0
1710+
1711+
The actual version numbers you see in the last two steps will depend (obviously) on whatever the good folks who maintain Node-RED thought was appropriate at the time they released whatever `latest-debian` variant is present on DockerHub at the moment when you perform the migration.
1712+
1713+
Please keep in mind that none of this affects the IOTstack menu. Re-running the menu is likely to revert your Node-RED service definition to be based on Alpine images.

0 commit comments

Comments
 (0)