6月6日 20:23

Docker 容器出问题了怎么排查?分层排查流程

容器启动失败、运行中崩溃、网络不通、数据丢失——Docker 故障排查有一套固定流程,按层级从外到内逐步缩小范围。

排查流程总览

shell
容器状态异常? ├── 容器没启动 → 查 docker logs ├── 容器运行但行为异常 → 查 docker logs + docker exec ├── 容器网络不通 → 查 docker network + 端口映射 ├── 容器数据问题 → 查 Volume 挂载 └── 宿主机资源不足 → 查 docker stats + 系统资源

第一步:确认容器状态

bash
# 查看所有容器(包括停止的) docker ps -a # 关注 STATUS 列 # Up 2 hours → 正常运行 # Exited (0) 5 mins → 正常退出 # Exited (1) 5 mins → 错误退出 # Exited (137) 1 min → 被 SIGKILL 杀掉(通常是 OOM) # Restarting → 不断重启(崩溃循环)

退出码含义:

退出码含义
0正常退出
1应用错误
137OOM Killed(内存不足)
139Segmentation Fault
143SIGTERM(正常停止)

第二步:查日志

docker logs

bash
# 查看容器日志 docker logs myapp # 实时跟踪 docker logs -f myapp # 最近 100 行 docker logs --tail 100 myapp # 带时间戳 docker logs -t myapp # 查看某个时间段的日志 docker logs --since "2024-01-01T00:00:00" --until "2024-01-01T12:00:00" myapp

容器已经退出了?

bash
# 已退出的容器仍然可以查日志 docker logs myapp # 只要容器没被 docker rm 删掉 # 如果容器被删了,日志文件还在(json-file 驱动) ls /var/lib/docker/containers/<container-id>/ # <container-id>-json.log

日志里什么都没有?

应用可能把日志写到了文件而不是 stdout/stderr。Docker 只捕获标准输出/错误流:

bash
# 进入容器查看日志文件 docker exec -it myapp sh cat /app/logs/app.log

最佳实践:让应用把日志输出到 stdout/stderr,这样 docker logs 直接能看到。不要写到容器内的文件——容器删除后日志也丢了。

第三步:进入容器排查

bash
# 进入容器执行命令 docker exec -it myapp sh # 如果容器没有 sh,用 bash docker exec -it myapp bash # 如果都没有(alpine 镜像可能只有 sh) docker exec -it myapp /bin/sh # 不进入容器,直接执行命令 docker exec myapp ps aux docker exec myapp cat /etc/config.yml

容器里能做的事:

  • ps aux 看进程
  • top 看 CPU/内存
  • cat /etc/hosts 看网络配置
  • curl localhost:3000 测试内部端口
  • env 看环境变量

容器一启动就退出了怎么办

docker exec 需要容器在运行状态。容器一启动就退出的情况,用覆盖入口点的方式:

bash
# 覆盖 CMD,保持容器运行 docker run -it --entrypoint sh myapp:latest # 或者在 Dockerfile 末尾临时加 # CMD ["sleep", "3600"]

进入容器后手动执行原来的启动命令,观察报错。

第四步:排查网络问题

容器端口没暴露

bash
# 检查端口映射 docker port myapp # 3000/tcp -> 0.0.0.0:3000 # 如果没有输出,说明没做端口映射 # 重新启动时加 -p docker run -d -p 3000:3000 myapp

容器间网络不通

bash
# 查看容器的网络 docker inspect myapp --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' # 查看所有 Docker 网络 docker network ls # 同一网络的容器可以互相通过容器名访问 # 不同网络的容器互相隔离
bash
# 测试容器间连通性 docker exec myapp ping redis docker exec myapp curl http://api-service:8080/health

DNS 解析失败

bash
# 查看容器的 DNS 配置 docker exec myapp cat /etc/resolv.conf # 自定义 DNS docker run --dns 8.8.8.8 myapp

第五步:排查 Volume 问题

文件没有出现在容器内

bash
# 检查 Volume 挂载 docker inspect myapp --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{println}}{{end}}' # /host/path -> /container/path

常见原因:

  • 宿主机路径写错了(相对路径问题)
  • 文件权限不匹配(容器内用户没读权限)
  • SELinux 阻止访问(加 :z:Z 后缀)
bash
# SELinux 环境 docker run -v /host/path:/container/path:z myapp # 多容器共享 docker run -v /host/path:/container/path:Z myapp # 单容器专用

容器内修改没有反映到宿主机

检查是不是挂载方向反了,或者挂载的是文件而不是目录:

bash
# 错误:挂载不存在的文件,Docker 会创建一个目录 docker run -v ./config.yml:/app/config.yml myapp # 如果 ./config.yml 不存在,Docker 会创建 /app/config.yml 目录 # 正确:确保文件先存在 touch ./config.yml docker run -v ./config.yml:/app/config.yml myapp

常见故障速查

症状排查命令常见原因
容器启动就退出docker logsCMD 执行失败、配置错误
容器被杀docker inspect --format '{{.State.OOMKilled}}'内存不足
端口访问不到docker port + curl没映射端口、防火墙
容器间不通docker exec ping不在同一网络
磁盘满df -h + docker system df日志太多、镜像太多
构建慢docker build --progress=plain没用缓存、上下文太大
权限错误docker exec ls -laUID 不匹配、SELinux
容器不断重启docker logs --tail 50启动脚本报错、依赖服务未就绪

调试技巧

用 debug 镜像替换

生产镜像可能是 distrolessalpine,没有 curlping 等工具。临时用 debug 镜像排查:

bash
# 把镜像临时换成有工具的版本 docker run -d --name myapp-debug \ --entrypoint sh \ -p 3000:3000 \ myapp:latest -c "sleep 3600"

docker diff 看文件变更

bash
# 查看容器内哪些文件被修改了 docker diff myapp # A /app/newfile.txt → Added # C /etc/config.yml → Changed # D /tmp/old.log → Deleted

docker events 实时监控

bash
# 监控 Docker 事件(容器启停、OOM、健康检查等) docker events --filter container=myapp
标签:Docker