主题
第 7 章:故障排查与调试
污点/容忍相关问题只有两大类症状:Pod 调度不上(Pending) 和 Pod 被意外驱逐。本章给出系统化排查方法论。
7.1 症状一:Pod 一直 Pending
第一步:看事件(90% 的问题在这里)
bash
kubectl describe pod <pod-name> -n <ns>
# 或直接看事件
kubectl get events -n <ns> --field-selector reason=FailedScheduling典型报错:
Warning FailedScheduling 10s default-scheduler
0/3 nodes are available: 1 node(s) had untolerated taint {dedicated: gpu},
2 node(s) had untolerated taint {node.kubernetes.io/unreachable: }.解读:3 个节点中,1 个有 dedicated=gpu 污点未被容忍,2 个不可达。调度器会列出每个节点被拒绝的具体原因,逐个解决即可。
常见报错模式对照表
| 报错信息 | 原因 | 解决 |
|---|---|---|
node(s) had untolerated taint {key: value} | 节点有污点,Pod 无容忍 | 给 Pod 加 toleration,或删除节点污点 |
node(s) didn't match Pod's node affinity/selector | affinity/selector 不匹配 | 检查节点 label 与 Pod affinity |
Insufficient cpu/memory | 资源不足 | 扩容或减小 requests |
node(s) were unschedulable | 节点被 cordon | kubectl uncordon <node> |
| 报错里同时出现多条 | 多个原因叠加,全部要解决 | 逐条对照处理 |
第二步:核对节点污点
bash
# 查看所有节点污点
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
# 查看某节点详情
kubectl describe node <node> | grep -A5 Taints第三步:核对 Pod 容忍
bash
kubectl get pod <pod> -n <ns> -o jsonpath='{.spec.tolerations}' | jq .注意:显示的是 API Server 处理后的最终配置(包含自动注入的默认容忍)。重点检查:
- key 是否与污点完全一致(大小写敏感)
- operator 是 Equal 时,value 是否一致
- effect 是否匹配(或已省略)
- Exists 时是否误写了 value(会被 API 拒绝,创建时就报错)
排查清单模板
□ Pod 的 toleration 与节点 taint 的 key/value/effect 完全匹配?
□ 节点上还有【其他】污点没被容忍吗?(多污点是 AND)
□ 除了污点,是否还有 affinity/资源等其他约束不满足?
□ 节点本身是否 Ready?(kubectl get nodes)
□ 节点是否被 cordon?7.2 症状二:Pod 被意外驱逐(Evicted)
排查路径
bash
# 1. 查看被驱逐的 Pod
kubectl get pods -n <ns> --field-selector=status.phase=Failed
# STATUS 显示 Evicted
# 2. 查看驱逐原因
kubectl describe pod <evicted-pod> -n <ns> | grep -A5 "Reason\|Message"驱逐原因分类
| 驱逐者 | 典型 Message | 根因 |
|---|---|---|
| 污点驱逐(taint-eviction-controller) | Pod 直接消失重建,原 Pod 无 Evicted 残留(Deployment 管理时) | 节点被打 NoExecute 污点(not-ready/unreachable/手动打的) |
| kubelet 资源驱逐(eviction-manager) | The node was low on resource: memory | 节点内存/磁盘/PID 压力,按 QoS 逐出 |
| kubectl drain | Evicted by kubectl drain | 人为维护 |
如何确认是污点驱逐
bash
# 看节点是否有 NoExecute 污点(现在或曾经)
kubectl describe node <node> | grep -A10 Taints
# 看节点事件
kubectl get events --field-selector involvedObject.kind=Node,involvedObject.name=<node>
# 看节点状态历史(是否经历过 NotReady)
kubectl describe node <node> | grep -A20 Conditions"Pod 怎么过了 5 分钟才重建?"
这不是故障,是默认容忍在起作用:not-ready/unreachable 默认 tolerationSeconds=300。如果节点 5 分钟内恢复,Pod 不会被驱逐;超过才迁移。想加快见第 5 章场景四。
7.3 症状三:Pod 调度到"不该去"的节点
bash
# 发现业务 Pod 跑到了专用节点上?检查:
# 1. 该 Pod 是否配置了容忍(可能是"万能容忍")
kubectl get pod <pod> -o jsonpath='{.spec.tolerations}' | jq .
# 2. 该节点污点是否还在(可能被人删了)
kubectl describe node <node> | grep Taints
# 3. 污点是 PreferNoSchedule 吗?(软约束挡不住)根因 TOP3:
- 污点是
PreferNoSchedule——软约束,资源紧张时仍会调度上去。 - 节点污点被误删(
kubectl taint ... key-用错)。 - Pod 模板里遗留了过宽的 toleration(如复制系统组件 YAML 带出来的)。
7.4 症状四:改了容忍但 Pod 没变化
关键认知:spec.tolerations 对 Deployment 的 Pod template 修改会触发滚动更新;但对单个已存在 Pod 直接 edit 通常不允许(Pod spec 大部分字段不可变)。
bash
# 正确姿势:改 Deployment/DaemonSet 的 template
kubectl patch deployment <name> -n <ns> --type=json -p='[
{"op":"add","path":"/spec/template/spec/tolerations/-","value":
{"key":"dedicated","operator":"Equal","value":"gpu","effect":"NoSchedule"}}
]'
# 触发滚动重建后生效
kubectl rollout status deployment/<name> -n <ns>7.5 常用调试命令速查
bash
# ── 调度类 ──────────────────────────────
kubectl get pods -n <ns> --field-selector=status.phase=Pending # 找 Pending Pod
kubectl describe pod <pod> -n <ns> | tail -20 # 看调度事件
kubectl get events -n <ns> --sort-by=.lastTimestamp | tail -20 # 按时间看事件
# ── 节点污点 ────────────────────────────
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
kubectl describe node <node> | grep -A5 Taints
# ── Pod 容忍 ────────────────────────────
kubectl get pod <pod> -n <ns> -o jsonpath='{.spec.tolerations}' | jq .
# ── 驱逐类 ──────────────────────────────
kubectl get pods -A --field-selector=status.phase=Failed # 全集群找被驱逐 Pod
kubectl get events -A --field-selector reason=Evicted # 驱逐事件
kubectl get events -A --field-selector reason=TaintManagerEviction # 污点驱逐事件(部分版本)
# ── 调度器日志(需要控制面权限)──────────
kubectl logs -n kube-system -l component=kube-scheduler --tail=1007.6 实战演练:一个完整排障案例
现象:运维同学反馈"新部署的 gpu-app 一直 Pending"。
bash
$ kubectl get pods -l app=gpu-app
NAME READY STATUS RESTARTS AGE
gpu-app-7d9f4b5c6-x2abc 0/1 Pending 0 5m
$ kubectl describe pod gpu-app-7d9f4b5c6-x2abc | tail -5
Events:
Warning FailedScheduling 5m default-scheduler
0/4 nodes are available: 3 node(s) had untolerated taint {nvidia.com/gpu: true},
1 node(s) didn't match Pod's node affinity/selector.
$ kubectl get pod gpu-app-7d9f4b5c6-x2abc -o jsonpath='{.spec.tolerations}'
# 输出为空 → Pod 没有配置容忍
$ kubectl describe node gpu-1 | grep Taints
Taints: nvidia.com/gpu=true:NoSchedule结论:Pod 需要补 toleration。但注意报错里还有"1 node didn't match affinity"——即使补了容忍,affinity 还指向了一个不匹配的节点,需一并修正:
bash
kubectl patch deployment gpu-app --type=json -p='[
{"op":"add","path":"/spec/template/spec/tolerations","value":
[{"key":"nvidia.com/gpu","operator":"Exists","effect":"NoSchedule"}]},
{"op":"replace","path":"/spec/template/spec/affinity/nodeAffinity/...","value":...}
]'经验:调度失败事件会把所有不满足的节点原因都列出来,务必逐条处理,不要只修第一条。
本章小结
- Pending 排障三板斧:
describe pod看事件 → 核对节点 taint → 核对 Pod toleration。 - 驱逐排障先分清:污点驱逐 / kubelet 资源驱逐 / drain。
- "5 分钟才重建"是默认 tolerationSeconds=300,不是 bug。
- 报错信息里每个节点的原因都要处理,多污点是 AND 关系。