Initially I was going to document the meaning of Docker layers, one of the killing features from Docker (IMHO), however during researching I came across something that I found curious and I’m going to dedicate a whole article about it.
Recently I documented how to dockerize Alpine on your Raspberry Pi, the next baby-step for me was to document how to build a more specific image from my dockerized Alpine image.
As the concept of layers in Docker proposes, your image can extend from a preexisting image. Therefore, from the image
kafebob/rpi-alpine-base I will create an image that provides a base environment for NodeJS applications.
You can use a precompiled version of NodeJS available from Alpine packages or a compiled version directly on your Raspberry. I’m going to document both.
From Alpine Repositories you can dockerize Node, a
Dockerfile like next one would be more than enough.
From previous Dockerfile, you will notice in line 3 & 4 the user and group
node, I’m using it for development purposes. To build an image called
kafebob/rpi-alpine-node locally available on your Pi, execute next command from same directory where previous Dockerfile is located.
sudo docker build -t kafebob/rpi-alpine-node .
Size of previous images is 43.3MB.
Now it’s time to create a container and check Node version.
sudo docker run --rm -it --user=node --entrypoint=/bin/sh kafebob/rpi-alpine-node
As you can see, latest available version from Alpine packages is Node.js-v6.10.3-r1 in Alpine 3.6 (at the time of writing this article).
So if you want to use a different/higher version, you should compile Node in your Raspberry Pi.
Here you could choose two alternatives, to compile your own version using the source code or to use NVM, although I must warn you, even though Raspberry is a masterpiece of hardware engineering in terms of computing power is not the best resource you can get, so be prepared to wait more than 2 hours for a compiled NodeJS binary.
I tried to compile my own version using Michael Hart’s Dockerfile, but Raspberry is literally inaccessible and after waiting more than 10 hours I’ll always got a message similar to
g++: internal compiler error: Killed (program cc1plus)
With NVM I had better luck and after 2 and a half hours compiling in my PI, I managed to get a correct image. For those who do not know, NVM is a simple bash script to manage multiple active node.js versions.
So then I’ll show you the Dockerfile, you are about to install NVM 0.33.2, Node 8.2.0 and NPM 5.3.0, as well user and group
node for development purposes. (You are able to change versions in line 3).
The key here is to use
-s flag for nvm install which requests nvm download Node source in order to compile it locally.
To create the image, similar to precompiled node version, in the same directory where previous Dockerfile is located:
sudo docker build -t kafebob/rpi-alpine-node .
Size of this image is 226MB! It is much larger since I have left compiling tools.
From this new image, we can create a new container and check installed versions
sudo docker run --rm -it --user=node --entrypoint=/bin/bash kafebob/rpi-alpine-node
If you do not want to wait for compilation, I uploaded this image to Docker Hub so you can do a pull and create a container based
on Node 8.2.0.
Instead of build, just pull the image from Docker Hub.
sudo docker pull kafebob/rpi-alpine-node
At the moment of writing this article, Alpine has Node v6.10.3 binary available in ARM architecture.
- If you do not care about node & npm versions just use a Dockerfile similar to the one described in Precompiled NodeJS section.
- If version is important, the only way I have found it is compiling Node on your Pi.
– You can use NVM and a Dockerfile similar to the one described in Compiling NodeJS section.
– You could use version from Michael Hart, but I have not yet found a way to make it work.
Do you think there is any other alternative? Please let me know in comments.
@ganlub has point it me out about
--virtual property in
apk add. This option is a feature to cleanup packages after a completed setup. Packages added under this virtual name can then be removed as one group.
For instance, you could add your build dependencies like this
apk --update add --virtual build-dependencies make gcc linux-headers and then at the end you could remove these dependencies like this
apk del build-dependencies.
@ganlub also told me there is no need to keep build dependencies in my NVM Docker version from Compiling Node Section, he’s right but the reason to keep them was to make life easier if your project is going to use modules like imagemin, this module needs to compile
jpegtran in order to be compatible with your system architecture. But it’s true, a node-base Docker image should be thinner as possible.
So then, I decided to update NVM Dockerfile.
With this Dockerfile you are going to get Node 8.2.1 and Npm 5.3.0 without building dependencies inside the container BUT!!! the image still has 230MB virtual size.
OMG! Why? Because the image depends on some layers and one of the layer is the build dependencies layer (line 5 from previous Dockerfile).
So I have a question, Is there a reason to delete build dependencies in line 23? At the end this image has almost same size as the image described here, where I didn’t care about delete dependencies.
After playing a bit with latest version, I have come to the conclusion that using Node through NVM with an user different than root is going to create more troubles than provide solutions. So I changed a bit the Dockerfile for
kafebob/rpi-alpine-node, in this version I’m using just user
root and the size of the image has decreased a lot, now 80MB because I’m using just one RUN command instead of three.
Still missing a step to use Node with any user but I’m working on it.