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 | 应用错误 |
| 137 | OOM Killed(内存不足) |
| 139 | Segmentation Fault |
| 143 | SIGTERM(正常停止) |
第二步:查日志
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 logs | CMD 执行失败、配置错误 |
| 容器被杀 | docker inspect --format '{{.State.OOMKilled}}' | 内存不足 |
| 端口访问不到 | docker port + curl | 没映射端口、防火墙 |
| 容器间不通 | docker exec ping | 不在同一网络 |
| 磁盘满 | df -h + docker system df | 日志太多、镜像太多 |
| 构建慢 | docker build --progress=plain | 没用缓存、上下文太大 |
| 权限错误 | docker exec ls -la | UID 不匹配、SELinux |
| 容器不断重启 | docker logs --tail 50 | 启动脚本报错、依赖服务未就绪 |
调试技巧
用 debug 镜像替换
生产镜像可能是 distroless 或 alpine,没有 curl、ping 等工具。临时用 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