Docker 容器中出现僵尸进程
背景
在工作中需要将自己的产品容器化,dockerfile中的entrypoint为[“/bin/sh”, “-c”, “java -jar xxx”]
结果偶然发现,运行一段时间后容器里有大量僵尸进程

相关业务逻辑
java主程序调用shell脚本,而shell脚本又会去启动sipp这个第三方开源的测试工具,随后结束本次逻辑。
分析
发现问题后,又在物理机上进行测试,结果发现物理机没有这种情况。
先去看下sipp源码:

而通过阅读sipp源码了解到,sipp启动时会fork自己,然后在子进程继续剩下的逻辑,父进程直接exit退出,sipp子进程继续测试。因此这个sipp子进程会因为父进程的退出变为孤儿进程,此时这个孤儿进程被pid为1的进程接管,而在容器中我们的java主程序pid为1,所以最终由java主程接管,而由于java无法直接捕获和处理操作系统级别的信号,比如这里的SIGCHLD,导致无法处理僵尸进程。
在 Java 中,无法直接捕获和处理操作系统级别的信号(如 SIGCHLD)。Java 的信号处理机制是基于 Java Signal API 的,它只支持捕获一些特定的信号,如 SIGINT、SIGTERM、SIGHUP 等。
解决方案
本以为entrypoint直接用init来启动java主程就能解决这个问题,[“init”, “-g”, “-e”, “java -jar xxx.jar”],结果在测试时发现报错
Couldn't find an alternative telinit implementation to spawn.
在 Docker 容器中,默认情况下,init 命令是没有可替代的实现的。这是因为容器环境与宿主机的进程管理方式不同,容器中没有一个真正的 init 进程来接管 PID 1。
使用守护进程管理工具:在容器中使用守护进程管理工具,例如 dumb-init、tini 或 supervisord,来替代默认的 init 进程。这些工具可以正确处理信号、孤儿进程和进程终止状态,以更好地管理容器内的进程。
搜了一下相关问题,决定用tini来作为init进程,用tini来启动java主程,这样java主程就不用接管僵尸进程了。
FROM centos:7
RUN yum -y remove java && \
yum install -y java-1.8.0-openjdk && \
yum clean all && \
wget https://github.com/krallin/tini/releases/download/v0.19.0/tini -O /sbin/tini && \
chmod +x /sbin/tini
ADD xxx.jar /bin/
EXPOSE 8080
ENTRYPOINT ["/sbin/tini", "--", "/bin/sh", "-c", "java -Dsun.misc.URLClassPath.disableJarChecking=true -jar /bin/xxx.jar"]
测试:

问题解决!
相关介绍:https://medium.com/@BeNitinAgarwal/an-init-system-inside-the-docker-container-3821ee233f4b
文章讲述了在Docker容器中,由于Java程序无法处理SIGCHLD信号,导致出现僵尸进程。通过分析发现,sipp工具的子进程在父进程退出后变成孤儿进程,由PID为1的Java程序接管。为了解决这个问题,作者尝试使用tini作为init进程,有效管理容器内的进程,从而解决了僵尸进程的问题。

3620

被折叠的 条评论
为什么被折叠?



