核心是让PID 1主动回收子进程:因僵尸已退出,kill无效;需确保PID 1响应SIGCHLD并调用waitpid(),推荐用tini(--init或ENTRYPOINT)替代默认shell。
清除 Docker 容器里的僵尸进程,核心不是“杀僵尸”,而是让 PID 1 进程能主动回收子进程——因为僵尸本身已退出,kill -9 对它完全无效。真正要处理的是容器内负责启动应用的那个父进程(即 PID 1),它必须响应 SIGCHLD 并调用 waitpid()。
先进入容器检查:
ps aux | grep ' Z '(注意空格,精准匹配 Z 状态)ps axo stat,pid,ppid,comm | grep "^Z" —— 显示僵尸的 PID、父 PID(PPID)和命令名ps aux | awk '$8 ~ /Z/ {print "PID:", $2, "PPID:", $3, "CMD:", $11}'如果看到 Z 状态进程,说明父进程(PPID 所指)没做回收。别急着 kill 僵尸,先看它的父进程是谁。
这是最快见效的应急方法,适用于调试或短期修复:
ps -o ppid= -p
kill -9
父进程一退出,所有子僵尸立刻变成孤儿进程,被容器的 PID 1(如 tini 或 sh)收养并立即回收。但如果父进程是常驻服务且会自动重启,新 fork 的子进程可能再次变僵尸——这说明问题没根治。
Docker 默认用 bash/sh 作 PID 1,但它不处理 SIGCHLD,所以子进程退出就滞留为僵尸。解决办法是换一个懂“收尸”的 PID 1:
ENTRYPOINT ["tini", "--"];或运行时加参数:docker run --init …
/etc/services.d/myapp/run,ENTRYPOINT 设为 /init
wait -n,容易失效光看“当前没僵尸”不够,得测它会不会再生:
sh -c 'sleep 0.01' &(快速启停子进程)ps axo stat | grep -c '^Z'
0,说明 PID 1 已稳定回收同时建议在应用代码里也补上 SIGCHLD 处理逻辑,双保险更稳妥。