深入解析:Docker 容器运行时故障排查
场景
凌晨 2 点,你的手机响起。某生产主机上的一个关键容器化 API 已经连续重启了 20 分钟。docker ps 显示 Restarting,docker logs 为空。服务级别目标(SLO)已经濒临失守。你需要快速、有条理地行动。
这台主机上运行着多个容器,应用团队报告说某个服务不断重启,但日志里没有任何错误信息。主机负载偏高,但尚未达到阈值。你怀疑问题出在 Docker 运行时本身,而不是应用代码。
症状
常见症状帮你快速初判:
docker ps显示容器处于Restarting状态,重启次数持续增长。docker inspect显示ExitCode: 137或OOMKilled: true,这通常代表内存溢出(OOM)。journalctl或dmesg中出现Out of memory: Kill process或oom-killer相关记录。df -h显示根分区或 Docker 数据目录(/var/lib/docker)已满。- 容器启动时报错:
failed to create shim task、runc create failed等。 - 容器能启动但数分钟后自动退出,无日志输出。
诊断
按照从外到内的顺序,逐一排查。
1. 确认 Docker 守护进程状态
systemctl status docker
journalctl -u docker --since "10 minutes ago"
检查守护进程是否因资源不足或死锁而崩溃。如果服务处于 inactive (dead) 或频繁重启,问题可能出在 Docker 本身。
2. 获取容器详细信息
docker ps -a --filter "name=your-service"
docker inspect <container-id> --format '{{.State.Status}} | ExitCode={{.State.ExitCode}} | OOMKilled={{.State.OOMKilled}}'
如果 OOMKilled 为 true,说明容器被内核 OOM killer 杀死。
3. 查看内核日志
dmesg -T | grep -i -E "oom|killed process" | tail -20
journalctl -k --since "10 minutes ago" | grep -i oom
这些命令能显示是哪个进程/容器触发了 OOM,以及当时主机的内存压力。
4. 检查容器日志和标准输出
docker logs --tail 50 <container-id>
注意:如果容器持续重启,你可能需要 docker logs --tail 50 --timestamps 来观察时间线。有时应用直接把错误写到 stderr,而日志驱动没有正确转发。
5. 检查主机磁盘和 Docker 磁盘占用
df -h
docker system df
du -sh /var/lib/docker/*
容器未清理的镜像、卷或日志文件可能占满磁盘,导致容器创建或写入失败。
6. 检查 cgroup 限制
cat /sys/fs/cgroup/memory/docker/<container-id>/memory.oom_control
cat /sys/fs/cgroup/memory/docker/<container-id>/memory.limit_in_bytes
cat /sys/fs/cgroup/pids/docker/<container-id>/pids.current
确认容器内存和 PID 限制是否设置过低。如果主机本身内存不足,即使容器限制正常也可能被杀。
7. 确认运行时组件(containerd/runc)
docker info | grep -i runtime
systemctl status containerd
runtime -v 2>/dev/null || true
runc 或 containerd 异常会导致容器启动失败。
风险控制
在动手修复之前,先降低风险:
- 避免重启守护进程:
systemctl restart docker会中断所有容器,除非你已经准备好隔离风险。 - 优先使用只读命令:
inspect、logs、dmesg不会改变状态。 - 记录现场:保存
docker inspect、dmesg、journalctl输出,便于后续分析。 - 考虑负载均衡摘除节点:如果主机上有副本,可以让 LVS/Nginx 将该节点标记为维护,然后从容排查。
回滚策略
如果问题源于最近的变更,快速回滚是首选:
- 切换镜像标签:如果当前镜像
latest被更新,回滚到上一个稳定的版本标签,如v1.0.1。 - 调整容器参数:如果是内存限制过小,启动新容器时增加
--memory和--memory-swap。 - 重新创建容器:使用
docker run替代start,保留原有挂载和网络配置。
注意:回滚操作前先确认旧版本镜像仍存在(docker images)。
验证
修复之后,验证不能只看进程是否存活:
docker ps --filter "status=running" --filter "name=your-service"
docker inspect --format '{{.State.Status}}, RestartCount={{.RestartCount}}' <container-id>
然后验证功能:
curl -f http://localhost:8080/healthz
持续观察 15 分钟,确保没有再次 OOM 或重启。使用 docker stats 监控资源:
docker stats --no-stream <container-id>
确认内存使用率稳定在限制的 60% 以下,CPU 不超过预期。
何时提交 OpsGlobal 工单
如果遇到以下情况,请不要继续在黑暗里摸索,立即提交工单给 OpsGlobal:
- 你已经按上述步骤排查,但问题在 1 小时内未解决。
- 涉及内核参数调整(如
vm.overcommit_memory)或runc/containerd的 bug。 - 主机上同时有多个容器异常,影响范围大。
- 你怀疑是硬件故障或底层驱动问题,如设备映射器异常。
OpsGlobal 的 SRE 专家可以在 15 分钟内介入,提供 7x24 小时远程支持,帮助你保护 SLO 和数据安全。
适用场景
适合正在处理 DevOps、Docker, 容器运行时, SRE, 故障排查 相关问题的团队,用于快速建立排查路径和交付标准。
问题背景
本篇文章带你走进生产环境中 Docker 容器运行时常见故障的真实场景,从症状识别、诊断命令、风险控制、回滚策略到最终验证,全面掌握 SRE 级别的排查思路。
排查步骤
先确认影响范围和最近变更,再收集日志、配置、指标和链路数据,最后按风险从低到高执行修复。
命令示例
示例命令请替换为你的真实资源名,并使用环境变量保存账号、密码、token 等敏感信息。
风险说明
生产环境操作前需要确认备份、权限边界、变更窗口和回滚路径,避免扩大故障影响。
回滚方案
保留原配置和发布版本;如修复后指标异常,立即回退配置、镜像或数据库变更并复核日志。
交付清单
问题定位记录、关键命令、修复步骤、验证结果、后续优化建议。
遇到类似技术问题?
如果你的服务器、K8s、Docker、CI/CD、数据库或监控系统出现类似问题,可以提交日志和配置文件,我们帮你远程诊断。