客户端命令卡死但 dockerd 进程存活,大概率是 goroutine 级死锁;需通过 kill -0、curl 测试 api、查线程数、sigusr1 堆栈 dump、journalctl 日志分析及 ebpf 追踪定位阻塞点。
客户端命令(如 docker ps、docker info)卡死,但 ps aux | grep dockerd 显示进程仍在,大概率是守护进程发生了 goroutine 级死锁,而非崩溃。这类“假死”需绕过表层状态,直接抓取运行时线索。
确认是否为守护进程假死
先排除误判:
- 执行
sudo kill -0 $(pgrep dockerd):无报错说明进程存活 - 用
timeout 3s curl -f http://unix:///var/run/docker.sock/v1.43/info >/dev/null 2>&1测试 API 响应;超时即失联 - 查线程数:
ps -o pid,tid,nlwp,comm -T $(pgrep dockerd) | tail -n +2 | wc -l;若远超 400,说明大量 goroutine 阻塞
抓取 goroutine 堆栈定位阻塞点
Docker 是 Go 程序,支持通过 SIGUSR1 触发堆栈 dump:
- 向主 dockerd 进程发信号:
sudo kill -USR1 $(pgrep -f "dockerd.*--config-file" | head -n1) - 立即查日志:
sudo journalctl -u docker.service --no-pager -n 200 | grep -A 50 "goroutine [0-9]* \[" - 重点关注状态为
semacquire、sync.(*Mutex).Lock或runtime.gopark的 goroutine - 特别留意调用链中含
runc.create、cgroup.v2.setup、nsproxy.init的阻塞项
用 eBPF 补全锁竞争上下文
仅看堆栈常难判断谁持锁、谁等锁,需内核级追踪:
- 监控
cgroup_mutex持有者:bpftrace -e 'kprobe:cgroup_mutex_lock { printf("lock held by %s\n", comm); }' - 追踪 shim socket 写延迟:
sudo ./trace -p $(pgrep containerd-shim) write(需提前部署 trace 工具) - 若发现某 goroutine 长时间停在
writev或sendto,且对端是containerd-shim,说明 shim 僵死,需 inspect 其状态
聚焦高频创建场景的典型诱因
死锁多发于 CI/CD、边缘设备等高频启停环境:
-
cgroup_mutex争抢:常见于 cgroup v2 初始化阶段,尤其容器并发创建时 -
nsproxy_mutex竞争:namespace 分配路径中同步原语未及时释放 - shim socket 写锁拖累:containerd-shim 进程响应慢或卡住,导致 dockerd 主 goroutine 在 send 阶段阻塞
- 临时缓解:禁用 BuildKit(
"features": {"buildkit": false})、降频创建节奏、避免短生命周期容器密集调度











