7

I've started using docker about a month ago now and cannot find a satisfying solution to the following situation.

I want to deploy a NodeJS application and since using ENTRYPOINT is a best practice I'd prefer to use this command: ENTRYPOINT ["node", "src/start.js"].

However I have not found a way to restart the entrypoint process within the container, which means that everytime I change something inside the nodejs app I have to restart the whole container which is mildly annoying in a development environment with a shared volume.

A solution I thought of would be to use a process manager for this, and do something like ENTRYPOINT ["pm2", "src/start.js"] but using a process manager for a single process seems wrong to me.

I'd like to ask for an approach that gets me as close to hot-swapping as possible without changing the Dockerfile at all between my "Development Docker" and the "Production Docker".

TL;DR: It should be possible to NOT have NodeJS or anything the app requires installed on my development machine but run everything from within a docker container while being able to restart the node process in said container without having to restart the container itself. It is not an option for me to change the Dockerfile and I'd like to use ENTRYPOINT.

EDIT:
Dockerfile

FROM mhart/alpine-node:4.4.7

# add curl and bash
RUN apk add --update curl bash

#Add user
RUN addgroup websites && adduser -s /bin/bash -D -G websites user-api

#Copy app
WORKDIR /srv/app
ADD src ./src/
ADD node_modules ./node_modules

#Expose port
EXPOSE 3000

ENTRYPOINT ["node", "src/start.js"]

Building the image with

docker build -t app .

Running the container on my workstation with

docker run -dit -p 53017:3000 --name app -v c:/Users/hesxenon/Projects/app:/srv/app app:latest
HES_Xenon
  • 103
  • 1
  • 1
  • 8
  • 1
    What's wrong with restarting the container? It's just a process with some cgroups and namespaces attached. The "container" is relatively light weight and won't be causing much slow down to a nodejs loading an app. Are you more concerned about rebuilding the container image between changes? – Matt Aug 05 '16 at 12:27
  • There's nothing inherently wrong with restarting the container, I'm just a really impatient person :) I don't like to wait 30s when I could just have the same effect in 2s. And while the container IS relatively lightweight I don't see much sense in restarting the whole underlying "system" for a single process. – HES_Xenon Aug 05 '16 at 19:05
  • 1
    Could you update the question with the steps you use to "restart" your app? A `docker run node src/start.js` should be imperceptibly slower than a `node src/start.js` so maybe you are rebuilding the image on each container restart? Or using docker-compose and restarting multiple containers that make up your "system"? I believe the question you are asking is "How can I speed up my Node.js+Docker development and build process without deviating too far from the Production build", we just have some differences in terminology. – Matt Aug 05 '16 at 22:23
  • So you're not rebuilding the image on each restart as you have the local app dir mounted. Is it a `docker stop` that takes a long time? – Matt Aug 06 '16 at 06:37
  • Now that you mention it, yes, docker stop takes quite long. – HES_Xenon Aug 06 '16 at 16:17

4 Answers4

2

Try handling the SIGTERM and SIGINT signals in Node.js.

A docker stop and docker restart sends a SIGTERM to your process. If there is no SIGTERM handler, docker will wait the default 10 second timeout until it sends a SIGKILL and your process restarts. This could be the bulk of your restart delay.

While you are dealing with signals, a ctrl-c sends a SIGINT so handle that too.

process.on('SIGTERM', function () {
  console.log('SIGTERM');
  process.exit(0);
}
process.on('SIGINT', function () {
  console.log('SIGINT');
  process.exit(0);
}

On Dockers Overhead

Here is some info on the overhead a container requires on top of the process you are launching. It is pretty minimal, so a quick test:

docker@default-docker:~$ time true
real    0m 0.00s
user    0m 0.00s
sys     0m 0.00s

docker@default-docker:~$ time docker run busybox true
real    0m 0.64s
user    0m 0.01s
sys     0m 0.00s

So a VM on my laptop takes about 0.64s longer to launch a process in a container compared to a plain process.

Community
  • 1
  • 1
Matt
  • 68,711
  • 7
  • 155
  • 158
2

There is a great npm module for this pm2. Install it as a global package in your nodejs base image.

Start your app with ENTRYPOINT ["pm2-docker", "src/start.js"]

You can then enter the docker image with docker exec -ti <containerid> <shell> and stop app using pm2 stop 0, then reconfigure, and start it again with pm2 start 0. Whitout killing the container thus to pid1 dying.

Unitech
  • 5,781
  • 5
  • 40
  • 47
MortenB
  • 2,749
  • 1
  • 31
  • 35
1

docker stop takes 10 seconds by default - you can adjust this with a -t arg, but docker restart should be nearly instant and should be the same thing as restarting node.

I'm not quite sure what you mean by underlying "system", it's not a VM and as long as you aren't restarting the Docker host, restarting the container (if it's set up properly) shouldn't take much longer than restarting the process itself.

ldg
  • 9,112
  • 2
  • 29
  • 44
  • that's why i put system in doublequotes, I know it's not a VM, but the container still needs to load some binaries from the host on startup, doesn't it? – HES_Xenon Aug 07 '16 at 08:53
  • @HES_Xenon docker does do *some* work and load `docker-containerd-shim` before your container process runs but that is all pretty quick. I've added some detail to my answer. – Matt Aug 07 '16 at 09:14
1

I think the best option is to use nodemon: https://www.npmjs.com/package/nodemon

It monitors the source directories you specify and restarts if anything changes.

Also as a added bonus to increase the startup time of the container, if you copy the package.json over then run npm install on the container it will use the cached package json, will probably cut it down to a second or two as the node_modules will be your biggest folder to copy: How to cache the RUN npm install instruction when docker build a Dockerfile

COPY package.json .
RUN npm install
COPY src ./src/
Community
  • 1
  • 1
Peter Grainger
  • 4,539
  • 1
  • 18
  • 22