Running Node Application in Docker Container on Raspberry Pi

Published on Wednesday, June 1, 2016

Dockerizing Node

Lets run a Node application in Docker on Raspberry Pi; for the proof of concept; I will be using a simple hello world app and a GIT/SSH setup we made in Staging Node Application on Raspberry Pi. The Docker way of running the application is that we have our “data” and “application” files outside of the container; so that container remains completely disposable. When running the container; we can mount the directory from the Host OS; using this feature we can have our data and application files on the Host OS and they are being used from the Container; something like this:

image

We can continue to have the GIT / NGINX arrangements that we did in Staging Node Application on Raspberry Pi; but now we can run Node and MongoDB (and others) from Containers. We already have made the Node Docker image in the Docker on Raspberry Pi; all we need is to run is so that we mount /home/pi/hello directory into the Node Container and run Node in the Container, doing so we will have the Node server at container’s 3000 port, expose this port to the Host OS’s 3000 port so that NGINX forwards the request to Host OS’s 3000 port when it receive any request at Host OS’ http://ip/node endpoint

pi@raspberrypi:~ $ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
khurram/pi           node                6af338545368        5 hours ago         159 MB
khurram/pi           nano                99f0053b387e        6 hours ago         105.6 MB
resin/rpi-raspbian   jessie              80a737f1a654        7 days ago          80.01 MB
pi@raspberrypi:~ $ cd hello
pi@raspberrypi:~/hello $ ls
hello.js
pi@raspberrypi:~/hello $ docker run -p 3000:3000 -v /home/pi/hello:/hello -it khurram/pi:node
root@381ece8ec01a:/# cd /hello
root@381ece8ec01a:/hello# ls
hello.js
root@381ece8ec01a:/hello# nodejs hello.js
Server running at port 3000

  • -p 3000:3000 is to expose container’s 3000 port and map it to Host OS’s 3000; the port of container where node application will run in the container and host os port where nginx is expected to forward the requests
  • -v localpath:remotepath is to mount the Host OS’s localpath directory as a remotepath in the container
  • -it is for interactive terminal
  • khurram/pi:node is the Node image we created

Once the container is running and we are on its terminal; we can start our node app; we have to leave the terminal running so Node server continue to run; and from another terminal we can issue docker ps to get the list of all the running container

pi@raspberrypi:~ $ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
381ece8ec01a        khurram/pi:node     "/bin/bash"         2 minutes ago       Up 2 minutes        0.0.0.0:3000->3000/tcp   goofy_khorana

  • Note; how the ports are mapped
  • Note the NAMES column; Docker has named our container “dynamically”

We can try http://ip:3000 and http://ip/node URL and our node application should be running there; using the Container Name or Container ID we can stop it. We can issue docker ps –a to list all the containers including those that are stopped

pi@raspberrypi:~ $ docker stop goofy_khorana
goofy_khorana
pi@raspberrypi:~ $ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
pi@raspberrypi:~ $ docker ps -a
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS                       PORTS               NAMES
381ece8ec01a        khurram/pi:node             "/bin/bash"              9 minutes ago       Exited (130) 9 seconds ago                       goofy_khorana
aece7089082d        khurram/pi:node             "-p 3000:3000 -v /hom"   10 minutes ago      Created                                          determined_colden
e5e7005489a2        khurram/pi:nano             "/bin/bash"              6 hours ago         Exited (0) 5 hours ago                           grave_pasteur
5b62a2d14818        resin/rpi-raspbian:jessie   "/bin/bash"              6 hours ago         Exited (0) 6 hours ago                           tiny_feynman

As you can see; our Containers are also getting stored on the Host OS; think of it as the Working Directory in the source control; the server will have code images that we commit and working directory has currently working copy of source code; similarly docker images are the images of container that we committed and containers are the running (or stopped) copies, they also eats up the disk and we should remove the unwanted one; keeping an eye on STATUS we can learn which one we are not using anymore and can remove them using docker rm

