Pre-Requisites
This post assumes that the reader has an understanding about Node-Red Flow
Programming tool and wishes to work with a custom Docker Images for it.
This post also assumes the readers are familiar with python programming.
Requirements
Since Node-Red provides a visual way of connecting many components through Flows,
we might be interested in using it for our own personal stack.
If you haven't tried it out, Node-Red Documentation for Docker provides some nice
ways to getting started.
Since the Flows are rendered to a web-browser, we might want to take into consideration
testing our custom Node-Red container when it might be integrated with other containers
within a stack deployment.
This guide will provide a concise way of joining the dots between creating your custom
Node-Red container and testing its reachability within a docker-compose
file using
the pytest-docker-compose
suite.
Follow Along
Please refer to the GitHub Repository for
the code structure and file content
Custom Node-Red Docker Container Creation
Diving into the deep-end straight, we want to create a Node-Red custom container with
just the following:
-
node-red
runtime-engine -
node-red
dashboard
NOTE: for this post sake, we stick to basic things, please feel free to add more things
according to you needs.
Adding Node-Red and its Dependencies
Standard practice with Node-Red lets the user add dedicated flows and dependencies via the
npm install
command-line. However within the Node-Red-Docker Repo Wiki, you can also
add these flows/dependencies via a dedicated package.json
.
Let's stick to this method and avoid adding things via CLI within the container.
Create a package.json
file and add the following:
{
"name": "node-red-slim-container",
"description": "A Slim Node-RED Docker Image running on Alpine Container",
"dependencies": {
"node-red": ">=2.2.0",
"node-red-dashboard": "*"
},
"scripts": {
"start": "node $NODE_OPTIONS node_modules/node-red/red.js $FLOWS --userDir=/data"
}
}
Moving on to creating a Dockerfile
Multi-Stage Dockerfile for a Slim Image
We will be using a Multi-Stage Docker build process to keep our image a bit light on the
size using alpine
based containers and the official minimal
image from Node-Red.
We will use TWO-Stages in our build process:
- prepare a base Image with
alpine
- use
nodered/node-red-minimal
image to install the flows/deps and prepare thenode_modules
We then use the base Image in step 1 as a production image and copy the node_modules
directory
to this production image.
Our Dockerfile
looks like the following:
FROM alpine:3.13 AS base
RUN apk add --no-cache \
nodejs \
npm && \
mkdir -p /usr/src/node-red /data && \
adduser -h /usr/src/node-red -D -H node-red -u 1000 && \
chown -R node-red:node-red /data
FROM nodered/node-red:2.2.2-minimal AS build
COPY package.json .
RUN npm install \
--unsafe-perm --no-update-notifier \
--no-audit --only=production
FROM base as prod
COPY --from=build --chown=node-red:node-red /data/ /data/
WORKDIR /usr/src/node-red
COPY settings.js /data/settings.js
COPY flows.json /data/flows.json
COPY --from=build --chown=node-red:node-red /usr/src/node-red/ /usr/src/node-red/
USER node-red
CMD ["npm", "start"]
We start with the base image as alpine:3.13
and prepare it by adding nodejs
and npm
needing to run Node-Red. For security best-practices, we add a node-red
user and group as
opposed to running everything as root
. This avoids any privilege escalation within the container,
as well as misuse within the container.
Our build
stage will install all the NPM packages in our package.json
and then we copy the
necessary node_modules
to our prod
image. In the end, we will run the command as npm start
You can build this image locally using:
docker build -t node-red-slim:latest .
Add a Healthcheck API in Node-Red
You can refer to this Discussion Thread on Node-Red Forum to create the flow, or use the following
flows.json
file
We can check if the flow is working again but building the Docker Image and then try:
curl -XGET http://localhost:1880/health
Don't forget to set the port 1880 when running the docker image e.g.
docker run -p 1880:1880 node-red-slim:latest
If that works, good job! now it is time to make an integration test with python
Integration Test of your Node-Red Image
One cool thing you can try out is the pytest-docker-compose which provides you a nice suite
to do tests with docker-compose
. I won't dive too deep into it, but I relied on this
Dev.To post by Iuliia Volkova for wrting a simple test that does a health check on our
/health
API once the container is up and running.
Testing Files
pytest-docker-compose
is a plugin for pytest
testing suite. The structure of your test directory
should be as follows:
.
├── README.md
├── requirements.dev.txt
├── scripts
│ ├── 00-build-test-env.sh
│ └── 01-run-tests.sh
├── test-docker-compose.yml
└── tests
├── conftest.py
└── test_fixtures.py
Refer to tests/integration directory for file contents
The conftest.py
is used to as fixture for the test-docker-compose.yml
to be used for the test stack.
the test_fixture.py
contains the test fixture to ping the /health
API from our flow once the
node-red container
For our case, if the /health
API provides a HTTP 200 OK response, our test passes. You can add more
tests depending on your requirements.
We will leverage simple bash scripts in the scripts
directory to setup the python
virtualenvironment as to bring the test-docker-compose
up and run the pytest
Additional: using this setup in a CI/CD Pipeline using GitHub
You can think of our steps we just went through as a CI/CD Pipeline. The steps are:
- Build our custom Node-Red Docker Image
- Test Integration using
pytest-docker-compose
- Push the image to Docker Hub, if test passes
For the repository, I have setup a GitHub Workflow that pushes the docker image only
when I push a Tag e.g. v1.2.0
etc.
For this Workflow, you will need to setup the credentials of your Registry in your GitHub Secrets.
For a sample of the YAML file, refer to this deploy.yml
Inference
That's how you do custom node-red, docker, docker-compose and integration testing !
I am not aware of conducting tests of the individual tests of the flows generated in Node-Red, but this
test focuses on integrating node-red with some possible stacks where different Databases are part of the
stack.
If you have comments, criticisms, feedback please reach out to me and I will be happy to learn and help!
Top comments (2)
I have entered the credentials into settings.js. But this is ignored.
Can you elaborate more?