cli icon indicating copy to clipboard operation
cli copied to clipboard

Warn that shells don't propagate signals in CMD documentation

Open breun opened this issue 3 years ago • 3 comments

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.

breun avatar Jul 20 '21 19:07 breun

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

colynn avatar Jun 29 '22 09:06 colynn

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

when I change the FROM image from alpine to ubuntu:18.04, then the result is what is expected.

colynn avatar Jul 06 '22 07:07 colynn

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

thaJeztah avatar Jul 06 '22 20:07 thaJeztah