12进阶:出事了怎么查
第 12 关 出事了怎么查
本关的产出:拿到一个起不来、退得快、或者连不上的容器,能在十分钟内定位到出错的那一层。
12.1 先看它是死是活
第一眼看两样:
STATUS:Up 3 seconds(在跑)、Exited (1) 2 minutes ago(退了)、Restarting(一直在重启)。- 退了的,看括号里那个数——退出码。
退出码怎么读:
| 码 | 意思 |
|---|---|
0 | 正常结束(一次性的活儿干完了) |
1 | 程序自己报错退了 |
125 | docker run 本身就没跑起来(参数错) |
126 | 要跑的命令不可执行 |
127 | 要跑的命令找不到 |
137 | 被 SIGKILL 打死(128+9)——常见于内存超限,或 stop 等超时后被强杀 |
143 | 被 SIGTERM 打倒(128+15) |
137、143 是「信号编号 + 128」——第 8 关讲的信号,在这儿成了读数。
12.2 退了?先看日志
127:镜像里根本没有这个命令——CMD/ENTRYPOINT写错了,或者你覆盖的那个命令不存在。1:程序自己的报错,日志里通常写着。- 日志是空的:要么它还没来得及输出就死了,要么它把日志写进了自己的文件、没往 stdout 写(第 11 关)。
12.3 docker inspect:字段怎么读
现在轮到第 2 关那个「输出很长、没有解释」的东西。它打出来是一整段 JSON,不用全看,记住几个地方就够。
先看 State:
| 字段 | 看什么 |
|---|---|
Status | 对应 docker ps 的 STATUS |
ExitCode | 就是 12.1 那张表 |
Error | 启动失败的原因,往往比日志直接 |
OOMKilled | 是不是内存超限被杀的(对得上 137) |
再看 Config,它是「这条容器最终拿到的配置」:
| 字段 | 是什么 | 查什么 |
|---|---|---|
Config.Cmd | 最终跑的启动命令 | 是不是你想跑的(被镜像名后面的参数顶掉了吗) |
Config.Entrypoint | 入口程序 | 有没有多套一层壳(第 8 关) |
Config.Env | 环境变量 | 你 -e 给的值进去了吗 |
Config.ExposedPorts | 声明的端口 | 服务该在哪个端口 |
Mounts | 挂载 | 目录真的挂上了吗、挂的是哪儿 |
一个很好用的观察:传 <容器> 进去时,Config.Env 是镜像默认值和你 -e 的值合并后的结果——一眼看得出这条容器最终拿到了什么,不用猜。
只看一项,用 --format,别在一大段 JSON 里翻:
Cmd、Entrypoint 的含义第 3 关和第 8 关讲过,这里不重讲——本关只讲拿它们查什么。
12.4 连不上:先分三层
「A 连不上 B」不是一个问题,是三个。按顺序查,每层都有证据:
- 名字没解析——
A根本不知道B是谁。docker exec A getent hosts B:解析不出来,就是这层。docker exec A cat /etc/resolv.conf:是不是127.0.0.11(第 5 关)。- 两个容器在不在同一张网:
docker inspect A的NetworkSettings.Networks。
- 解析到了,但网络不通——包发不出去。
- 拿上一步的 IP 直接连:连不上,就是这层。
- 常见原因:一开始就没接上,或者接上之后又被
docker network disconnect摘了(命令见第 5 关 5.3)。
- 网络通了,端口没人应答——包到了,门没人开。
- 要么服务没起,要么没就绪(第 7 关那个坑),要么端口写错了。
第 2 层和第 3 层,看报错就能分:
Connection refused→ 到了,门没人开(第 3 层)。Connection timed out→ 包根本没到(第 2 层)。
这跟第 1 关那条「客户端连不上服务端」和「服务端上没东西」是同一个思路——先确定断在哪一段,再往下查。
别背具体地址。 解析出的 IP、resolv.conf 里的值,会随环境和 Docker 版本变(第 5 关也说过)。排查时只看 「这个地址是不是我要的那个」。
12.5 两个现成的例子
例一:起不来——端口被占用
报 bind: address already in use。谁占了?在宿主机上跑(不是进容器里):
结果随平台不同:
- macOS:往往是
com.docker.backend——另一个容器已经把这个端口发布出去了(第 5 关)。 - Linux:通常是
docker-proxy,也可能是任意一个在用 8080 的进程。 - Windows:用
netstat -ano | findstr :8080(最后一列是 PID),再tasklist /FI "PID eq "看是哪个程序。
例二:连不上——mysql 还没就绪
第 2 关那个坑:mysql 第一次启动要先初始化十几秒,这期间连 3306 会被拒。不是密码错,也不是网络错。
还在初始化,就等;打印出 ready for connections,再连。要自动等,第 7 关的 healthcheck 是正解。
12.6 顺序
十分钟排查,照这个顺序走:
docker ps -a——活着还是死了,退出码多少。docker logs——它自己说了什么。docker inspect——配置对不对(命令、环境变量、挂载、端口、网络)。docker exec进去——网络三层逐层验。
跟第 8 关的分界:第 8 关讲信号为什么传不到主进程(进程语义),本关讲出了事按什么顺序查(排查动作)。两关都会碰到「容器莫名其妙退出」,但一个解释原因,一个定位位置。
这一关的产出:一套从「死没死」到「哪一层」的排查顺序。下一关换到安全——同样的容器,怎么让它即使出错,也伤不到外面。
本关命令速查
容器状态与排查,见 命令速查表 · 容器生命周期、命令速查表 · 镜像。