5

I was trying to catch SIGTERM signal from a docker instance (basically when docker stop is called) but couldn't find a way since I have different results for each try I performed.

Following is the setup I have

Dockerfile

FROM gitlab/gitlab-runner:alpine

COPY ./start.sh /start.sh

ENTRYPOINT ["/start.sh"]

start.sh

#!/bin/bash

deregister_runner() {
    echo "even if nothing happened, something happened"
    exit
}
trap deregister_runner SIGTERM

while true; do
    sleep 10
done

Now I build the docker image

$ docker build -t dockertrapcatch .
Sending build context to Docker daemon  51.71kB
Step 1/3 : FROM gitlab/gitlab-runner:alpine
 ---> 9f8c39873bee
Step 2/3 : COPY ./start.sh /start.sh
 ---> Using cache
 ---> ebb3cac0c509
Step 3/3 : ENTRYPOINT ["/start.sh"]
 ---> Using cache
 ---> 7ab67fe5a714
Successfully built 7ab67fe5a714
Successfully tagged dockertrapcatch:latest

Run the docker

$ docker run -it dockertrapcatch

Now when I run docker stop <<container_id_here>> or docker kill --signal=SIGTERM <<container_id_here>>, my deregister_runner function is not called.

After that I changed the start.sh script as following (SIGKILL ==> EXIT)

#!/bin/bash

deregister_runner() {
    echo "even if nothing happened, something happened"
    exit
}
trap deregister_runner EXIT

while true; do
    sleep 10
done

After this change and creating the docker image and running it docker stop <<container_id_here>> still does not work but docker kill --signal=SIGTERM <<container_id_here>> works!

$ docker run -it dockertrapcatch
even if nothing happened, something happened
$ docker kill --signal=SIGTERM 6b667af4ac6c
6b667af4ac6c

I read that actually docker stop sends a SIGTERM but I think this time it is not working? Any idea?

ErikMD
  • 13,377
  • 3
  • 35
  • 71
abdsahin
  • 63
  • 1
  • 5
  • I believe [this other StackOverflow answer](https://stackoverflow.com/a/50536078/9164010) might be useful for your question. – ErikMD Dec 29 '19 at 20:57
  • Does this answer your question? [Docker Run Script to catch interruption signal](https://stackoverflow.com/questions/50257930/docker-run-script-to-catch-interruption-signal) – ErikMD Dec 29 '19 at 21:01
  • Actually the base image you use seems to behave differently w.r.t. that of the thread I mentioned… I'll try to recap this in a more complete answer. – ErikMD Dec 29 '19 at 21:20

1 Answers1

6

I can reproduce the issue you raise, while it does not show up when I replace the base image with debian:10, for example.

It happens the issue is not due to alpine but to the gitlab/gitlab-runner:alpine image itself, namely this Dockerfile contains the following line:

STOPSIGNAL SIGQUIT

To be more precise, the line above means docker stop will send a SIGQUIT signal to the running containers (and wait for a "graceful termination time" before killing the containers, as if a docker kill were issued in the end).

If this Dockerfile directive is not used, the default signal sent by docker stop is SIGTERM.

Beware that SIGKILL would be a very poor choice for STOPSIGNAL, given that the KILL signal cannot be trapped.

So, your first example should work if you use the following line:

trap deregister_runner SIGINT SIGQUIT SIGTERM

This way, your cleanup function deregister_runner will be triggered anytime you issue a docker stop, or use the Ctrl-C keybinding (thanks to SIGINT).

Finally, two additional notes related to this question of Docker, bash and signals:

  • The "graceful termination time" (between stop and kill) can be customized, and there are some pitfalls when using a Bash entrypoint (regarding the "signal propagation"). I explained both issues in more detail in this SO answer: Speed up docker-compose shutdown.

  • Beware that in many alpine images, bash is not pre-installed, e.g.:

    $ sudo docker run --rm -it alpine /bin/bash
      /usr/bin/docker: Error response from daemon: OCI runtime create failed:
      container_linux.go:346: starting container process caused
      "exec: \"/bin/bash\": stat /bin/bash: no such file or directory": unknown.
    

    (fortunately, this was not the case of gitlab/gitlab-runner:alpine, which indeed contains the bash package :)

ErikMD
  • 13,377
  • 3
  • 35
  • 71