Skip to content

第 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/selectoraffinity/selector 不匹配检查节点 label 与 Pod affinity
Insufficient cpu/memory资源不足扩容或减小 requests
node(s) were unschedulable节点被 cordonkubectl 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 处理后的最终配置(包含自动注入的默认容忍)。重点检查:

  1. key 是否与污点完全一致(大小写敏感)
  2. operator 是 Equal 时,value 是否一致
  3. effect 是否匹配(或已省略)
  4. 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 drainEvicted 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

  1. 污点是 PreferNoSchedule——软约束,资源紧张时仍会调度上去。
  2. 节点污点被误删(kubectl taint ... key- 用错)。
  3. 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=100

7.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 关系。