我不会起名字322 · 后端 / 算法 / 数据库
文章目录
- [Pod 一直 Pending:调度器 5 类失败原因与 kubectl describe 的正确读法](#Pod 一直 Pending:调度器 5 类失败原因与 kubectl describe 的正确读法)
-
- 一、第一个要改掉的习惯
- [二、5 类失败原因](#二、5 类失败原因)
-
- [原因 1:资源不足(但要看的是 request,不是实际用量)](#原因 1:资源不足(但要看的是 request,不是实际用量))
- [原因 2:亲和性 / nodeSelector 不匹配](#原因 2:亲和性 / nodeSelector 不匹配)
- [原因 3:污点没有容忍](#原因 3:污点没有容忍)
- [原因 4:PVC 没绑定](#原因 4:PVC 没绑定)
- [原因 5:节点自身状态异常](#原因 5:节点自身状态异常)
- [三、`kubectl describe` 的正确读法](#三、
kubectl describe的正确读法) - [四、批量 Pending 时怎么判断范围](#四、批量 Pending 时怎么判断范围)
- 五、带走物
Pod 一直 Pending:调度器 5 类失败原因与 kubectl describe 的正确读法
一、第一个要改掉的习惯
看到 Pod Pending,最常见的第一反应是"资源不够了,扩容"。
这个判断经常是错的。 Pending 的原因至少有六类,资源不足只是其中一类。而且更麻烦的是:如果只是调度约束不匹配,加多少节点都不会让 Pod 起来,你会白白扩容一轮、浪费时间和成本。
正确的第一步永远是:
bash
kubectl describe pod <pod-name> -n <ns>
看最底下的 Events。调度器已经把原因写在里面了,绝大多数 Pending 靠这一段就能定性。
二、5 类失败原因
原因 1:资源不足(但要看的是 request,不是实际用量)
这是最容易被误读的一类。看这段输出:
text
Events:
Warning FailedScheduling 0/12 nodes are available:
3 Insufficient cpu, 5 Insufficient memory, 4 node(s) had untolerated taint.
关键点:调度器看的是 request,不是 actual usage。
bash
kubectl describe node node-1 | grep -A 20 "Allocated resources"
你会看到类似:
text
Allocated resources:
Resource Requests Limits
cpu 3600m (90%) 8000m (200%)
memory 6Gi (85%) 12Gi (170%)
一个节点 CPU 实际只用了 20%,但 request 已经被预留到 90%,那么新 Pod 依然调不上去。这是"节点看起来很闲但 Pod 就是 Pending"的根本原因。
另外别忘了 namespace 级的配额:
bash
kubectl get resourcequota -A
kubectl describe resourcequota -n <ns>
ResourceQuota 打满时,加节点完全没用------配额是 namespace 维度的硬上限。
原因 2:亲和性 / nodeSelector 不匹配
text
Events:
Warning FailedScheduling 0/12 nodes are available: 12 node(s) didn't match Pod's node affinity/selector.
Pod 里写了:
yaml
nodeSelector:
disk: ssd
但集群里没有任何节点带 disk=ssd 这个标签。或者用了 nodeAffinity / podAntiAffinity,规则相互冲突。
排查:
bash
# 看节点有什么标签
kubectl get nodes --show-labels | tr ',' '\n' | grep -i disk
# 看 Pod 的约束
kubectl get pod <pod> -o yaml | grep -A 10 -E "nodeSelector|affinity"
生产集群里这类隐式规则往往很多 (SSD 节点池、GPU 节点池、可用区约束),是最容易被忽略的一类。尤其是 podAntiAffinity 配了 requiredDuringScheduling 时,副本数超过节点数就永远调不上。
原因 3:污点没有容忍
text
Events:
Warning FailedScheduling 0/12 nodes are available: 12 node(s) had untolerated taint {dedicated: gpu}.
节点被打了污点(taint),Pod 没有对应的 toleration:
yaml
tolerations:
- key: "dedicated"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"
排查:
bash
kubectl describe node node-1 | grep -i taint
常见场景:专用节点池(GPU、高内存)、控制平面节点(默认有 node-role.kubernetes.io/control-plane:NoSchedule)。
原因 4:PVC 没绑定
Stateful 服务 Pending,很多时候不是计算资源问题,是存储卷没绑上:
bash
kubectl get pvc -n <ns>
kubectl describe pvc data-mysql-0 -n <ns>
如果 PVC 也是 Pending,看它的 Events:
text
Events:
Normal WaitForFirstConsumer waiting for first consumer to be created before binding
# 或者是
Warning ProvisioningFailed storageclass.storage.k8s.io "fast" not found
常见原因:StorageClass 名字写错、没有默认 StorageClass、CSI 驱动没装、或者用了 WaitForFirstConsumer 而 Pod 又因为别的原因起不来(形成死锁)。
这类问题千万不要用加计算节点的方式去解决,方向完全错了。
原因 5:节点自身状态异常
节点可能处于不健康状态,调度器直接跳过它:
bash
kubectl get nodes
kubectl describe node node-1 | grep -A 10 Conditions
要找的关键字:
MemoryPressure/DiskPressure/PIDPressure:节点资源压力,kubelet 会自动打上对应污点;Ready=False:节点失联;SchedulingDisabled:节点被cordon了(维护中)。
bash
# 被 cordon 的节点
kubectl get nodes | grep SchedulingDisabled
三、kubectl describe 的正确读法
很多人 describe 只看最后一行,这是不够的。完整的读法是从上到下三段:
bash
kubectl describe pod <pod> -n <ns>
第 1 段:顶部状态区
text
Status: Pending
Node: <none> ← 空 = 还没被调度(调度问题)
← 有节点名 = 已调度,卡在镜像拉取/容器创建(另一类问题)
这一行就能把问题分成两大类 :Node: <none> 是调度器的问题;Node: 有值是 kubelet 的问题(镜像拉取失败、CNI 没准备好等)。
第 2 段:Conditions(容器没起来时看这里)
text
Conditions:
Type Status
PodScheduled False ← 明确告诉你"没被调度上"
第 3 段:Events(最关键)
调度器每次尝试失败都会在这里写一条,带时间戳。如果同一个原因在反复出现,说明调度器一直在重试同一件注定失败的事。
如果 Events 是空的(比如 Pod 创建很久了,Events 被驱逐),用集群事件补:
bash
kubectl get events -A --sort-by=.lastTimestamp | tail -50
四、批量 Pending 时怎么判断范围
单个 Pod Pending 是局部问题,大量 Pod 因同一个原因 Pending 是平台问题。
bash
# 找出所有未调度的 Pod 并聚合原因
kubectl get pods -A --field-selector=status.phase=Pending -o wide
# 按原因聚合(看 Events 里的关键句)
for ns in $(kubectl get ns -o name | cut -d/ -f2); do
kubectl get events -n $ns --field-selector=reason=FailedScheduling -o json |
jq -r '.items[].message' | sed 's/[0-9]\+//g' | sort | uniq -c | sort -rn
done
按原因分类统计之后,就能判断是"某个应用配错了"还是"某类节点不够了"或是"存储供应异常"。
建议平台侧直接做成指标:
text
pending_pods_by_reason{reason="unschedulable_by_resource"}
pending_pods_by_reason{reason="unschedulable_by_affinity"}
pending_pods_by_reason{reason="pvc_pending"}
pending_pods_by_reason{reason="quota_exceeded"}
有了这个视图,调度约束过严、节点池不足、存储异常都能在业务报障之前发现。
五、带走物
一个能直接用的排查顺序:
text
1. kubectl describe pod → 看 Node 字段
├─ Node: <none> → 调度问题,继续第 2 步
└─ Node: 有值 → kubelet 问题(镜像/CNI/运行时),换排查路线
2. 看 Events 里的 FailedScheduling 原因:
├─ Insufficient cpu/memory → 看 node 的 Allocated requests + namespace ResourceQuota
├─ didn't match node selector → 看 Pod 的 nodeSelector/affinity vs 节点 labels
├─ untolerated taint → 看节点 taints vs Pod tolerations
└─ (无 Events / PVC 相关) → kubectl get pvc,看 StorageClass
3. kubectl describe node → 看 Conditions(Pressure / cordon / NotReady)
4. 大量 Pod Pending → 按原因聚合,判断是应用问题还是平台问题
结论一句话:Pending 的答案已经写在 Events 里了,先 describe 再决定要不要扩容。扩容是手段,不是诊断。