容器里 ps 能看到几行 defunct 进程,数量随着请求量一起增长,父进程都是 PID 1,句柄数跟着一路往上走,重启容器依然出现。日志里能对上时间点,这种反而好排。
应用作为一号进程没有接管子进程回收,退出的子进程没人 wait,进程表里一直留着条目,攒多了就把可用的 pid 耗尽,看进程状态才看得出来。这一点先确认清楚,后面就不用反复试。
docker run --init -d --name app app:1.0 # compose 里对应写法 init: true docker exec app ps -eo stat,comm | grep Z
围绕「容器 PID 1 僵尸进程」排查时,命令行跑一遍比在面板里点十次有用,面板会替你做掉不少隐式操作。先做最小复现,能稳定跑出来的问题,排查成本会低一个数量级。
别只看报错的最后一行,往上看几行,真正的起因通常在上面。
处理上按这个顺序:给容器加上 --init 参数 让 tini 来当一号进程 回收就有人管了;或者自己在应用里把信号处理和子进程回收写完整;用编排平台的时候确认一下它有没有默认注入 init。
结果对不对很好判断,跑一整天之后再 ps 看不到 Z 状态进程,pid 数量保持平稳,内存也不再缓慢上涨。
