Kubernetes Pod 一直 Pending 排查全流程:从 describe events 到资源、污点、PVC 逐层定位

📅 2026/7/28 14:28:22 👁️ 阅读次数 📝 编程学习
Kubernetes Pod 一直 Pending 排查全流程:从 describe events 到资源、污点、PVC 逐层定位

Kubernetes Pod 一直 Pending 排查全流程:从 describe events 到资源、污点、PVC 逐层定位

上线时kubectl apply一把梭,结果kubectl get pods看到 Pod 卡在Pending十分钟不动。Pending 意味着调度器(scheduler)还没给这个 Pod 找到能落脚的节点,或者找到了节点但容器还没起来。它和CrashLoopBackOff(起来了又崩)是两码事——Pending 是根本没被调度上去

这篇按「怎么定位 → 五类常见原因 → 每类怎么修」的顺序,给一套能照着敲的排查流程。

第一步:永远先看 describe 的 Events

九成 Pending 问题的答案就写在kubectl describe最底下的 Events 里,别急着改 YAML。

kubectl describe pod<pod-name>-n<namespace>

翻到最下面的Events:段,重点看FailedScheduling这行。scheduler 会明确告诉你为什么调度不上去,比如:

Events: Type Reason Message ---- ------ ------- Warning FailedScheduling 0/3 nodes are available: 3 Insufficient cpu.

这行0/3 nodes are available后面的原因,直接决定你往哪个方向查。下面按最常见的几类 Message 分别讲。

原因一:资源不够(Insufficient cpu/memory)

Message 长这样:0/3 nodes are available: 3 Insufficient cpu.Insufficient memory。意思是所有节点的可分配剩余资源都小于你 Pod 的requests

注意关键点:调度看的是requests 之和 vs 节点 allocatable,不是节点当前真实使用率。哪怕节点 CPU 实际只用了 10%,只要已调度 Pod 的 requests 加起来占满了,新 Pod 照样调度不上。

先看节点到底还剩多少:

kubectl describenode<node-name>|grep-A6"Allocated resources"

输出里Requests那列就是已被预定的量:

Allocated resources: Resource Requests Limits cpu 3800m (95%) 6 (150%) memory 7Gi (90%) 10Gi

CPU requests 已经占到 95%,新 Pod 要 500m 自然塞不下。三种修法:

# 修法1:调低 Pod 的 requests(前提是真的用不了那么多)resources:requests:cpu:200m# 从 500m 降下来memory:256Mi
# 修法2:扩容节点(手动加节点或让 Cluster Autoscaler 自动加)kubectl get nodes# 确认加成功# 修法3:清理占着 requests 却闲置的 Podkubectltoppods-A--sort-by=cpu# 找出「requests 高但实际使用低」的

最容易被忽略的坑:某个 Pod 把requests.cpu写成了4(4 核),自己占满一台节点。写 requests 要按实际稳态用量估,不要照着 limits 抄。

原因二:节点有污点(Taints),Pod 没配容忍(Tolerations)

Message:node(s) had untolerated taint {key: value}。节点被打了污点(比如 master 节点默认带node-role.kubernetes.io/control-plane:NoSchedule),普通 Pod 不配 toleration 就被排斥。

看节点污点:

kubectl describenode<node-name>|grepTaints# Taints: node-role.kubernetes.io/control-plane:NoSchedule

两种修法。如果这个污点确实该被这个 Pod 容忍(比如 DaemonSet 要跑在所有节点上),给 Pod 加 toleration:

tolerations:-key:"node-role.kubernetes.io/control-plane"operator:"Exists"effect:"NoSchedule"

如果是节点被误打了污点(比如之前 drain 后忘了恢复),把污点去掉:

# 注意 key 后面加个减号表示删除kubectl taint nodes<node-name>node-role.kubernetes.io/control-plane:NoSchedule-

还有一种自动加的污点要小心:节点 NotReady 或磁盘压力大时,K8s 会自动打node.kubernetes.io/not-ready等污点,这时候要去修节点本身,而不是加 toleration 硬塞。

