读症状靠的是条件反射
一句话总结
故障排查无法只靠文字学会。看到 kubectl get pod 显示的状态后,必须在 3 秒内想到下一步该看哪里,而这种反应只有亲手制造故障才能形成。
为什么必须是真实故障
故障排查在 CKA 中占很大分值。但前面模块使用的模拟集群既没有 kubelet,也没有容器运行时,根本无法制造故障本身。
| 症状 | 在模拟集群中 |
|---|---|
ImagePullBackOff |
不拉取镜像,因此不会发生 |
CrashLoopBackOff |
没有进程,因此也不会退出 |
OOMKilled |
不使用内存,因此不会超限 |
| 探针失败后重启 | 不运行探针 |
PVC Bound |
没有供应器,永远保持 Pending |
因此过去只能通过文字学习“这个症状意味着什么”。但故障排查不能这样掌握。看到 kubectl get pod 的状态后,要在 3 秒内想到下一步检查位置,必须亲手复现。
看似相同、原因却不同的情况
Pending与ContainerCreating——前者是调度器问题,后者是 kubelet 问题。可通过kubectl get pod -o wide的 NODE 列是否已填写来区分。liveness失败与readiness失败——前者会重启,后者只会从流量中移除。- 通过
env引用不存在的 ConfigMap 会得到CreateContainerConfigError;通过卷引用同一对象则会是Pending。
只需记住三件事
- 先看
describe的 Events。 状态名称只说明卡在哪里,缺少什么总会写在事件中。 logs为空时使用--previous。 CrashLoopBackOff Pod 当前的容器刚创建,还没来得及输出任何内容。- 查看 NODE 列。 空白表示调度器,已填写表示 kubelet。这一列就能把调查范围缩小一半。
每种症状下一步看哪里
不要把状态名称直接连接到原因,而应连接到下一步查看位置,这样调查会快得多。对应关系如下。
| 状态 | 下一步查看位置 | 常见原因 |
|---|---|---|
Pending,NODE 为空 |
describe pod 的 Events |
requests 超过节点余量、污点、节点选择器不匹配任何节点 |
Pending,与卷相关 |
PVC 状态与 StorageClass | 没有供应器或访问模式不匹配 |
长时间 ContainerCreating |
该节点的 kubelet | 镜像过大、卷挂载未完成、缺少 Secret |
ImagePullBackOff |
Events 中拉取失败的行 | 标签拼错、缺少私有仓库凭据、网络阻断 |
CrashLoopBackOff |
logs --previous |
缺少配置导致立即退出、无法访问依赖服务 |
OOMKilled |
describe 的 Last State |
limits 低于实际使用量 |
CreateContainerConfigError |
引用的 ConfigMap、Secret | 名称拼错或对象尚未创建 |
Running 但无流量 |
端点与 readiness | selector 与标签不匹配,或 readiness 持续失败 |
最后一行尤其容易误导人。Pod 是 Running,日志也正常,却完全收不到请求。此时应查看的不是 Pod,而是Service 端点。端点为空只有两个原因:Service selector 与 Pod 标签不匹配,或 readiness 失败使 Pod 被移出列表。区分方法很简单:Pod 显示 Ready 0/1 是探针问题;显示 Ready 1/1 而端点仍为空,则是 selector 问题。
节点本身的问题也会先表现为 Pod 症状。 节点变成 NotReady 后,其上的 Pod 会暂时保持 Running,过一段时间才清理。因此,遇到“Pod 是 Running 但没有响应”时,不要只看 Pod,还应养成执行一次 kubectl get nodes 的习惯。这一行能节省几十分钟。
实际工作中真正重要的事
先读 Events,而不是状态名称。 状态只说明卡在哪里,缺少什么总在事件中。无论考试还是工作,颠倒顺序都会多花一倍时间。
NODE 一列就能把调查范围减半。 空白是调度器,已填写是 kubelet。养成使用 kubectl get pod -o wide 的习惯,就能顺手完成这一分支判断。
在 CrashLoopBackOff 中,不带 --previous 就不要看日志。 当前容器刚刚重建,还没有任何输出。看到空日志就断定“没有日志”,是最常见的无效排查。
接下来的两个实验会亲手制造这些情况。