spring-attic / top-spring-boot-docker

Spring Boot Docker:: Topical guide to using Docker and how to create container images for Spring Boot applications :: spring-boot
https://spring.io/guides/topicals/spring-boot-docker
180 stars 127 forks source link

Dangerous suggestions in Dockerfile ENTRYPOINT samples #13

Closed sbreakey closed 2 years ago

sbreakey commented 3 years ago

In the "The Entry Point" section it is suggested to wrap the launching of the application in sh to be able to pass env vars to the launch command. E.g. ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"]

From what I have read elsewhere this is a very bad idea, as the java process that is spawned will not receive a SIGTERM when docker begins to stop the container (SIGTERM is sent to PID 1 which is sh in this case, which does not forward the SIGTERM to child processes). The spring application will therefore simply have the plug pulled rather than allowing the shutdown hooks to kick in for the application to gracefully shut down.

It might be a good idea to either remove this suggestion, or put a big nasty warning next to it.

Incidentally, we are using ENTRYPOINT ["java","-jar","/app.jar"] and setting env var JAVA_TOOL_OPTIONS instead of JAVA_OPTS, which seems to work fine as this env var is read automatically, although not sure if this has any detrimental impact yet)

sbreakey commented 3 years ago

Another possible solution, that may be a bit better than swapping JAVA_OPTS for JAVA_TOOL_OPTIONS as mentioned above, is to change the entrypoint to: ENTRYPOINT ["sh", "-c", "exec java ${JAVA_OPTS} -jar /app.jar"]

Instead of spawning a child process, exec replaces the current process, thereby retaining PID 1 for the java process itself so it receives the SIGTERM directly.

dsyer commented 3 years ago

It's funny because I find that the Java process gets the SIGTERM even without an "exec". I wonder what might be different? Do all processes get SIGTERM when process 1 dies? (Note that the "exec" form was used in the run.sh example in this guide for precisely the reason you outlined. It just didn't seem necessary empirically for the literal entry point style.)

dsyer commented 2 years ago

I'm pretty sure it works as described. To verify:

$ docker exec -ti myapp /bin/sh
/ # ps -elf
PID   USER     TIME  COMMAND
    1 root      0:11 java -jar /app.jar
   31 root      0:00 /bin/sh
   38 root      0:00 ps -elf
sbreakey commented 2 years ago

@dsyer thanks for updating this. Could it be dependent on the linux distro in use? I see the example in the docs is FROM eclipse-temurin:17-jdk-alpine, and when i try test this with that base image, my results are as per yours.

However in my case, our base images are FROM ubuntu:18.04, and if I leave off the exec in my ENTRYPOINT, like so: ENTRYPOINT [ "sh", "-c", ". /expandEnvSecrets.sh && java $JAVA_OPTS -jar /application.jar"] then when checking the pids, i see:

root@c872a302032a:/# ps -elf
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root         1     0  0  80   0 -  1160 -      17:54 ?        00:00:00 sh -c . /expandEnvSecrets.sh && java $JAVA_OPTS -jar /application.jar
4 S root        17     1 99  80   0 - 1712195 -    17:54 ?        00:00:20 java -jar /application.jar
4 S root       151     0  0  80   0 -  4630 -      17:54 pts/0    00:00:00 bash
4 R root       161   151  0  80   0 -  8604 -      17:54 pts/0    00:00:00 ps -elf
dsyer commented 2 years ago

Interesting. That’s probably worth a sidebar in the guide. You can PR it if you want (I won’t get to it straight away anyway).