本节摘要:调试工具是故障排查的第一层武器。本节把命令分成三组:compose 内置的 config、ps、top、exec、run 负责项目层面的状态与校验;docker 底层的 inspect、logs、stats、events 负责容器细节;网络层面的 ping、DNS 查询、netstat、tcpdump 负责连通性判断。掌握"什么症状用什么命令"的对应关系,再走一遍 web 连不上数据库的完整案例,你就能把抽象报错翻译成具体动作。
凌晨两点收到"web 挂了"的消息,你的第一反应是什么?如果答案是 docker compose restart,那这一节就是为你准备的。restart 只能解决进程崩溃,解决不了配置错误、依赖没就绪、网络不通这类真正的问题。正确做法是把手里的命令按三层组织好,像工具箱一样分类摆放:
第一层,compose 内置命令,回答"项目整体怎么样"。config 校验语法,ps 看状态,top 看进程,exec 进容器,run 跑一次性任务,events 看生命周期事件。
第二层,docker 底层命令,回答"单个容器细节怎么样"。inspect 看 JSON 级配置,logs 看输出,stats 看资源,events 看事件流。
第三层,网络命令,回答"容器之间通不通"。ping 服务名测连通,getent 测 DNS 解析,netstat 看监听端口,tcpdump 抓包。
| 层 | 命令 | 回答的问题 | 典型场景 |
|---|---|---|---|
| compose 内置 | docker compose config | 语法和合并结果是否正确 | 修改文件后第一步 |
| compose 内置 | docker compose ps / top | 哪些容器在跑、进程如何 | 判断整体状态 |
| compose 内置 | docker compose exec / run | 容器内部实际状态 | 进容器查证 |
| docker 底层 | docker inspect | 挂载、网络、环境变量的真实值 | 配置与预期不符 |
| docker 底层 | docker logs / stats | 输出内容与资源占用 | 找报错、看瓶颈 |
| docker 底层 | docker events | 容器从创建到删除发生了什么 | 容器莫名消失 |
| 网络 | ping / tcpdump | 连通性与数据包路径 | 容器间不通 |
一个细节先说明:老版本命令写作 docker-compose,新版 Docker 写作 docker compose,两者参数完全一致,只是连字符变成了空格。本书示例沿用 docker-compose 写法,你按自己环境里的版本选择即可。
先给一份可以跟着实验的 compose 文件,后面所有命令都围绕它展开:
services: web: image: nginx:1.27-alpine ports: - "8080:80" depends_on: db: condition: service_started networks: - appnet db: image: mysql:8.4 environment: MYSQL_ROOT_PASSWORD: example volumes: - dbdata:/var/lib/mysql networks: - appnet volumes: dbdata: networks: appnet:
关键行解读:depends_on 配了 condition: service_started,只保证 db 容器被创建并启动,不保证 MySQL 已经就绪,后者要靠健康检查,这是第 5.2 节的重点;networks 把两个服务放进同一个自定义网络 appnet,容器间用服务名互访的前提就是这个;具名卷 dbdata 把数据库文件挂出容器,容器重建后数据还在。
六个内置命令逐个说:
run 和 exec 的选择有一条经验:要改现有容器的现场,用 exec;要在一个干净环境里重放某个命令,用 run。比如验证数据库迁移脚本,run 一个临时容器执行迁移命令,末尾加 --rm 参数让容器退出后自动清理,不留下任何痕迹。
compose 命令管项目,docker 命令管单个容器。两者配合使用:
容器间通信有两个默认前提:在同一个网络里、用服务名当主机名。网络排查按从粗到细的顺序来:
⚠️ 两个网络坑提醒:其一,ping 通不等于业务通——ICMP 走的是网络层,业务走的是 TCP 端口,端口没监听照样报连接被拒;其二,部分精简镜像根本没装 ping,ping 不通先确认镜像里有没有这个命令,别急着下结论。
docker-compose exec web bash 进入容器后,按固定顺序检查五件事,避免瞎转:
两个 exec 的细节值得记住。第一,exec 只对运行中的容器有效,容器已经退出时命令会报错,这时应该看 docker logs 找退出原因,而不是硬 exec。第二,exec 启动的进程不经过镜像的 entrypoint,entrypoint 里临时导出的环境变量在 exec 的 shell 里看不到,别据此误判变量没配置。
镜像选型上,busybox 体积不到 2MB 却自带 ping、wget、netstat,是理想的临时调试镜像;alpine/git 则适合需要 git 命令的场景。可以临时把调试镜像拉进项目网络做连通测试,测完就删,不污染生产容器。
💡 代码级调试同样可行:容器里可以装 gdb 调试 C 和 C++ 程序,用 pdb 调试 Python 程序。不过容器里装调试器又重又麻烦,我更倾向先加日志缩小范围,日志定位不了的疑难杂症再上调试器。
把前面所有工具串起来走一遍。场景:项目启动后 web 报数据库连接失败,报错是 Connection refused。
这个案例的要点不是命令本身,而是顺序:先确认状态,再进容器,先测网络层,再查应用层。每一步都把嫌疑范围缩小一半,七步之内必然收口。补充一个实战细节:web 容器里如果装了 curl,最后一步可以直接 curl 「相关地址请参见官方文档」 验证端口通不通,比 ping 更贴近业务的真实路径。
把前面所有工具对应到四类最常见的故障现象,形成一张速查表,遇到问题直接查:
| 现象 | 优先检查 | 对应工具 |
|---|---|---|
| 容器无法启动 | compose 配置、镜像名、宿主机资源、Docker 守护进程 | config、ps、logs |
| 容器之间无法通信 | 是否同网络、防火墙、连通性 | exec 加 ping、traceroute |
| 应用访问失败 | 端口映射、应用监听端口、防火墙 | ps、exec 加 netstat |
| 数据卷挂载失败 | 卷配置、宿主机目录、容器内挂载点 | inspect 的 Mounts 段 |
容器无法启动时,四个方向按成本从低到高查:先 docker-compose config 确认配置能解析,再 docker-compose logs 看启动输出里的报错,然后确认宿主机 CPU、内存、磁盘余量够不够,最后确认 Docker 守护进程本身在运行——Linux 上用 systemctl status docker,Windows 上检查 Docker Desktop 的状态。容器之间无法通信,先确认两个服务都在同一个 networks 段里,再进容器用 ping 和 traceroute 逐跳定位断点。应用访问失败,先确认端口映射正确,再进容器确认应用监听的就是映射的那个端口,最后查宿主机防火墙。数据卷挂载失败,docker inspect 看 Mounts 段的源路径、目标路径和读写标志,比对 compose 里的声明就能发现问题。
工具认全了,接下来就要面对报错本身。5.2 节我们把最常见的九类错误逐一拆开,每类都给真实报错和修复步骤。