Docker 完全指南 / 17 - 故障排查
17 - 故障排查
掌握 Docker 常见问题的排查方法、调试技巧与系统化的故障定位流程。
17.1 排查流程总览
Docker 故障排查流程:
1. 收集信息
├── docker ps -a 容器状态
├── docker logs 容器日志
├── docker inspect 容器配置
└── docker events 系统事件
2. 分析问题类别
├── 容器启动失败
├── 容器运行异常
├── 网络连接问题
├── 存储/卷问题
├── 镜像问题
└── Docker daemon 问题
3. 定位根因
├── 查看退出码
├── 分析日志
├── 检查资源限制
└── 验证配置
4. 修复并验证
├── 应用修复
├── 重启服务
└── 监控确认
17.2 容器启动失败
问题:容器立即退出
# 查看容器状态
docker ps -a
# CONTAINER ID IMAGE STATUS NAMES
# a1b2c3d4e5f6 nginx:alpine Exited (0) 5 seconds ago my-nginx
# 查看退出码
docker inspect --format '{{.State.ExitCode}}' my-nginx
# 0 = 正常退出
# 1 = 应用错误
# 137 = SIGKILL (OOM 或 docker kill)
# 139 = SIGSEGV (段错误)
# 143 = SIGTERM (正常停止)
| 退出码 | 含义 | 常见原因 |
|---|---|---|
| 0 | 正常退出 | 命令执行完毕,前台进程退出 |
| 1 | 应用错误 | 代码异常、配置错误 |
| 126 | 权限不足 | 文件不可执行 |
| 127 | 命令未找到 | CMD/ENTRYPOINT 中命令拼写错误 |
| 137 | SIGKILL | OOM Killer 或 docker kill |
| 139 | SIGSEGV | 段错误、内存越界 |
| 143 | SIGTERM | 正常停止信号 |
原因一:CMD/ENTRYPOINT 错误
# 查看容器日志
docker logs my-nginx
# 测试:覆盖命令运行
docker run -it nginx:alpine /bin/sh
# 如果能进入 shell,说明原始 CMD 有问题
# ❌ 错误:shell 格式可能导致问题
CMD python app.py # 通过 /bin/sh -c 执行,PID 1 是 sh
# ✅ 正确:exec 格式
CMD ["python", "app.py"] # 直接执行,PID 1 是 python
原因二:OOM(内存溢出)
# 检查是否被 OOM Killer 终止
docker inspect --format '{{.State.OOMKilled}}' my-container
# 查看系统日志中的 OOM 记录
dmesg | grep -i "oom\|killed"
# 查看内存限制
docker inspect --format '{{.HostConfig.Memory}}' my-container
# 增加内存限制
docker run -m 1g my-app:latest
原因三:权限问题
# 查看容器内文件权限
docker run --rm my-app:latest ls -la /app/
# 以 root 用户运行调试
docker run -it --user root my-app:latest /bin/sh
# 检查 entrypoint 脚本权限
docker run --rm my-app:latest ls -la /entrypoint.sh
17.3 容器运行异常
问题:应用无法连接数据库
# 1. 检查网络连接
docker exec my-app ping db
docker exec my-app nc -zv db 5432
docker exec my-app curl http://db:5432
# 2. 检查环境变量
docker exec my-app env | grep DB
# 3. 检查 DNS 解析
docker exec my-app nslookup db
docker exec my-app cat /etc/resolv.conf
# 4. 确认服务在同一网络
docker network inspect my-net
docker inspect --format '{{json .NetworkSettings.Networks}}' my-app
# 5. 检查数据库服务是否健康
docker inspect --format '{{.State.Health.Status}}' db
问题:端口无法访问
# 1. 检查端口映射
docker port my-container
# 2. 检查容器是否监听端口
docker exec my-container netstat -tlnp
docker exec my-container ss -tlnp
# 3. 检查防火墙
sudo iptables -L -n | grep 8080
sudo ufw status
# 4. 从宿主机测试
curl http://localhost:8080
telnet localhost 8080
# 5. 检查 Docker 网络
docker network ls
docker network inspect bridge
# 6. 查看 iptables NAT 规则
sudo iptables -t nat -L -n | grep 8080
问题:容器性能差
# 1. 查看资源使用
docker stats my-container --no-stream
# 2. 检查资源限制
docker inspect --format 'CPU: {{.HostConfig.NanoCpus}} Memory: {{.HostConfig.Memory}}' my-container
# 3. 在容器内查看系统负载
docker exec my-container top -bn1
docker exec my-container cat /proc/loadavg
# 4. 检查 I/O
docker exec my-container cat /proc/diskstats
# 5. 检查网络延迟
docker exec my-container ping -c 5 google.com
17.4 网络问题
问题:容器间无法通信
# 1. 确认容器在同一网络
docker inspect --format '{{json .NetworkSettings.Networks}}' container1
docker inspect --format '{{json .NetworkSettings.Networks}}' container2
# 2. 检查容器 IP
docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container1
# 3. 测试连通性
docker exec container1 ping container2
docker exec container1 ping <container2-ip>
# 4. 检查 DNS
docker exec container1 nslookup container2
# 5. 检查端口是否开放
docker exec container1 nc -zv container2 80
# 6. 将容器连接到同一网络
docker network connect my-net container1
docker network connect my-net container2
问题:无法访问外部网络
# 1. 检查容器 DNS
docker exec my-container cat /etc/resolv.conf
docker exec my-container nslookup google.com
# 2. 测试网络连通性
docker exec my-container ping -c 3 8.8.8.8
# 3. 检查宿主机网络
ping google.com
# 4. 检查 Docker 网络配置
docker network inspect bridge | grep -i gateway
# 5. 重启 Docker 网络
docker network disconnect bridge my-container
docker network connect bridge my-container
网络调试工具
# 在容器内安装网络工具
docker exec -it my-container sh
# Alpine:
apk add --no-cache curl bind-tools netcat-openbsd tcpdump
# Debian/Ubuntu:
apt-get update && apt-get install -y curl dnsutils netcat-openbsd tcpdump
# 抓包分析
docker exec my-container tcpdump -i eth0 -c 100 -w /tmp/capture.pcap
docker cp my-container:/tmp/capture.pcap ./capture.pcap
# 使用 nsenter 在宿主机上调试容器网络
PID=$(docker inspect --format '{{.State.Pid}}' my-container)
sudo nsenter -t $PID -n ip addr
sudo nsenter -t $PID -n tcpdump -i eth0
17.5 存储与卷问题
问题:数据未持久化
# 1. 检查卷挂载
docker inspect --format '{{json .Mounts}}' my-container | jq .
# 2. 确认使用的是 volume 而非容器层
docker volume ls
docker volume inspect my-data
# 3. 检查容器内挂载点
docker exec my-container df -h
docker exec my-container mount | grep data
# 4. 检查权限
docker exec my-container ls -la /data/
问题:卷权限错误
# 查看容器运行用户
docker inspect --format '{{.Config.User}}' my-container
# 以 root 进入修改权限
docker exec -it --user root my-container chown -R appuser:appgroup /data
# 在 Dockerfile 中预设权限
# COPY --chown=appuser:appgroup . /app/
问题:磁盘空间不足
# 查看 Docker 磁盘使用
docker system df
docker system df -v
# 清理无用资源
docker system prune # 清理停止的容器、悬空镜像、未使用的网络
docker system prune -a # 包括所有未使用的镜像
docker system prune --volumes # 包括未使用的卷
docker system prune -a --volumes # 全部清理(谨慎!)
# 单独清理
docker container prune # 清理停止的容器
docker image prune -a # 清理未使用的镜像
docker volume prune # 清理未使用的卷
docker builder prune # 清理构建缓存
# 查看详细磁盘占用
du -sh /var/lib/docker/*
du -sh /var/lib/docker/overlay2/*
17.6 Docker daemon 问题
问题:Docker 服务无法启动
# 1. 查看 Docker 服务状态
sudo systemctl status docker
# 2. 查看详细日志
sudo journalctl -u docker --since "1 hour ago"
sudo journalctl -u docker -f
# 3. 检查配置文件语法
sudo dockerd --validate
# 4. 检查 daemon.json
cat /etc/docker/daemon.json | python3 -m json.tool
# 5. 检查磁盘空间
df -h /var/lib/docker
# 6. 检查内核版本
uname -r
# 7. 重启 Docker
sudo systemctl restart docker
问题:daemon.json 配置错误
# 验证 JSON 语法
python3 -c "import json; json.load(open('/etc/docker/daemon.json'))"
# 临时忽略配置启动
sudo dockerd --config-file="" &
# 查看默认配置
docker info
问题:容器无法删除
# 强制删除容器
docker rm -f <container>
# 如果仍然无法删除,停止 Docker 进程后手动清理
sudo systemctl stop docker
sudo rm /var/lib/docker/containers/<container-id>/*
sudo systemctl start docker
17.7 镜像问题
问题:镜像拉取失败
# 1. 检查网络连接
ping registry-1.docker.io
# 2. 检查镜像源配置
docker info | grep "Registry Mirrors"
# 3. 尝试手动拉取
docker pull nginx:alpine
# 4. 检查认证
docker login
cat ~/.docker/config.json
# 5. 使用 digest 精确拉取
docker pull nginx@sha256:abc123...
# 6. 检查代理设置
env | grep -i proxy
问题:构建缓存不生效
# 1. 检查 COPY 文件是否变化
docker build --no-cache -t my-app . # 对比有缓存和无缓存的结果
# 2. 查看构建过程
docker build --progress=plain -t my-app .
# 3. 检查 .dockerignore
cat .dockerignore
# 4. 确认 BuildKit 启用
DOCKER_BUILDKIT=1 docker build -t my-app .
17.8 实用调试命令速查
信息收集
# Docker 系统信息
docker version
docker info
docker system df
# 容器信息
docker ps -a
docker logs --tail 100 <container>
docker inspect <container>
docker stats --no-stream
# 网络信息
docker network ls
docker network inspect <network>
# 卷信息
docker volume ls
docker volume inspect <volume>
# 系统事件
docker events --since 1h
docker events --filter "type=container"
容器内调试
# 进入容器
docker exec -it <container> /bin/sh
# 以 root 身份进入
docker exec -it -u root <container> /bin/sh
# 查看进程
docker exec <container> ps aux
# 查看网络
docker exec <container> ip addr
docker exec <container> cat /etc/resolv.conf
# 查看磁盘
docker exec <container> df -h
docker exec <container> du -sh /*
# 查看日志
docker exec <container> tail -f /var/log/app.log
宿主机调试
# 查看容器进程
ps aux | grep docker
# 查看容器 cgroup
cat /sys/fs/cgroup/docker/<container-id>/memory.max
# 查看网络规则
sudo iptables -L -n -t nat
# 查看 Docker 存储
sudo ls -la /var/lib/docker/
sudo ls -la /var/lib/docker/containers/<container-id>/
# 实时监控系统
sudo atop
sudo iostat -x 1
sudo sar -n DEV 1
17.9 常见错误速查表
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
port is already allocated | 端口被占用 | 更换端口或停止占用的进程 |
no space left on device | 磁盘满 | docker system prune 清理 |
permission denied | 权限不足 | 检查文件权限或使用 --user root |
image not found | 镜像不存在 | 检查镜像名称和标签 |
network not found | 网络不存在 | 创建网络或检查网络名称 |
container name already in use | 容器名冲突 | 删除旧容器或更换名称 |
OOMKilled | 内存不足 | 增加 --memory 限制 |
exec format error | 架构不匹配 | 使用多架构镜像或 --platform |
manifest unknown | 标签不存在 | 检查可用标签 |
TLS handshake timeout | 网络超时 | 配置镜像源或检查网络 |
driver failed programming external connectivity | iptables 错误 | 重启 Docker: systemctl restart docker |
cannot start container | 启动失败 | 查看 docker logs 和退出码 |
17.10 一键诊断脚本
#!/bin/bash
# docker-diagnose.sh — Docker 诊断脚本
echo "=== Docker 版本 ==="
docker version 2>&1
echo -e "\n=== Docker 系统信息 ==="
docker info 2>&1
echo -e "\n=== 磁盘使用 ==="
docker system df
echo -e "\n=== 容器状态 ==="
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
echo -e "\n=== 容器资源使用 ==="
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
echo -e "\n=== 网络列表 ==="
docker network ls
echo -e "\n=== 卷列表 ==="
docker volume ls
echo -e "\n=== Docker daemon 日志(最近 50 行)==="
sudo journalctl -u docker --no-pager -n 50
echo -e "\n=== 内核版本 ==="
uname -r
echo -e "\n=== 宿主机磁盘空间 ==="
df -h /var/lib/docker
echo -e "\n=== 宿主机内存 ==="
free -h
echo -e "\n诊断完成。"
chmod +x docker-diagnose.sh
./docker-diagnose.sh
要点回顾
| 要点 | 核心内容 |
|---|---|
| 退出码 | 0=正常, 137=OOM/kill, 143=SIGTERM |
| 日志优先 | docker logs 是排查问题的第一步 |
| 网络调试 | 检查 DNS、端口、网络连通性 |
| 磁盘管理 | 定期 docker system prune 清理 |
| 一键诊断 | 脚本化收集系统信息,加速排查 |
注意事项
不要盲目重启: 先收集日志和诊断信息,再决定是否重启。重启可能丢失现场信息。
保留现场: 在删除容器前,先用
docker commit保存当前状态用于事后分析。
监控预防: 部署监控系统(Prometheus + Grafana),在问题发生前收到告警。
下一步
→ 18 - 最佳实践:总结 Docker 生产环境最佳实践。