Docker 故障排查实战:OOM 分析、网络诊断、存储问题与容器调试技巧
系统讲解 Docker 生产环境的故障排查方法论:容器 OOM Kill 的原因定位与解决(内存泄漏 vs 配置不足)、容器网络不通的五步诊断法(DNS/路由/iptables/防火墙)、存储相关故障(磁盘满/overlay2 问题/volume 权限)、进入崩溃容器调试的方法、docker-proxy 高 CPU 问题,以及常见错误信息的含义与解决方案速查。
生产中遇到 Docker 问题,能快速定位比什么都重要。本文总结最常见的故障场景,给出系统化的排查步骤,而不是“碰运气”式的尝试。
故障排查基本思路
遇到问题时,先回答三个问题:
1. 现象是什么?
容器启动失败?进程崩溃?服务无响应?响应慢?
2. 什么时候开始的?
一直都有?部署后新出现?定时触发?
3. 是否有变更?
代码变更?配置变更?镜像更新?宿主机变化?
排查顺序:
docker ps → 容器状态
docker logs → 应用日志
docker inspect → 配置详情
docker stats → 资源使用
docker exec → 进入容器检查
宿主机工具 → dmesg/journalctl/tcpdump
一、容器无法启动
1.1 镜像拉取失败
# 错误示例
# Error response from daemon: pull access denied for myapp, repository does not exist
# 排查
docker login harbor.company.com # 确认登录状态
cat ~/.docker/config.json # 查看已保存的认证信息
# 常见原因
# 1. 未登录私有仓库
docker login harbor.company.com -u admin -p Harbor12345
# 2. 镜像名/tag 错误
docker pull harbor.company.com/backend/api:v1.2.3 # 确认完整路径
# 3. 网络不通(内网仓库)
curl https://harbor.company.com/v2/ # 测试仓库可达性
# 4. 自签名证书未信任
ls /etc/docker/certs.d/harbor.company.com/ca.crt # 检查是否有证书
1.2 端口冲突
# 错误:Bind for 0.0.0.0:80 failed: port is already allocated
# 查找占用进程
sudo lsof -i :80 # 或
sudo netstat -tlnp | grep :80
sudo ss -tlnp | grep :80
# 如果是另一个容器占用
docker ps | grep 0.0.0.0:80
# 解决:换端口或停止占用者
docker stop nginx-old
docker run -d -p 80:80 nginx
1.3 入口点/命令错误
# 错误:standard_init_linux.go: exec user process caused: no such file or directory
# 可能原因1:CMD/ENTRYPOINT 路径错误
docker run --rm --entrypoint ls myapp /app # 检查 /app 目录内容
# 可能原因2:Windows 行尾(\r\n)污染脚本
file entrypoint.sh
# entrypoint.sh: Bourne-Again shell script, ASCII text, with CRLF line terminators ← 问题!
dos2unix entrypoint.sh
docker build --no-cache -t myapp . # 重新构建
# 可能原因3:多平台镜像,架构不对
docker inspect myapp --format '{{.Architecture}}'
uname -m # 宿主机架构
二、OOM Kill 分析
2.1 确认是否 OOM Kill
# 方法1:docker inspect
docker inspect myapp --format '{{.State.OOMKilled}}'
# true ← 确认是 OOM Kill
# 方法2:dmesg
dmesg | grep -E 'oom|Out of memory|killed' | tail -20
# [123456.789] Out of memory: Kill process 12345 (python3) score 900 or sacrifice child
# [123456.790] Killed process 12345 (python3) total-vm:1024000kB, anon-rss:512000kB
# 方法3:systemd journal
journalctl -k | grep -i oom | tail -20
# 方法4:Prometheus 查询
# container_oom_events_total{name="myapp"} > 0
2.2 判断 OOM 原因
# 原因A:内存限制设太低(应用正常,但 limit 不够)
docker inspect myapp --format '{{.HostConfig.Memory}}'
# 268435456 → 256MB(可能不够用)
# 查看应用实际需要多少内存
docker stats myapp --no-stream
# CONTAINER CPU% MEM USAGE/LIMIT MEM% ...
# myapp 45% 245MiB/256MiB 95.7% ← 接近 100%,说明 limit 太低
# 解决:增加内存限制
docker update --memory=512m --memory-swap=512m myapp
# 原因B:内存泄漏(内存持续增长)
# 查看内存增长趋势(Grafana 或命令行)
for i in $(seq 1 10); do
docker stats myapp --no-stream --format '{{.MemUsage}}'
sleep 30
done
# 如果每 30 秒都在增长,说明有内存泄漏
# 解决:定位泄漏(在容器内用语言特定工具)
# Python: memory_profiler, tracemalloc
# Java: jmap, jvisualvm, heap dump
# Go: pprof
# 原因C:cgroup 内存核算包含缓存
# Linux 的 container_memory_usage_bytes 包含文件缓存
# 真实内存消耗 = container_memory_rss(只含 RSS)
docker stats myapp --format '{{.MemPerc}}'
# 如果很高但进程本身不大,可能是文件缓存
三、网络不通排查(五步法)
# 场景:api 容器无法访问 db 容器
# 第一步:确认容器在同一网络
docker inspect api --format '{{json .NetworkSettings.Networks}}' | jq 'keys'
# ["app-net"]
docker inspect db --format '{{json .NetworkSettings.Networks}}' | jq 'keys'
# ["app-net"] ← 在同一网络,正常
# 第二步:测试 DNS 解析
docker exec api nslookup db
# Server: 127.0.0.11
# Address: 127.0.0.11:53
# Name: db
# Address: 172.18.0.3 ← 能解析,继续下一步
# 如果解析失败:
# docker network inspect app-net 确认 db 容器在网络中
# docker compose down && docker compose up -d 重建网络
# 第三步:测试 IP 连通性
docker exec api ping -c 3 172.18.0.3
# 如果 ping 不通,说明网络层问题
# 第四步:测试端口连通性
docker exec api nc -zv db 5432
# Connection to db 5432 port [tcp/*] succeeded! ← 端口通
# 如果不通:db 容器是否真的在监听?
docker exec db ss -tlnp | grep 5432
# 第五步:宿主机 iptables 检查
# 如果以上都正常但就是不通,查 iptables
sudo iptables -L DOCKER-USER -n -v
sudo iptables -L FORWARD -n -v | grep -E 'DROP|REJECT'
# 如果有阻断规则,临时放行测试
sudo iptables -I DOCKER-USER -j ACCEPT
外网访问不通
# 容器内 curl 不通外网
docker exec myapp curl -v https://www.baidu.com
# 排查1:DNS
docker exec myapp cat /etc/resolv.conf
# nameserver 127.0.0.11 ← Docker 内置 DNS
# 测试 DNS
docker exec myapp nslookup baidu.com
# 排查2:IP 转发
sysctl net.ipv4.ip_forward
# net.ipv4.ip_forward = 1 ← 必须是 1
# 如果是 0:
echo 1 > /proc/sys/net/ipv4/ip_forward
# 排查3:NAT 规则
sudo iptables -t nat -L POSTROUTING -n -v | grep MASQUERADE
# 必须有 MASQUERADE 规则(Docker 自动添加)
# 排查4:宿主机防火墙
# Ubuntu UFW
sudo ufw status
# CentOS/RHEL Firewalld
sudo firewall-cmd --list-all
# 排查5:代理
# 内网环境如果有代理
docker run -d \
-e http_proxy=http://proxy.company.com:8080 \
-e https_proxy=http://proxy.company.com:8080 \
-e no_proxy=harbor.company.com,localhost \
myapp
四、存储相关故障
4.1 磁盘空间不足
# 症状:docker: write /var/lib/docker/overlay2/...: no space left on device
# 查看 Docker 磁盘使用
docker system df
# TYPE TOTAL ACTIVE SIZE RECLAIMABLE
# Images 45 12 15.2GB 8.1GB (53%)
# Containers 23 5 2.3GB 2.1GB (91%)
# Local Volumes 15 5 45GB 30GB (66%)
# Build Cache 0 0 0B 0B
# 清理(分步操作,避免误删)
# 1. 清理停止的容器
docker container prune -f
# 2. 清理悬空镜像
docker image prune -f
# 3. 清理未使用的网络
docker network prune -f
# 4. 清理构建缓存
docker buildx prune -f --keep-storage=5gb
# 5. 确认 volumes(慎重!会删除数据)
docker volume ls --filter dangling=true # 先查看
docker volume prune -f # 再清理
# 全量清理(危险!删除所有未使用资源,包括停止的容器)
docker system prune -a --volumes -f
# 查找最占空间的容器日志
du -sh /var/lib/docker/containers/*/ | sort -hr | head -10
4.2 Volume 权限问题
# 症状:Permission denied 访问挂载的目录
# 查看挂载目录的实际权限
ls -la /data/myapp/
# drwxr-xr-x 2 root root 4096 ... ← owner 是 root
# 查看容器内的用户
docker exec myapp id
# uid=1000(app) gid=1000(app) ... ← 容器以 1000 用户运行
# 解决方案1:修改宿主机目录权限
sudo chown -R 1000:1000 /data/myapp/
# 或
sudo chmod 777 /data/myapp/ # 宽松但不推荐
# 解决方案2:Dockerfile 中配置
RUN mkdir -p /data && chown -R app:app /data
# 解决方案3:COPY --chown
COPY --chown=app:app . /app
4.3 overlay2 存储问题
# 症状:No space left on device,但 df -h 显示有空间
# 可能是 inode 耗尽
df -i /var/lib/docker
# Filesystem Inodes IUsed IFree IUse%
# /dev/sda1 1310720 1310720 0 100% ← inode 耗尽!
# 原因:大量小文件(如 npm node_modules、Python __pycache__)
# 解决:清理旧容器和镜像
docker system prune -a -f
# 长期方案:格式化时增加 inode 数量(需要重新挂载)
# mkfs.ext4 -T news /dev/sdb # news 模板:更多 inode
# overlay2 相关错误
# "device or resource busy" 无法删除容器
lsof | grep overlay2 | grep <container_id>
# 找到并关闭占用进程,再 docker rm -f <container>
五、进入崩溃容器调试
# 场景:容器启动后立即崩溃(无法 exec 进入)
# 方法1:查看退出日志
docker logs myapp --tail=100
# 方法2:覆盖 ENTRYPOINT 进入 shell(镜像有 shell 时)
docker run -it --entrypoint bash myapp:v1
# 或
docker run -it --entrypoint sh myapp:v1
# 方法3:用 sleep 替换入口(让容器保持运行)
docker run -d --entrypoint sleep myapp:v1 infinity
docker exec -it <container_id> bash
# 在里面手动运行原来的启动命令,观察报错
# 方法4:复制崩溃容器的文件系统(distroless 容器)
CONTAINER_ID=$(docker create myapp:v1) # 只创建不运行
docker cp ${CONTAINER_ID}:/app ./debug-app
docker rm ${CONTAINER_ID}
ls ./debug-app/ # 检查文件是否完整
# 方法5:从崩溃容器创建镜像(快照当前状态)
docker commit myapp-crashed myapp-debug:snapshot
docker run -it --entrypoint bash myapp-debug:snapshot
六、常见错误速查
错误信息 原因与解决
──────────────────────────────────────────────────────────────
cannot create container for service X: 容器名重复
Conflict. The name is already in use docker rm -f <name>
driver failed: ...: network sandbox join: MAC 地址冲突(macvlan)
failed to program external connectivity 检查 IP/MAC 配置
Error response: 403 Forbidden Harbor RBAC 无权限
(unauthorized: unauthorized to access...) 检查机器人账户权限
OCI runtime exec failed: exit status 1: exec 目标不存在
not found in PATH 用 /bin/sh 替代 bash
exec /entrypoint.sh: no such file or 文件路径错误或 CRLF 问题
directory dos2unix entrypoint.sh
ERROR: for service X: "x" invalid yaml 格式问题
character 'x' 检查 compose.yml 缩进
Cannot connect to the Docker daemon at Docker daemon 未运行
unix:///var/run/docker.sock systemctl start docker
Got permission denied while trying to 用户不在 docker 组
connect to the Docker daemon sudo usermod -aG docker $USER
failed to register layer: ...: 磁盘空间或 inode 不足
no space left on device docker system prune
小结
Docker 故障排查的核心原则:先看日志,再看状态,最后抓包。
docker logs → docker inspect → docker exec → 宿主机工具(dmesg/tcpdump/iptables),这个顺序覆盖 90% 的生产故障场景。
OOM 问题一定要区分“limit 设太低”和“内存泄漏”:前者看 docker stats 显示使用率一直高,后者看内存在持续增长。网络不通先确认同一 network、再 DNS、再端口、再 iptables,五步走不会漏掉任何层。