原因三:nodeSelector / affinity 把自己框死了

Message:node(s) didn't match Pod's node affinity/selector。你给 Pod 加了nodeSelectornodeAffinity,但没有节点带对应的 label。

# 比如要求节点有 disktype=ssd 标签nodeSelector:disktype:ssd

排查:看有没有节点真带这个 label。

kubectl get nodes --show-labels|grepdisktype# 空 → 没有任何节点带这个标签,自然调度不上

修法二选一:给目标节点打上 label,

kubectl label nodes<node-name>disktype=ssd

或者放宽约束。硬亲和(requiredDuringScheduling)调不上就直接 Pending,如果只是「优先」而非「必须」,改成软亲和preferredDuringSchedulingIgnoredDuringExecution,匹配不到时也能退而求其次调到别的节点。

原因四:PVC 没绑上(pod has unbound immediate PersistentVolumeClaims)

Message:pod has unbound immediate PersistentVolumeClaims。Pod 挂了个 PVC,但这个 PVC 没找到能绑定的 PV,Pod 就跟着一起 Pending。

先看 PVC 自己的状态:

kubectl get pvc-n<namespace># NAME STATUS VOLUME CAPACITY STORAGECLASS# data Pending (空)

PVC 也是 Pending,再 describe 它:

kubectl describe pvc data-n<namespace>

常见两种根因:一是storageClassName写错或不存在,动态供应器根本没被触发;二是集群没装 provisioner(比如裸机集群没配 storage class)。

kubectl get storageclass# 确认 class 名字存在、且有 default 标记

修法:PVC 的storageClassName对上真实存在的 class;裸机环境要么手动创建 PV 静态绑定,要么装个 provisioner(如 local-path-provisioner)。

原因五:调度上了但容器起不来(ContainerCreating 卡住)

有时 Pod 不是纯 Pending,而是ContainerCreating长时间不动——已经调度到节点了,但 kubelet 在拉镜像或挂卷时卡住。还是 describe 看 Events:

  • Failed to pull image ... ImagePullBackOff:镜像名/tag 写错,或私有仓库缺imagePullSecrets
  • MountVolume.SetUp failed:卷挂载失败,常是 PV 后端(NFS/云盘)不可达。

镜像拉取失败最常见,先手动在节点上验证镜像能不能拉:

# 私有仓库要给 Pod 配 imagePullSecretskubectl create secret docker-registry regcred\--docker-server=<你的仓库>--docker-username=<user>--docker-password=<pwd>

然后在 Pod spec 里引用:

spec:imagePullSecrets:-name:regcred

一条命令快速缩小范围

排查前先用这条把「所有异常 Pod + 事件」一次性捞出来,比逐个 describe 快:

# 按最近事件排序,直接看到 FailedScheduling 原因kubectl get events-n<namespace>--sort-by='.lastTimestamp'|grep-i-E"fail|pending|insufficient"

小结

Pod Pending 的排查心法是「先问 scheduler 为什么调不上去」,而不是瞎改 YAML:

  • 第一步永远是kubectl describe pod看 Events 里的FailedScheduling,原因都写在那。
  • Insufficient cpu/memory:比的是 requests 之和 vs 节点 allocatable,不是真实使用率 → 降 requests、扩节点或清闲置 Pod。
  • untolerated taint:节点有污点 → 加 toleration 或删污点,自动打的污点要修节点本身。
  • didn’t match node selector/affinity:约束太死没节点匹配 → 打 label 或把硬亲和改软亲和。
  • unbound PVC:存储没绑上 → 查 storageClass 是否存在、有没有 provisioner。
  • ContainerCreating 卡住:已调度但拉镜像/挂卷失败 → 查镜像名和 imagePullSecrets。

一句话记忆点:Pending 看 scheduler,CrashLoop 看容器日志——describe 的 Events 段是这两类问题共同的第一现场。