强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 中命令拼写错误
137SIGKILLOOM Killer 或 docker kill
139SIGSEGV段错误、内存越界
143SIGTERM正常停止信号

原因一: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 connectivityiptables 错误重启 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 生产环境最佳实践。