docker stop doesn't work for node process
Asked Answered
W

2

9

I want to be able to run node inside a docker container, and then be able to run docker stop <container>. This should stop the container on SIGTERM rather than timing out and doing a SIGKILL. Unfortunately, I seem to be missing something, and the information I have found seems to contradict other bits.

Here is a test Dockerfile:

FROM ubuntu:14.04
RUN apt-get update && apt-get install -y curl
RUN curl -sSL http://nodejs.org/dist/v0.11.14/node-v0.11.14-linux-x64.tar.gz | tar -xzf -
ADD test.js /
ENTRYPOINT ["/node-v0.11.14-linux-x64/bin/node", "/test.js"]

Here is the test.js referred to in the Dockerfile:

var http = require('http');

var server = http.createServer(function (req, res) {
  console.log('exiting');
  process.exit(0);
}).listen(3333, function (err) {
  console.log('pid is ' + process.pid)
});

I build it like so:

$ docker build -t test .

I run it like so:

$ docker run --name test -p 3333:3333 -d test

Then I run:

$ docker stop test

Whereupon the SIGTERM apparently doesn't work, causing it to timeout 10 seconds later and then die.

I've found that if I start the node task through sh -c then I can kill it with ^C from an interactive (-it) container, but I still can't get docker stop to work. This is contradictory to comments I've read saying sh doesn't pass on the signal, but might agree with other comments I've read saying that PID 1 doesn't get SIGTERM (since it's started via sh, it'll be PID 2).

The end goal is to be able to run docker start -a ... in an upstart job and be able to stop the service and it actually exits the container.

Whitsun answered 15/1, 2015 at 18:5 Comment(0)
M
7

My way to do this is to catch SIGINT (interrupt signal) in my JavaScript.

process.on('SIGINT', () => {
  console.info("Interrupted");
  process.exit(0);
})

This should do the trick when you press Ctrl+C.

Moberg answered 8/10, 2019 at 8:20 Comment(2)
docker stop uses SIGTERM. I added that piece of code both for SIGINT and SIGTERMCyclostome
@Cyclostome yes, you are correct. Using SIGINT won't have any effect, while using SIGTERM will help with terminating the processOletaoletha
W
2

Ok, I figured out a workaround myself, which I'll venture as an answer in the hope it helps others. It doesn't completely answer why the signals weren't working before, but it does give me the behaviour I want.

Using baseimage-docker seems to solve the issue. Here's what I did to get this working with the minimal test example above:

Keep test.js as is.

Modify Dockerfile to look like the following:

FROM phusion/baseimage:0.9.15

# disable SSH
RUN rm -rf /etc/service/sshd /etc/my_init.d/00_regen_ssh_host_keys.sh

# install curl and node as before
RUN apt-get update && apt-get install -y curl
RUN curl -sSL http://nodejs.org/dist/v0.11.14/node-v0.11.14-linux-x64.tar.gz | tar -xzf -

# the baseimage init process
CMD ["/sbin/my_init"]

# create a directory for the runit script and add it
RUN mkdir /etc/service/app
ADD run.sh /etc/service/app/run

# install the application
ADD test.js /

baseimage-docker includes an init process (/sbin/my_init) which handles starting other processes and dealing with zombie processes. It uses runit for service supervision. The Dockerfile therefore sets the my_init process as the command to run on boot, and adds a script /etc/service for runit to pick it up.

The run.sh script is simple:

#!/bin/sh
exec /node-v0.11.14-linux-x64/bin/node /test.js

Don't forget to chmod +x run.sh!

By default, runit will automatically restart the service if it goes down.

Following these steps (and build, run, and stop as before), the container properly responds to requests for it to shutdown, in a timely fashion.

Whitsun answered 19/1, 2015 at 10:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.