Node.js on Torizon
Introduction
This article explains how to add Node.js to a Torizon Container and run an example application. This article also explains how to use Dockcer Compose to add the image to a device with an over-the-air update. For a more in-depth guide to application development with Torizon, please see the Torizon Documentation.
This article complies to the Typographic Conventions for Torizon Documentation.
For information about Node.js and Node RED on Toradex BSP, please see the Node.js and Node-RED on Linux article. For instructions on Node RED on Torizon, plese see the Node-RED article.
Sample
This article is build around a sample, available on the Torizon Samples Github. The pre-built image available on DockerHub.
Containers
Torizon OS applications run in containers, as containerized applications have advantages such as:
- A container ships all the required files/libraries to run the program in a pre-built image, simplifying the deploy process, and reducing the chances of something breaking.
- Updating a container does not leave residual files that can interfere with the program, or leave the program in a broken state by missing some file while downloading an update.
If you want to learn more about why we use containers in Torizon OS, see Torizon OS Technical Overview.
Dockerfile
The Dockerfile controls the Image build process. The Dockerfile defines the the base image (debian, ubuntu, etc.), the commands to run while creating the image, which files to copy to the image, and the commands to run when starting the image.
Prerequisites
- A System on Module running Torizon OS.
- A configured environment for building Torizon Containers.
- Torizon Cloud account for OTA updates and deployment.
- DockerHub account.
How To
Create a Dockerfile
Create a text file named Dockerfile in an empty directory (<project-folder>
). Be careful to not use any extension, the file name should be Dockerfile
, not Dockerfile.txt
.
Import the base Image
Open the Dockerfile and add the command below to import the base image, which contains the operating system and some libraries. In this example we will use the Torizon Debian image:
The flag --platform=…
tells docker that the image should be built for the board (arm), and not the development computer (x86).
Torizon Image
The Torizon Debian Image is a good starting point, having pre-configured hardware access, not running as root by default, and being tested on Torizon OS. For more information about the Torizon Debian Image, see Debian Containers for Torizon.
You may also use the Node Official Image, which is more frequently updated but lacks some of the Torizon Debian Image features.
Set the working directory
Lets set the working directory where the commands such as ADD
, COPY
, RUN
, CMD
, and ENTRYPOINT
are executed. This way the files we copy ant the commands we run will output to our folder and not create a mess.
Add this command to your Dockerfile to set the working directory to /home/torizon
:
WORKDIR /home/torizon
Install Node and NPM
We also need to install node and npm, as they don't come pre installed in the base image.
RUN apt-get update \
&& apt-get install -y --no-install-recommends nodejs npm \
&& apt-get clean \
&& apt-get autoremove \
&& rm -rf /var/lib/apt/lists/*
The --no-install-recommends
prevents the installation of unnecessary packages, keeping the image smaller and with less packages to break. The last command, rm -rf /var/lib/apt/lists/*
, clears the apt cache, reducing the image size.
Configure NPM
Lets configure npm, installed in the last section.
Lets set the NPM Log Level by setting the NPM_CONFIG_LOGLEVEL
environment variable:
ENV NPM_CONFIG_LOGLEVEL info
An ENV
variable is saved to the image, providing a way to control the behaviour of applications on the deployed image.
To set a variable for the container build step only, use ARG
instead.
Install NPM Packages
For this basic web server example we are going to install express.
Add to your Dockerfile:
RUN npm install express
For larger projects, a package.json
is easier to mantain compared to adding the dependencies directly to the Dockerfile.
Create a package.json
file in the Dockerfile folder, then add to package.json
:
Add the COPY
command to the Dockerfile, to copy the package.json
to the image:
COPY package.json .
And the npm install
command to run npm using the configuration from package.json
:
RUN npm install
Expose ports
Lets declare our intent of using the port 3000 in our application:
EXPOSE 3000
The EXPOSE command makes the port accessible to other containers, but not to the host. It is good pratctice to include the EXPOSE
, as it helps others understand what port the application listens by just reading the Dockerfile. For more information see the EXPOSE
command documentation.
Copy files
We still need the app files in our image.
Lets copy the APP files from the computer to the image using the COPY
command:
COPY app.js .
In this example we are going to move a single file, app.js
, to the working directory .
.
Set the application entrypoint
Lets configure the image to run node when starting.
ENTRYPOINT ["node"]
CMD ["app.js"]
Setting the entrypoint and cmd is equivalent to running the following command on a terminal inside the container:
## node app.js
Complete Dockerfile
The resulting Dockerfile from the commands above, with hard-coded variables replaced:
Create the JS appplcation
Create a file named "app.js" on the same folder of your Dockerfile and paste the following code:
The program starts a "hello-world" web server, listening on port 3000. The web page shows the device temperature (does not auto refresh).
Build the image
To build the image, on terminal with arm emulation enabled, cd
to the Dockerfile folder and use the build
command:
$ docker buildx build -t nodeapp .
The -t
flag specifies a tag (name) for the image, in this case nodeapp
.
If the docker daemon is not running, running a docker command will result in the following error:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
To start the docker daemon, use sudo systemctl start docker
and to enable the docker daemon to start when the computer boots, use sudo systemctl enable docker
.
To check if the docker daemon is running, use sudo systemctl status docker
.
If the docker daemon is running but the user is not on docker group, running a docker command will result in the following error:
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock:
Please follow the instructions to run docker as a non-root user.
Run the program
Upload Image
Upload the container image to your docker hub account, to be later downloaded on the target device when updating:
$ docker login
$ docker tag nodeapp <dockerhub-account-name>/nodeapp:0.1.0
$ docker push <dockerhub-account-name>/nodeapp:0.1.0
The :0.1.0
specifies the image version, 0.1.0
in this case. It is recommended to increase the version number at every update, as images are only refreshed when the version number changes, or by explicitly requesting using command such as docker pull
.
Configure Docker Compose
Lets use docker compose to configure the deployment of the container.
Create a file named docker-compose.yml
and copy the following code to the file, replacing <dockerhub-account-name>
with your DockerHub account name:
services:
helloworld:
image: <dockerhub-account-name>/nodeapp
# A pre built image is also available:
# image: torizonextras/sample-nodejs
ports:
- "3000:3000"
image
specifies where to get the image, if the image is not found locally.
ports
specifies which host ports to attach to the container ports.
Update your device
Create a package on the Torizon Cloud Web UI using the Docker Compose file, then update the target device with the new package.
Test the App
After your device has been updated, open the web browser on your computer, enter the target device ip followed by the port 3000
:
<target-device-ip>:3000
Use the target device's hostname with a .local
suffix (<hostname>.local
) to access the device, insted of the device's IP.
For more information, see Using the Hostname.