loading...

Docker Container: Uncaught Kill Signal

mdemblani profile image Manish Demblani ・3 min read

Has it ever happened, that you are unable to terminate/stop a running Docker container instance. Trying to press the Ctrl+C (Cmd+C) in your terminal multiple times, but being hapless all the time.

Ultimately you resort to killing the Docker process and in this way, resulting in your container stopping successfully with the famous exit code 0(if you are lucky) or it getting killed forcefully with an exit error.

Docker kill corrupts

In-case an exit error occurs due to killing the container, it results in corruption of the data in the volume associated with that container and hence, you have to re-build your containers.

There are two reasons due to which this happens:

  1. The container does not listen to the kill signals such as SIGINT that is generated by Ctrl+C (Cmd+C) key combination
  2. The container shutdown process exceeds the timeout time specified by the docker process which it allots a container to shutdown, resulting in the docker process forcibly killing the container and hence the corruption.

Solution

Now there are two known ways to solve the above stated problem:

  1. Increase the Timeout
  2. Patch the Entry Script

Increase the Timeout

In this case, you simply inform the docker process to wait for some more time until the container performs a graceful shutdown. The value be which the timeout should be increased, should be a little more than the time the container takes to shutdown after detecting the signal. To get started, you can set the timeout to around 30 seconds as a safe-bet.

docker stop -t 30 CONTAINER_NAME

Patch the Entry Script

In this method, we basically monkey-patch the entry file of the container with custom code, that allows us to detect the SIGINT signal sent to the container.

To patch the entry script, we will have to create a listener file(signal-listener.sh):


#!/bin/bash
# A wrapper around /entrypoint.sh to trap the SIGINT signal (Ctrl+C) and forwards it to the mysql daemon
# In other words : traps SIGINT and SIGTERM signals and forwards them to the child process as SIGTERM signals

signalListener() {
    "$@" &
    pid="$!"
    trap "echo 'Stopping PID $pid'; kill -SIGTERM $pid" SIGINT SIGTERM

    # A signal emitted while waiting will make the wait command return code > 128
    # Let's wrap it in a loop that doesn't end before the process is indeed stopped
    while kill -0 $pid > /dev/null 2>&1; do
        wait
    done
}

signalListener /entrypoint.sh $@

Make sure the file is in the same folder as the Dockerfile you will build.

The next step, is to wrap the entrypoint.sh file with the signal-listener.sh file. In your Dockerfile add the following lines at the very end:


COPY signal-listener.sh /run.sh

# Entrypoint overload to catch the ctrl+c and stop signals
ENTRYPOINT ["/bin/bash", "/run.sh"]
CMD [ADD_THE_DEFAULT_DOCKER_CMD_IF_ANY]

Now if you observe, after running the docker container, you could press the Ctrl+C(Cmd+C) combination and pass it the SIGINT command. This command would now be detected by the container and it would terminate gracefully.

Credits:
This post is based on the solution provided in the following Github issue: Container does not catch signals and exit (Ctrl+C). Thanks to user Jérémy VIGNELLES for the solution provided.

Discussion

pic
Editor guide