cli
cli copied to clipboard
Warn that shells don't propagate signals in CMD documentation
Description
The documentation for CMD says: "If you want shell processing then you must either use the shell form or execute a shell directly, for example: CMD [ "sh", "-c", "echo $HOME" ]
."
While this is correct, I think it would be good to warn that starting an application as a child of a shell breaks signal passing.
This may for instance lead someone to use a CMD
line like this:
CMD [ "sh", "-c", "java $JAVA_OPTS -jar app.jar" ]
Here an sh
process will be started which evaluates the $JAVA_OPTS
variable and starts java
as a child process. The problem with this is that sh
doesn't pass signals like SIGTERM
to its children. So, when a container is stopped, the SIGTERM
signal will be sent to the sh
process, but the signal won't reach the java
process, so the application will not get a chance to respond to this signal by doing a graceful shutdown and will just be killed suddenly when the container gets stopped.
The documentation for ENTRYPOINT does mention that sh -c
does not pass signals and that the executable will not receive a SIGTERM
signal from docker stop <container>
. Further down this documentation says: "If you need to write a starter script for a single executable, you can ensure that the final executable receives the Unix signals by using exec
and gosu
commands".
The problematic example above can indeed be fixed by adding exec
to the mix, because exec
will replace the sh
process with the java
process, so it will be a top-level process with process ID 1:
CMD [ "sh", "-c", "exec java $JAVA_OPTS -jar app.jar" ]
I think it would be good to point this out in the documentation for CMD
. I have already encountered a lot of Dockerfile
files that get this wrong.
P.S. I know another solution for the example above is using the JAVA_TOOL_OPTIONS
or JDK_JAVA_OPTIONS
environment variable, which don't need to be explicitly passed to java
, but are picked up automatically, but this is not generally applicable to any process of course.
Based on this demo code, whatever I use Dockerfile.v1
or Dockerfile.v2
, it always can receive SIGTERM
signal, It is a puzzle.
Env Info as below:
- docker client:
19.03.14
- docker server:
19.03.14
- go version:
go1.15.11
Based on this demo code, whatever I use
Dockerfile.v1
orDockerfile.v2
, it always can receiveSIGTERM
signal, It is a puzzle.Env Info as below:
- docker client:
19.03.14
- docker server:
19.03.14
- go version:
go1.15.11
when I change the FROM
image from alpine
to ubuntu:18.04
, then the result is what is expected.
when I change the FROM image from alpine to ubuntu:18.04, then the result is what is expected.
depends on the container's main process; I think recent versions of bash
made some changes to now also handle signals when running as PID1.
For comparison, a busybox image vs ubuntu image also shows a difference (busybox not accepting the SIGTERM
, so docker forcibly stops it after 10 seconds);
docker run -dit --rm --name one busybox
time docker stop one
# takes ~ 10 seconds
docker run -dit --rm --name two ubuntu
time docker stop two
# instant