查 containerd shim 卡住需先用 ps 找 pid,再通过 ls -l /proc//fd/ 检查残留 fd,最后用 strace -p 观察阻塞在 wait4/epoll_wait/recvfrom;若 runc 已退出但 shim 仍持 socket,需手动清理对应 runtime 目录。

containerd shim 进程卡住,怎么查它在等什么?
shim 进程本身不执行容器,只做 containerd 和 runc 之间的“传话员”;它卡住,通常不是 shim 本身坏了,而是它在等 runc 返回、或等容器进程退出、或等 cgroup 状态就绪。最直接的判断方式是看它的子进程和状态:
- 用
ps -o pid,ppid,comm,state -C containerd-shim找出卡住的 shim PID - 用
ls -l /proc/<shim-pid>/fd/</shim-pid>看它是否还持着initctl或console-socket的 fd(常见于容器没正确 detach) - 用
strace -p <shim-pid></shim-pid>观察最后阻塞在哪个系统调用(通常是wait4、epoll_wait或recvfrom) - 如果
shim持有runc的 socket 连接但runc已退出,说明 runc 崩溃后没清理 socket,需手动 kill 并清理/run/containerd/io.containerd.runtime.v2.task/<namespace>/<id>/</id></namespace>下残留目录
runc exec 进入容器失败,报 “no such process” 或 “container not running”
这不是权限或路径问题,而是 runc 查找容器状态时依赖 state.json 文件,而这个文件由 shim 维护——如果 shim 挂了但容器进程还在,runc exec 就会找不到合法状态。关键点在于:runc 不直接跟内核打交道,它只读 /run/containerd/io.containerd.runtime.v2.task/<ns>/<id>/state.json</id></ns>,并据此生成 exec.fifo 和绑定到容器 init 进程的 stdin/stdout/stderr。
- 先确认容器是否真在跑:
ps -eo pid,ppid,comm,args | grep <container-pid></container-pid> - 检查
state.json是否存在且"status": "running";若文件损坏或 status 是created,说明 shim 没完成启动流程 -
runc exec必须指定--pid-file或通过--root指向正确的 runc root(默认/var/run/runc),否则会去错地方找 state - 不要用
runc --root /run/containerd/runc/<ns> exec ...</ns>直接操作——containerd v2 runtime 要求 runc 使用自己的 bundle layout,路径必须匹配 shim 创建时的bundle字段
调试 OCI runtime 时,如何让 runc 输出详细日志?
runc 默认静默,但它的日志开关不在命令行参数里,而在环境变量和配置中。真正生效的是 RUNC_LOG + RUNC_LOG_LEVEL,且仅当 runc 编译时启用了 debug 支持(主流发行版包通常关闭了)。
- 临时启用:运行前加
RUNC_LOG=/tmp/runc.log RUNC_LOG_LEVEL=debug runc --debug run -b <bundle><id></id></bundle> -
--debug参数必须显式带上,否则RUNC_LOG被忽略 - 注意日志路径需 shim 进程有写权限(比如 containerd 启动 shim 时用的是
containerd用户,不是 root) - 如果看到
failed to load OCI config: invalid character,大概率是config.json里多了注释或用了 tab 缩进——OCI spec 严格要求纯 JSON,不能有注释、尾逗号、tab
containerd 重启后容器消失,但 runc list 还能看到?
这是典型的生命周期管理错位:runc list 只扫 /var/run/runc 下的 state 文件,而 containerd v2 runtime 把容器状态存在 /run/containerd/io.containerd.runtime.v2.task/<ns>/<id>/state.json</id></ns>,两者完全不互通。runc 看到的是“孤儿容器”,containerd 认为它们已丢失。
- 不要直接
runc delete这些容器——可能残留 cgroup、网络命名空间、mount ns,导致后续创建失败 - 正确做法是先用
containerd-stress cleanup(如安装了 containerd-devel 包),或手动清理:find /run/containerd/io.containerd.runtime.v2.task -name state.json -exec dirname {} \; | xargs -r rm -rf - 更稳妥的是启用 containerd 的
oom_score_adj和reclaimable配置,避免因 OOM 导致 shim 异常退出却不通知 containerd - 所有生产环境务必开启
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]中的SystemdCgroup = true,否则 cgroup v2 下 runc 和 containerd 对 cgroup 路径解析不一致,重启后状态无法对齐
OCI runtime 调试真正的难点不在命令怎么敲,而在于每个组件只认自己那一小段契约:shim 信 state.json,runc 信 bundle 目录结构,containerd 信 shim 的 gRPC 响应。一旦其中一环状态没同步(比如 shim panic 但没删 state.json),整个链就断成两截,得靠交叉验证才能定位真实断点。










