Problem

Correctly handling certain use cases requires that containers running under Docker properly receive and handle sent signals. It comes to mind graceful process termination when systemd will send a SIGTERM to the process giving it a chance to cleanly stop before it’s being sent a SIGKILL that results in immediate process stop. Handling signals in this case is quite simple and well-documented, either using the newer sigaction function or the older signal. However, if the process ran by systemd is in fact a Docker container then things are not so simple.

The Docker daemon will forward signals to the process running inside the container with PID=1. When running outside a container, that’s usually process init, the root of all the process hierarchy. Inside a container, however, the process, may or may not be the root process (thus having PID = 1).

Example: Dockerfile using the shell form for CMD or ENTRYPOINT:

CMD “echo ‘hello world’”
ENTRYPOINT “echo ‘hello world’”

These will result in an execution under the shell:

/usr/bin/bash -c “echo ‘hello world’”

What this means is that the shell process (bash here) will have PID=1, therefore it is the shell that will get signals sent by Docker. The “echo” process running above will not receive those signals.

exec form

What is recommended in this case is running the exec form for CMD or ENTRYPOINT where the arguments are inside a JSON array, as below:

CMD [“echo”, “hello world’]
ENTRYPOINT [“echo”, “hello world”]

Now the echo process will have PID=1 and will properly receive all the signals. This works very well. Unless, the shell is needed.

Environment variable substitution

Environment ariable substitution for environment variables declared as ENV in the Dockerfile does not work with exec form. A shell would be needed in order to perform that substitution.

What’s needed is a custom process that properly forwards signals to children processes, but that also properly does variable substitutions and passes expanded environment variables to launched children processes.

Check the init2 for a possible solution.