Node.js applications are usually deployed with PM2 or Docker. If you want to deploy with Docker, you may also encounter the problem of how to choose the node image.
-
Node :
: This is the official default image, built on Debian, which can be specified as:
- Debian 10 (Buster) — Current Stable
- Debian 9 (stretch) — oldstable
- Debian 8 (Jessie) — Older Stable
- Debian 7 (Wheezy) — The stable version that was eliminated
These images are built based on buildpack-deps (see Dockerfile). The advantage of these images is that they have a lot of dependencies to install, such as curl and wget, but the disadvantages are that they are too large.
-
Node :
– Slim: This is a compact version image with redundant dependencies removed. Also built on Debian, it is much smaller than the default image, removes many common software packages, and retains only the basic Node environment.
-
Node :
-alpine: This version is built on the Alpine image, which is smaller than the Debian Slim version and arguably the smallest Node image. It’s small, but it does a lot of work. Regular Node.js applications will run, but if alpine uses a C ++ extension, it’s not worth using. Alpine uses musl instead of glibc, and some C/C ++ environments may not be compatible.
Next, based on Node.js version 14, download the above three images for comparison:
docker pull node:14-buster
docker pull node:14-buster-slim
docker pull node:14-alpine
Copy the code
Mirror volume size comparison
Run the docker images | grep node:
node 14-buster 70c62b76e4cc 5 hours ago 912MB
node 14-buster-slim 9917d232c3dd 5 hours ago 181MB
node 14-alpine 9db54a688554 5 hours ago 117MB
Copy the code
As you can see, the default mirror image of Node: 14-Buster is 912MB, which is quite large. Node: 14-Buster Slim is much smaller, while Node: 14-Alpine is much lighter.
Container memory usage comparison
Start containers with the images above:
docker run -d --name node-14-buster node:14-buster node -e "require('http').createServer((req, res) => res.end('Hello World')).listen(3030)"
docker run -d --name node-14-buster-slim node:14-buster-slim node -e "require('http').createServer((req, res) => res.end('Hello World')).listen(3030)"
docker run -d --name node-14-alpine node:14-alpine node -e "require('http').createServer((req, res) => res.end('Hello World')).listen(3030)"
Copy the code
Run Docker Stats to see the runtime memory footprint
NAME CPU % MEM USAGE/LIMIT MEM % NET I/O BLOCK I/O PIDS node-14-alpine 0.00% 4.809MiB/1.796GiB 0.26% 0B/0B 0B/0B 7 Node-14-buster 0.00% 4.238MiB/1.796GiB 0.23% 0B/0B 0B/0B 7 Node-14-Buster 0.00% 4.207MiB/1.796GiB 0.23% 0B/0B 4.88 MB / 0 b 7Copy the code
The difference isn’t huge — the Alpine takes up slightly more memory, but both are within acceptable limits.
How to choose?
From the perspective of node.js applications, how should you select an image? The biggest difference between alpine and buster/ bust-slim is the C++ plugin. For example, if you use sharp to process images in your package, alpine mirror won’t work because it’s incompatible. In other cases, if your application and dependencies are written purely in node.js and do not involve C++ plug-ins, alpine mirroring is recommended.
The buster/ bust-slim image can run all node.js projects, including those C++ dependencies, but there is a pitfall that projects started with NPM start cannot listen to docker SIGTERM signals. If the process does not have the corresponding SIGTERM event, docker waits 10 seconds by default and then kills the application forcibly. The test procedure is as follows:
-
First create a code folder in the root directory and say index.js:
console.log('pid', process.pid) process.on('exit'.(code) = > { console.log('Process exit event code:', code) }) process.on('SIGTERM'.(code) = > { console.log('SIGTERM', code) process.exit(0)})require('http').createServer((req, res) = > res.end('Hello World')).listen(3030) Copy the code
-
Then create package.json:
{ "scripts": { "start": "node index.js"}}Copy the code
-
Start container:
docker run -d --name node-14-buster -v /code:/code node:14-buster sh -c 'cd code && npm start' docker run -d --name node-14-buster-slim -v /code:/code node:14-buster sh -c 'cd code && npm start' docker run -d --name node-14-alpine -v /code:/code node:14-alpine sh -c 'cd code && npm start' Copy the code
-
Restart the container
When rebooting buster/ Bust-Slim mirror, it was found that the speed was very slow, reaching the timeout time of 10s, because there was no response to the SIGTERM signal transmitted by docker, which can be tested by the following code:
docker logs -f node-14-buster Then open a new command line docker kill -s SIGTERM node-14-buster docker restart node-14-buster Copy the code
Neither buster nor Bust-Slim can pick up SIGTERM signals, while Alpine can.
Custom mirror
As it happens, our current project uses both the C++ plugin and the SIGTERM event, so none of the three official images are available. However, it is easy to customize a node.js image for other operating systems, such as Centos7 to create a Dockerfile:
FROM centos:7
RUN curl -L https://dl.yarnpkg.com/rpm/yarn.repo -o /etc/yum.repos.d/yarn.repo
RUN curl --silent --location https://rpm.nodesource.com/setup_14.x | bash -
RUN yum install -y nodejs yarn
WORKDIR /code
EXPOSE 80
CMD npm start
Copy the code
Then build the Node.js image:
docker build -t node:14-centos7 .
Copy the code