Pod 一直 Pending:调度器 5 类失败原因与 kubectl describe 的正确读法

我不会起名字322 · 后端 / 算法 / 数据库

📘 技术栈 | 🧩 力扣 Hot100 | 🐹 Go 项目 | 🟥 Redis | 🐬 MySQL

文章目录

  • [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 再决定要不要扩容。扩容是手段,不是诊断。

相关推荐
我不会起名字3224 小时前
K8s 服务 DNS 解析偶发 5 秒超时:conntrack 与 ndots 的 3 个坑
kubernetes·k8s·dns·conntrack·coredns
risc1234564 小时前
万变不离其宗
人工智能·低代码·云原生
T.O.P_KING7 小时前
Falco vs. Tetragon:云原生运行时安全选型指南
安全·云原生
七夜zippoe7 小时前
把 OpenClaw Agent 搬进 K8s:容器化部署全记录
ai·云原生·容器·kubernetes·openclaw
March.s18 小时前
Kubernetes 全景导览:从部署演进到云原生底座
云原生·kubernetes·dubbo
熊出没20 小时前
云原生架构落地实践:Kubernetes 生产级配置与 Service Mesh 流量治理
云原生·架构·kubernetes·istio
东风微鸣1 天前
K8s 最佳实践已更新:请使用 Node Swap!
kubernetes
CCblandy1 天前
一致性哈希到底省了多少搬迁?本机实测 3 轮:取模 6→7 搬 85.8%,V=128 只搬 14.3%,虚拟节点太少会被倾斜骗
后端·python·云原生·性能优化·架构