在 Kubernetes 里创建了 Deployment,kubectl get pods 盯着看,状态一直卡在 Pending,几分钟过去也不变成 Running。这种情况比 CrashLoopBackOff 更让人摸不着头脑,因为容器根本没启动,也就没有应用日志可看——问题出在调度阶段,而不是运行阶段。
Pod 的 Pending 本质上意味着「调度器没能为它找到一个合适的节点」。Kubernetes 的调度是两道关:先做过滤(Filter),排除不满足条件的节点;再做打分(Score),在剩下的节点里挑最优的。任何一道关上所有候选节点都被淘汰,Pod 就会一直 Pending。常见原因包括资源不足、节点选择器/亲和性不匹配、污点与容忍度冲突、PVC 未绑定、镜像过大等。
下面按「先看事件、再查资源、后看亲和与存储」的思路,把 Pod Pending 的排查方法讲清楚,配上真实命令和 YAML 片段,让你能快速定位到是哪一类约束把调度卡住了。
一、第一步永远是看 Events
kubectl describe pod 会输出事件列表,几乎一半以上的 Pending 问题,答案就写在 Events 里:
kubectl describe pod -n <命名空间>
重点看最后几行的 Events 段落,找 Warning 类型。几个高频提示及其含义:
| 事件提示 | 含义 | 根因方向 |
|---|---|---|
| 0/3 nodes are available: 3 Insufficient cpu | 节点 CPU 不足 | 资源请求过大 |
| 0/3 nodes available: 3 node(s) had taint | 节点有污点且 Pod 未容忍 | 污点/容忍度 |
| 0/3 nodes available: 3 didn't match node selector | 标签不匹配 | nodeSelector/亲和性 |
| pod has unbound immediate PersistentVolumeClaims | PVC 未绑定 | 存储问题 |
| no nodes available to schedule pods | 没有可调度节点 | 节点 NotReady |
| FailedScheduling | 综合调度失败 | 看后面的具体原因 |
也可以直接看集群事件:
kubectl get events -n <命名空间> --sort-by='.lastTimestamp'
二、资源不足:最常见的一类
调度器判断「够不够」依据的是 Pod 的 requests(不是 limits)。如果一个 Pod 请求 4 核 CPU、8GB 内存,而所有节点剩余可分配资源都不足,就会一直 Pending。
查看节点资源与已分配情况:
kubectl describe node <节点名> | grep -A8 "Allocated resources"
输出示例:
Allocated resources:
Resource Requests Limits
cpu 3800m (95%) 5000m
memory 14Gi (87%) 16Gi
如果 Requests 已经逼近 100%,就说明该节点几乎没法再塞入新的 Pod 了。
查看各节点整体使用率(需要 metrics-server):
kubectl top nodes
kubectl top pods -n <命名空间>
如果没部署 metrics-server,可以看节点容量与已分配:
kubectl get nodes -o custom-columns=\
NAME:.metadata.name,\
CPU:.status.capacity.cpu,\
MEM:.status.capacity.memory
解决方向:
- 降低 Pod 的 requests(前提是确实用不了那么多);
- 给集群扩容节点;
- 清理闲置 Pod 释放资源;
- 检查是否 requests 设得虚高(很多团队照抄模板,把 requests 设成 4 核却只用到 0.2 核)。
三、节点选择器、亲和性与污点
nodeSelector
如果 Pod 指定了 nodeSelector,而没有任何节点带这个标签,就会 Pending:
spec:
nodeSelector:
disktype: ssd
排查节点标签:
kubectl get nodes --show-labels
给节点补标签:
kubectl label node <节点名> disktype=ssd
节点亲和性(nodeAffinity)
requiredDuringSchedulingIgnoredDuringExecution 是硬约束,不满足就不调度。查看 Pod 的亲和性配置,再对照节点标签是否匹配。用 In、Exists 等操作符时要小心大小写和拼写。
污点与容忍度(Taint/Toleration)
污点让节点「排斥」Pod,只有带对应容忍度的 Pod 才能调度上去。查看节点污点:
kubectl describe node <节点名> | grep -A3 Taints
常见污点如 node-role.kubernetes.io/control-plane:NoSchedule(主节点默认不打业务 Pod)。给 Pod 加容忍度:
spec:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
需要提醒的是,容忍度只是「允许」调度到有该污点的节点,不等于「一定」会去。如果同时有 nodeAffinity 限定,两者要同时满足。
四、存储问题:PVC 未绑定
如果 Pod 挂载了 PersistentVolumeClaim,而该 PVC 处于 Pending,Pod 就会一直等,事件里会出现 pod has unbound immediate PersistentVolumeClaims。
kubectl get pvc -n <命名空间>
kubectl describe pvc -n <命名空间>
PVC Pending 的常见原因:
- 没有可用的 PV 与之匹配(静态供给场景);
- StorageClass 不存在或 provisioner 异常(动态供给场景);
- 存储容量不足或访问模式(accessModes)不匹配。
排查 StorageClass 是否正常:
kubectl get storageclass
kubectl describe storageclass <名称>
关于 PV/PVC 的完整配置,后面另有一篇专门讲,这里只强调一点:存储没准备好,Pod 一定 Pending,而且事件里会明确指向 PVC,看到这个提示就直接去查存储,不用在资源上绕圈。
五、节点状态异常与不可调度
有时候不是资源不够,而是节点本身有问题。检查:
kubectl get nodes
如果节点是 NotReady,或带着 SchedulingDisabled,调度器就不会往里放 Pod。kubectl cordon 会把节点标记为不可调度:
# 取消不可调度标记
kubectl uncordon <节点名>
节点 NotReady 常见原因是 kubelet 挂了、网络插件异常、磁盘压力(DiskPressure)、内存压力(MemoryPressure)。查看节点状况:
kubectl describe node <节点名> | grep -A6 Conditions
如果看到 MemoryPressure: True 或 DiskPressure: True,需要先解决节点本身的资源压力。
六、一张表定位 Pending 根因
| 排查维度 | 查看命令 | 典型结论 |
|---|---|---|
| 事件 | kubectl describe pod | 直接给出失败原因 |
| 资源 | describe node 的 Allocated | requests 超节点余量 |
| 标签 | get nodes --show-labels | nodeSelector 无匹配 |
| 污点 | describe node 的 Taints | 缺少 toleration |
| 存储 | get pvc | PVC 未 Bound |
| 节点 | get nodes | 节点 NotReady/封锁 |
| 亲和 | get pod -o yaml | 硬亲和不满足 |
七、实操:一个完整的排查流程
假设有个 Pod 一直 Pending,按下面顺序走:
# 1. 看 Pod 概况与所在节点
kubectl get pod -o wide
# 2. 看事件,找 FailedScheduling 的具体理由
kubectl describe pod | sed -n '/Events/,$p'
# 3. 如果是资源不足,看节点余量
kubectl describe node <节点名> | grep -A8 "Allocated resources"
# 4. 如果是选择器不匹配,看节点标签
kubectl get nodes --show-labels
# 5. 如果涉及存储,看 PVC 状态
kubectl get pvc -A | grep -v Bound
# 6. 看调度器自身是否正常(多调度器或自定义调度器场景)
kubectl get pods -n kube-system | grep scheduler
如果调度器日志需要看:
kubectl logs -n kube-system --tail=100
经验补充:一个上了 500 个节点的集群,调度决策通常在毫秒到几十毫秒级,如果 Pod 长时间不调度,几乎可以锁定是约束条件冲突,而不是调度器「慢」。
八、写 YAML 时的预防习惯
- requests 要基于真实监控数据,不要抄模板虚高;
- 尽量少用硬亲和(required),能软(preferred)就软,给调度器留余地;
- 给关键业务加
PodDisruptionBudget,避免节点维护时被批量驱逐; - 集群留出 15% 到 20% 的缓冲资源,应对突发扩容和节点故障;
- 用
kubectl get pod -o yaml定期 review 线上 Pod 的资源声明,防止长期漂移。
九、从过滤到打分:调度器到底怎么想
把 Pending 彻底搞懂,最好理解一下调度器的两步逻辑。
第一步是过滤(Filter),也叫预选。调度器遍历所有节点,逐个检查是否满足 Pod 的硬性约束:资源是否够(按 requests 算)、节点选择器与亲和性是否匹配、污点是否被容忍、端口是否冲突、卷所在可用区是否对得上。任何一条不满足,该节点就被淘汰。如果所有节点都被淘汰,Pod 就 Pending,事件里会打印每个节点被淘汰的原因,这就是为什么 describe 里那句「0/5 nodes are available」后面跟着一串理由——每一种理由前面都带着被淘汰的节点数。
第二步是打分(Score),也叫优选。过滤剩下的候选节点会被按多种策略打分,比如资源均衡(倾向于把 Pod 放到资源更空闲的节点)、亲和性权重、拓扑分散(把副本打散到不同节点)等,得分最高的节点胜出。打分段一般不会导致 Pending,因为它是在「有候选」的前提下做选择。所以排查 Pending,重点永远在过滤段。
理解了这一点,排查就有了明确手势:看 0/N nodes are available 里 N 等于节点总数,说明所有节点都过不了过滤;后面跟着的原因往往不止一条,要一条条对照。如果 N 小于节点总数,说明只有部分节点被过滤,剩下的节点可能因为其他原因(比如镜像拉取失败)而卡住,这时要往节点侧看。
还有两个容易被忽略的细节。一是资源计算用的是 requests 而非实际用量,节点上「已分配」的虚拟资源可能早已超过真实负载,但调度器只认 requests,于是新的 Pod 被拒之门外;二是资源碎片,一个节点虽然总量够,但被切得七零八落,剩不下满足最小请求的连续资源,也会导致 Pending。面对碎片,除了扩容,也可以适当调小 Pod 的 requests 粒度来提升装箱效率。
常见问题
Q1:Pod 一直 Pending,但 describe 里没有明显报错,怎么办?
A:先确认是不是根本没有 Ready 的节点(kubectl get nodes),再看是否所有节点都被 cordon 或带污点。极端情况下是调度器组件异常,检查 kube-scheduler Pod。
Q2:节点上 CPU 还有空余,为什么还报 Insufficient cpu?
A:调度看的是 requests,不是实际使用率。节点上已分配 requests 之和可能已经占满,即使实际用量很低。要么调低其他 Pod 的 requests,要么扩容节点。
Q3:加了大内存节点,Pod 还是 Pending?
A:检查是否还有其他约束没满足,比如 nodeSelector、污点、PVC。大节点若不满足这些硬性条件一样调度不上去。
Q4:PVC 显示 Bound 了,Pod 还是 Pending?
A:看 Pod 事件里是否有其他原因,比如访问模式冲突(ReadWriteOnce 被多节点争用)、镜像体积过大导致节点拉取超时,或者节点亲和与 PVC 所在可用区不一致。
Q5:能不能强制把 Pod 调度到某个节点?
A:可以临时用 nodeName 字段直接指定,但这会绕过调度器的资源检查,风险较高,仅适合紧急排障,事后应删除。推荐的做法是给节点打标签配合 nodeSelector。
总结
- Pod Pending 一定是调度阶段的问题,第一件事是
kubectl describe pod看 Events,多数情况答案就在那里。 - 资源类报错看的是 requests 而非实际使用率,
kubectl describe node的 Allocated resources 是关键依据。 - 标签、污点、亲和性是三个高频「硬约束」,排查时把它们和节点实际属性逐一对照。
- 事件里出现 unbound PVC 就直奔存储,PVC 不 Bound,Pod 永远 Pending。
- 预防胜于救火:requests 按实测来设、少用硬亲和、集群留足缓冲资源,能大幅减少 Pending 的发生。
企业QQ咨询