pi@raspberrypi:~ $ docker rm goofy_khorana
goofy_khorana
pi@raspberrypi:~ $ docker rm aece7089082d
aece7089082d

We dont always have to get the interactive shell on starting container; if we know what command to run when container is running; we can start our container in the background giving the command to run as parameter; lets create a new container for our Node application (as we have deleted the previously created one) from the Docker Image; something like this

pi@raspberrypi:~ $ docker run -d -p 3000:3000 -v /home/pi/hello:/hello khurram/pi:node nodejs /hello/hello.js
49b347531127cc1d6c07f9b266e9e146afa0c4214c3c13514b7a851a444c525e
pi@raspberrypi:~ $ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
49b347531127        khurram/pi:node     "nodejs /hello/hello."   9 seconds ago       Up 3 seconds        0.0.0.0:3000->3000/tcp   stupefied_wing
pi@raspberrypi:~ $ curl
http://localhost/node
Hello World from NODE in Container

Restarting Container

Now if we reboot the Raspberry; and give docker ps –a when it comes back; you will notice that our Container is not running anymore

pi@raspberrypi:~ $ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                            PORTS               NAMES
49b347531127        khurram/pi:node     "nodejs /hello/hello."   2 minutes ago       Exited (143) About a minute ago                       stupefied_wing

This can be taken care using –restart=always as the paramter to docker run; with this; even if our container exits unexpectedly; Docker will restart it; and also start it when machine boots (Docker Daemon gets started)

pi@raspberrypi:~ $ docker run --restart=always -d -p 3000:3000 -v /home/pi/hello:/hello -it khurram/pi:node nodejs /hello/hello.js
575b6cf68407fb08bf0fae895ea1170f5cbc02fba596f903c1901e17aa859747
pi@raspberrypi:~ $ curl
http://localhost/node
Hello World from NODE in Container
pi@raspberrypi:~ $ sudo shutdown -r now

Using docker ps we can see that the second container that we started with restart=always is running on the boot!

pi@raspberrypi:~ $ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS                       PORTS                    NAMES
575b6cf68407        khurram/pi:node     "nodejs /hello/hello."   About a minute ago   Up 4 seconds                 0.0.0.0:3000->3000/tcp   serene_mirz
49b347531127        khurram/pi:node     "nodejs /hello/hello."   6 minutes ago        Exited (143) 5 minutes ago                            stupefied_w
pi@raspberrypi:~ $ curl
http://localhost/node
Hello World from NODE in Container

We can delete the previous container using docker rm, and if we want to protect the Container ports being exposed on Host; we can use iptables!

Restarting Container on changing application files

We know that we need to restart the node process when application files are changed. In this case; we simply can restart the Docker Container, it takes almost the same time. This can be done using docker restart command; but we need to "know" the container name at runtime so that we can use it in our post-receive GIT script; i-e when new code is “pushed” the GIT hook can restart the container. We can have a static known name for our container if we run the container with –name parameter

pi@raspberrypi:~ $ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
575b6cf68407        khurram/pi:node     "nodejs /hello/hello."   3 hours ago         Up 3 hours          0.0.0.0:3000->3000/tcp   serene_mirzakhani
pi@raspberrypi:~ $ docker stop 575b6cf68407
575b6cf68407
pi@raspberrypi:~ $ docker rm 575b6cf68407
575b6cf68407
pi@raspberrypi:~ $ docker run --restart=always --name hello -d -p 3000:3000 -v /home/pi/hello:/hello -it khurram/pi:node nodejs /hello/hello.js
6774bc75b25e584b9f132bf78894a90789180d868c3176593b96e3f426db4118
pi@raspberrypi:~ $ curl
http://localhost/node
Hello World from NODE in Container
pi@raspberrypi:~ $ docker restart hello
hello
pi@raspberrypi:~ $ curl
http://localhost/node
Hello World from NODE in Container

We just need to add docker restart hello in hello.git/hooks/post-receive; similar to Staging Node Application where we added pm2 restart hello to restart the pm2 application

Resources

Happy Containering!