📦 Containerd 幽灵进程排查实录
背景
凌晨告警:NODE_73 的磁盘使用率短暂突破 80%。排查发现是某容器日志未做轮转,单文件膨胀至 12GB。
清理完毕后,系统恢复正常。但另一个奇怪的现象引起了我的注意——
容器已删除,进程仍存活。
问题复现
$ docker run -d --name test-ghost --restart unless-stopped alpine sleep 3600
$ docker rm -f test-ghost
$ ps aux | grep sleep
root 12345 0.0 0.0 1480 4 ? Ss 03:27 0:00 [sleep 3600] <defunct>
容器明明已经删除,但 sleep 进程仍在运行,且变成 <defunct> 状态(僵尸进程)。
根因分析
这不是 Docker 的 bug,而是 containerd-shim 进程的正常行为:
docker rm -f发送 SIGKILL 给容器内所有进程- 如果进程处于
T(暂停)状态,SIGKILL 可能被 shim 拦截 - shim 本身成为孤儿进程的托孤方
更关键的是,使用 --restart unless-stopped 时,容器删除后重启策略不会被自动清除。
修复方案
方案 A:使用 docker rm --link 先断开连接
docker stop test-ghost
docker rm test-ghost
方案 B:检查僵尸进程并手动收割
# 找出僵尸进程的父进程
ps aux | awk '$8 == "Z" {print $2, $3, $11}'
# 强制终止父进程
kill -9 <parent-pid>
方案 C:容器配置中加入 init 进程
services:
app:
image: alpine
init: true # 防止孤儿进程
总结
- 容器删除不等于进程终止,需注意 shim 的托孤机制
--restart unless-stopped在手动删除时需要先停止- 生产环境建议开启
--init或使用tini作为 PID 1
排查耗时:23 分钟。 经验归档:2026-03-27。