Node-RED allows Raspberry Pis to act as gateways for specific home-automation features, playing an essential role in both the van and the cabin.
This is one of several posts on containerization of common, useful software.
If you’ve not used it before, Node-RED is a visual code editor — of the sorts often used to introduce computer programming to beginners, or otherwise simplify the creation of basic scripts.
Docker Considerations
The official getting started guide has lots of good information about running in Docker. It covers the basics, like persistence and security. However, there are many possible ways to use node RED.
The above screenshot shows the code for my DIY “Nest-equivalent” smart thermostat. It connects to an Arduino via a USB port and then feeds sensor data to Home Assistant. I frequently follow this pattern of using Node RED on a Raspberry Pi device to control peripherals.
Because everything runs in Kubernetes, Node-RED is communicating with Home Assistant within a secure virtual network. It’s even easy to monitor each of the different nodes from the Grafana Kubernetes panel.
Serial Ports (USB), GPIO & LIRC
As noted in the docs, the preferred way to access the gpio/serial in versions of Node-RED >= 1.0 is to use node-red-node-pi-gpiod
on the host machine and ensure that the Docker user is in the dialout
group.
Alternatively, it is still possible to achieve host access with privileged: true
security context in Kubernetes. And an alternative to the dialout
group is to make the necessary devices more permissive. The most heavy-handed approach is to do so for all devices by editing /etc/udev/rules.d/50-myusb.rules
to include:
KERNEL=="ttyUSB[0-9]*",MODE="0666"
KERNEL=="ttyACM[0-9]*",MODE="0666"
Running on Multiple Devices
If each of your nodes are meant to have exactly the same configuration, then you can scale up the deployment as usual. However, in many cases each node is meant to run different code. It may even have different hardware attached, as in the example above.
The first trick is to use the node’s name to automatically mount a sub-directory of the persistent volume, so each node’s config lives in its own folder:
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
volumeMounts:
- name: node-red-data
subPathExpr: $(NODE_NAME)
mountPath: /data
If each node needs to be addressed uniquely (almost certainly the case), then it becomes necessary to create a service. Using Switchboard, you can simply set the egress to the name of the service (i.e., node-red.svc.default.cluster.local
). When combined with Pi-Hole DNS and free OpenDNS servers, you’re then able to access the node via a secure URL (i.e., https://node-red.my-domain.com
) both inside and outside the local network.
My Deployment File(s)
Here is an example of deploying Node-RED as a daemon set with a service:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-red
labels:
app: node-red
spec:
selector:
matchLabels:
app: node-red
template:
metadata:
labels:
app: node-red
spec:
containers:
- name: node-red
image: nodered/node-red:latest
ports:
- containerPort: 1880
name: node-red-ui
securityContext:
privileged: true
volumeMounts:
- name: node-red-data
subPathExpr: $(NODE_NAME)
mountPath: /data
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: TZ
value: America/Los_Angeles
volumes:
- name: node-red-data
persistentVolumeClaim:
claimName: node-red-claim
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: node-red-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: rode-red
spec:
selector:
app: node-red
type: LoadBalancer
loadBalancerIP: 192.168.0.103
ports:
- name: node-red-ui
port: 1880
protocol: TCP
targetPort: node-red-ui
I use metallb to achieve the static IPs seen above in a baremetal cluster. However, it is not necessary to do so when addressing the service via the internal DNS to Kubernetes services.
Hi,
Really liking the site!
If you need any devops work done please get in touch, we’d
be happy to help you out.