使用 'docker stop' 和官方 java 图像的 java 进程未收到 SIGTERM
SIGTERM not received by java process using 'docker stop' and the official java image
我正在 运行在 Docker 容器中使用基于 debian/jessie
.
的图像 java:7u79
创建一个 dropwizard Java 应用程序
我的 Java 应用程序处理 SIGTERM
信号以正常关闭。当我 运行 没有 Docker 的应用程序时,SIGTERM
处理工作完美。
当我在 Docker 容器中 运行 时,当我发出 docker stop
命令时,SIGTERM
没有到达 Java 应用程序。它会在 10 秒后突然终止进程。
我的Dockerfile
:
FROM java:7u79
COPY dropwizard-example-1.0.0.jar /opt/dropwizard/
COPY example.keystore /opt/dropwizard/
COPY example.yml /opt/dropwizard/
WORKDIR /opt/dropwizard
RUN java -jar dropwizard-example-1.0.0.jar db migrate /opt/dropwizard/example.yml
CMD java -jar dropwizard-example-1.0.0.jar server /opt/dropwizard/example.yml
EXPOSE 8080 8081
这个Dockerfile
有什么问题?有没有其他方法可以解决这个问题?
假设您通过在 Dockerfile
中定义以下内容来启动 Java 服务:
CMD java -jar ...
当您现在进入容器并列出进程时,例如通过 docker exec -it <containerName> ps AHf
(我没有尝试使用 java
但使用 ubuntu
图像)你看到你的 Java 进程不是根进程(不是带有 PID 的进程1) 但是 /bin/sh
进程的子进程:
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 18:27 ? 00:00:00 /bin/sh -c java -jar ...
root 8 1 0 18:27 ? 00:00:00 java -jar ...
所以基本上你有一个 Linux shell,它是 PID 1 的主进程,它有一个 PID 8 的子进程 (Java)。
要使信号处理正常工作,您应该避免那些 shell 父进程。这可以通过使用内置 shell 命令 exec
来完成。这将使子进程接管父进程。所以最后,以前的父进程不再存在了。子进程成为 PID 为 1 的进程。在 Dockerfile
:
中尝试以下操作
CMD exec java -jar ...
然后进程列表应该显示如下内容:
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 18:30 ? 00:00:00 java -jar ...
现在你只有一个 PID 为 1 的进程。通常一个好的做法是让 docker 容器只包含一个进程 - PID 为 1 的进程(或者如果你真的需要更多进程,那么你应该使用例如 supervisord
作为 PID 1,它本身负责为其子进程处理信号)。
使用该设置,SIGTERM
将直接由 Java 进程处理。不再有 shell 进程,其间可能会中断信号处理。
编辑:
同样的 exec
效果可以通过使用不同的 CMD
隐含的语法来实现(感谢 Andy 的评论):
CMD ["java", "-jar", "..."]
@h3nrik 的回答是正确的,但有时您确实需要使用脚本来设置启动。在大多数情况下,只需使用 exec 命令即可解决问题:
#!/bin/sh
#--- Preparations
exec java -jar ...
看看这个精彩blog post
我正在 运行在 Docker 容器中使用基于 debian/jessie
.
java:7u79
创建一个 dropwizard Java 应用程序
我的 Java 应用程序处理 SIGTERM
信号以正常关闭。当我 运行 没有 Docker 的应用程序时,SIGTERM
处理工作完美。
当我在 Docker 容器中 运行 时,当我发出 docker stop
命令时,SIGTERM
没有到达 Java 应用程序。它会在 10 秒后突然终止进程。
我的Dockerfile
:
FROM java:7u79
COPY dropwizard-example-1.0.0.jar /opt/dropwizard/
COPY example.keystore /opt/dropwizard/
COPY example.yml /opt/dropwizard/
WORKDIR /opt/dropwizard
RUN java -jar dropwizard-example-1.0.0.jar db migrate /opt/dropwizard/example.yml
CMD java -jar dropwizard-example-1.0.0.jar server /opt/dropwizard/example.yml
EXPOSE 8080 8081
这个Dockerfile
有什么问题?有没有其他方法可以解决这个问题?
假设您通过在 Dockerfile
中定义以下内容来启动 Java 服务:
CMD java -jar ...
当您现在进入容器并列出进程时,例如通过 docker exec -it <containerName> ps AHf
(我没有尝试使用 java
但使用 ubuntu
图像)你看到你的 Java 进程不是根进程(不是带有 PID 的进程1) 但是 /bin/sh
进程的子进程:
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 18:27 ? 00:00:00 /bin/sh -c java -jar ...
root 8 1 0 18:27 ? 00:00:00 java -jar ...
所以基本上你有一个 Linux shell,它是 PID 1 的主进程,它有一个 PID 8 的子进程 (Java)。
要使信号处理正常工作,您应该避免那些 shell 父进程。这可以通过使用内置 shell 命令 exec
来完成。这将使子进程接管父进程。所以最后,以前的父进程不再存在了。子进程成为 PID 为 1 的进程。在 Dockerfile
:
CMD exec java -jar ...
然后进程列表应该显示如下内容:
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 18:30 ? 00:00:00 java -jar ...
现在你只有一个 PID 为 1 的进程。通常一个好的做法是让 docker 容器只包含一个进程 - PID 为 1 的进程(或者如果你真的需要更多进程,那么你应该使用例如 supervisord
作为 PID 1,它本身负责为其子进程处理信号)。
使用该设置,SIGTERM
将直接由 Java 进程处理。不再有 shell 进程,其间可能会中断信号处理。
编辑:
同样的 exec
效果可以通过使用不同的 CMD
隐含的语法来实现(感谢 Andy 的评论):
CMD ["java", "-jar", "..."]
@h3nrik 的回答是正确的,但有时您确实需要使用脚本来设置启动。在大多数情况下,只需使用 exec 命令即可解决问题:
#!/bin/sh
#--- Preparations
exec java -jar ...
看看这个精彩blog post