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.
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
CMDline like this:Here an
shprocess will be started which evaluates the$JAVA_OPTSvariable and startsjavaas a child process. The problem with this is thatshdoesn't pass signals likeSIGTERMto its children. So, when a container is stopped, theSIGTERMsignal will be sent to theshprocess, but the signal won't reach thejavaprocess, 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 -cdoes not pass signals and that the executable will not receive aSIGTERMsignal fromdocker 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 usingexecandgosucommands".The problematic example above can indeed be fixed by adding
execto the mix, becauseexecwill replace theshprocess with thejavaprocess, so it will be a top-level process with process ID 1:I think it would be good to point this out in the documentation for
CMD. I have already encountered a lot ofDockerfilefiles that get this wrong.P.S. I know another solution for the example above is using the
JAVA_TOOL_OPTIONSorJDK_JAVA_OPTIONSenvironment variable, which don't need to be explicitly passed tojava, but are picked up automatically, but this is not generally applicable to any process of course.