Dockerizing Node
- Part 1: Running Node Application in Docker Container on Raspberry Pi
- Part 2: Dockerfile
- Part 3: Dockerizing Mongo and Express
- Part 4: docker-compose
- Part 5: Rancher–First Application
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:
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
- https://docs.docker.com/engine/reference/run/
- https://docs.docker.com/engine/userguide/containers/dockervolumes/
- https://docs.docker.com/engine/userguide/networking/default_network/binding/
- https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/
- https://wiki.debian.org/iptables
Happy Containering!