📚 建议收藏 | 遇到 Pod Pending 问题,看这一篇就够了

凌晨两点,你被报警电话吵醒——服务挂了。打开监控一看,新扩容的 Pod 全部卡在 Pendingkubectl get pods 刷了半天,状态死活不变。你心想“是不是节点挂了?”跑去查节点,kubectl get nodes 显示全部 Ready。再 kubectl describe pod 一看,Events 里躺着一行刺眼的 FailedScheduling,后面跟着 0/5 nodes are available: 5 Insufficient cpu

集群资源被榨干了。

这个场景我太熟悉了。过去十年里,从自建 K8s 到云上托管集群,Pod Pending 是我遇到过最频繁的生产事故之一。而其中 80% 以上,罪魁祸首就是资源不足——要么是 CPU 被打满,要么是内存被吃光,要么是你给 Pod 配的 requests 太高,集群里没有一个节点塞得下。

这篇文章我从 SRE 实战角度,把 Pod 因资源不足卡 Pending 的排查思路和解决方案完整梳理一遍。适用 Kubernetes 1.28 ~ 1.36 版本,命令和原理在各版本间通用。

先上急救包:5 条命令快速定位

遇到 Pod Pending 别慌,按顺序敲这几条命令,5 分钟内就能把问题摸清楚:

# 1. 看 Pod 到底卡在哪
kubectl describe pod <pod-name> -n <namespace>
# 重点看 Events 尾部,会直接告诉你原因

# 2. 看集群里所有节点还剩多少资源
kubectl describe nodes | grep -A 5 "Allocated resources"
# 重点关注 CPU 和 Memory 的请求占比

# 3. 看节点实时负载(需要安装 metrics-server)
kubectl top nodes

# 4. 看调度器是不是还活着(自建集群用这条)
kubectl get pods -n kube-system -l component=kube-scheduler

📌 托管集群用户注意:如果你用的是云厂商的托管集群(如 EKS、AKS、TKE 等),控制面由云厂商管理,用户通常无法直接查看调度器 Pod 的状态。建议通过云厂商控制台或工单确认调度器是否正常。

# 5. 筛选所有 Pending 的 Pod,看看是不是大面积沦陷
kubectl get pods -A --field-selector=status.phase=Pending

这几条命令跑完,90% 的情况你已经有答案了。

Pod Pending 到底发生了什么?

先花 30 秒搞清楚 K8s 调度的逻辑。

当你创建一个 Pod 时,它不会立刻被扔到某个节点上。调度器(kube-scheduler)会先对所有节点做一轮“过滤”——检查每个节点是否满足 Pod 的所有硬性要求,比如 CPU/内存是否够、节点标签是否匹配、污点能不能容忍等等。

调度器看的是 requests,不是 limits,更不是节点上的实时 CPU 使用率

这句话太重要了,我见过无数人被这个点坑过。你 kubectl top nodes 一看 CPU 才用了 30%,心想“资源充足啊”,但调度器根本不看这个——它看的是所有 Pod 的 requests 总和有没有超过节点的 Allocatable。就算节点实际负载很低,只要 requests 满了,新 Pod 就进不来。

说到 Allocatable,有必要解释一下它是怎么来的。节点的 Allocatable 是在节点总容量基础上,扣除了 kubelet 为系统组件预留的资源(--kube-reserved--system-reserved)以及驱逐阈值(--eviction-hard)之后的可分配量。如果你发现节点总容量很大但 Allocatable 少了很多,先去检查 kubelet 的这几个启动参数。

┌─────────────┐
│  Pod 创建   │
└──────┬──────┘
       ▼
┌─────────────┐     过滤失败      ┌─────────────┐
│  调度器过滤  │ ───────────────▶ │  Pending    │
│  (Filter)   │                   │ FailedSched │
└──────┬──────┘                   └─────────────┘
       ▼ 过滤通过
┌─────────────┐
│  调度器打分  │
│   (Score)   │
└──────┬──────┘
       ▼
┌─────────────┐
│  绑定节点   │
└─────────────┘

过滤失败最常见的原因就是资源不足,Events 里会直接告诉你——0/3 nodes are available: 3 Insufficient cpuInsufficient memory

为什么 kubectl top nodes 显示资源充足,但 Pod 还是 Pending?

这个问题太常见了,值得单独拎出来说。

kubectl top nodes 显示的是节点上所有 Pod 当前瞬时的实际资源使用量总和,它依赖 metrics-server 从 kubelet 的 /stats/summary 端点采集数据。而调度器做决策时看的是 requests 总和——也就是 Pod 向集群“预订”的资源量。

这两者之间的差距可能非常大。一个 Pod requests: 2 CPU 但实际只用 0.1 CPU,在 top 命令里只显示 0.1,但在调度器的账本里已经扣掉了 2 个核。所以你会看到 top 显示资源充足,但调度器依然报 Insufficient

我的建议:排查资源问题时,把 kubectl describe nodekubectl top nodes 结合起来看。前者告诉你“还有多少配额可以分配”,后者告诉你“实际用了多少”。两个数字都对不上,那就要去查每个 Pod 的 requests 配置了。

四步排查法:从现象到根因

第一步:确认是资源不足还是别的坑

拿到 kubectl describe pod 的输出后,Events 部分的 Message 字段会直接告诉你调度失败的原因。

kubectl describe pod my-app-7d8f9b6c4d-xyzab -n production

如果看到类似这样的信息:

Warning  FailedScheduling  12s  default-scheduler  0/4 nodes are available: 4 Insufficient cpu.

那就确定是 CPU 资源不足。如果是 Insufficient memory,就是内存不够。

但也有可能不是资源问题——比如 Events 显示 0/4 nodes are available: 4 node(s) had untolerated taint,那是污点没容忍;或者是 didn't match Pod's node affinity,那是亲和性规则太严。这些后面会单独提一嘴,但今天重点说资源不足。

第二步:看看到底哪个节点还有余粮

确认是资源不足后,下一步是搞清楚集群的资源水位。

kubectl describe nodes

重点看每个节点末尾的 Allocated resources 部分:

Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests     Limits
  --------           --------     ------
  cpu                4800m (120%)  9600m (240%)
  memory             8Gi (80%)     16Gi (160%)
  ephemeral-storage  0 (0%)        0 (0%)

看到 cpu 4800m (120%) 了吗?这意味着这个节点上所有 Pod 的 CPU requests 总和已经超过了节点的可分配容量。新 Pod 自然进不来。

我踩过的坑:有一次我盯着 kubectl top nodes 看了半天,每个节点 CPU 使用率都不到 40%,死活想不通为什么调度失败。后来才发现是某个团队给所有微服务都配了 requests: 2(相当于 2000m),把整个集群的 requests 配额撑爆了,但实际负载很低。从那以后我养成习惯——排查资源问题先看 requests,再看实际使用

第三步:揪出“占着茅坑不拉屎”的 Pod

找到资源最紧张的节点后,看看是哪些 Pod 在消耗 requests

kubectl describe node <node-name> | grep -A 10 "Non-terminated Pods"

或者更精确一点,看每个 Pod 的 requests

kubectl get pods -n <namespace> -o=custom-columns=NAME:.metadata.name,CPU_REQ:.spec.containers[*].resources.requests.cpu,MEM_REQ:.spec.containers[*].resources.requests.memory

这时候你可能会发现:有些 Pod 的 requests 配得极其夸张——一个简单的 Node.js 服务配了 2 核 CPU、4Gi 内存,但实际运行只需要 100m CPU、256Mi 内存。

第四步:对症下药

资源不足的解决方案无非三条路:

方案一:降低 Pod 的 requests

这是最快、最经济的手段。把不合理的 requests 调低,释放调度空间。

resources:
  requests:
    cpu: 100m      # 原来是 500m
    memory: 256Mi  # 原来是 1Gi
  limits:
    cpu: 200m
    memory: 512Mi

方案二:清理僵尸 Pod 和过期资源

有时候节点上堆了一堆已经完成或驱逐的 Pod 还在占用 requests 记录。清理掉它们:

kubectl delete pod <pod-name> -n <namespace>
# 或者批量清理 Completed 状态的 Pod
kubectl delete pods -n <namespace> --field-selector=status.phase=Succeeded

方案三:扩容节点

如果业务确实需要这么多资源,那就加节点吧。云上环境可以用 Cluster Autoscaler 自动扩容。

一个真实案例:半夜被报警炸醒

说个真事。去年某个周五晚上,我们一个核心服务的 HPA 触发了扩容,新副本一直 Pending。我远程连上去一看,Events 显示 5 Insufficient memory

kubectl describe nodes 一看,所有节点的内存 requests 都已经超过 95%。再往下翻,发现有个非核心的批处理任务跑了 200 个 Pod,每个 requests.memory 配了 512Mi——加起来整整 100Gi 内存被“预定”了,但实际上这些 Pod 每个只用了几十 Mi。

我当时直接把这个任务的副本数从 200 砍到 20,核心服务立刻调度成功。事后我给这个任务配了 PriorityClass: low-priority,保证核心服务可以抢占它的资源。

其他导致 Pending 的常见原因(快速过一遍)

虽然这篇文章聚焦资源不足,但实际生产环境中 Pod Pending 的原因还有好几种:

原因

Events 关键词

怎么查

怎么解决

污点未容忍

had untolerated taint

kubectl describe node 看 Taints

给 Pod 加 tolerations 或移除节点污点

节点亲和性不匹配

didn't match Pod's node affinity/selector

检查 Pod 的 nodeSelectoraffinity

调整亲和性规则或选择合适节点

PVC 未绑定

pod has unbound PersistentVolumeClaimsFailedBinding

kubectl get pvc -n <ns> 看状态

检查 PVC 状态和 StorageClass 配置

hostPort 冲突

didn't have free ports for the requested pod ports

检查是否有多个 Pod 抢同一个 hostPort

避免用 hostPort,改用 Service

命名空间资源配额耗尽

exceeded quota

kubectl describe resourcequota -n <ns>

调高配额或清理无用资源

💡 一个容易被误解的调度器优化特性:QueueingHint

聊点进阶的。在 Kubernetes 1.32 中,调度器引入了一个叫 QueueingHint 的 Beta 特性,默认开启。它的作用是:当集群状态发生变化时(比如有人删了 Pod 释放了资源),调度器可以通过 QueueingHint 回调函数快速判断哪些等待调度的 Pod 需要重新尝试调度,从而减少无效重试、提升调度吞吐量。

这个特性在 v1.34 中正式 GA(稳定版,默认启用) 。到 v1.36 时,SchedulerQueueingHints 特性门控已从代码中移除(因为已经 GA 了)。

📌 但请注意:QueueingHint 解决的是“资源释放后调度器响应速度”的问题,不是“资源不足时强行调度”的问题。如果你的集群确实没资源了,升级到 1.34 或 1.36 并不会让 Pod 突然就能调度成功——资源还是那些资源,requests 还是那些 requests资源不足本身要靠降 requests、清理 Pod、加节点来解决,QueueingHint 只是让资源释放后调度器反应更快而已。

预防为主:三个习惯帮你远离 Pending

  1. 给所有 Pod 配合理的 requests ——不要照抄网上的模板,根据实际压测结果来。从 100m CPU、128Mi 内存开始,观察后再调整。
  2. 监控集群的 requests 水位,而不是实际使用率——在 Prometheus 里加一条 sum(kube_pod_container_resource_requests) 的告警,比只看 CPU 使用率靠谱得多。
  3. 用 PriorityClass 保核心业务——给关键服务配高优先级,资源紧张时能抢占低优先级 Pod 的资源。

总结一下

Pod 因资源不足卡 Pending,核心就三件事:

  • 看 Eventskubectl describe pod 直接告诉你原因
  • requests:调度器认的是 requests,不是实际使用
  • 三选一:降 requests、清僵尸 Pod、加节点

遇到类似问题,按我给的 5 条急救命令走一遍,基本能搞定 90% 的情况。剩下的 10%,欢迎在评论区交流——你在生产环境还遇到过哪些奇葩的 Pending 原因?

觉得有用的话,转发给团队里的小伙伴,下次半夜被报警吵醒的时候,至少有个现成的排查手册。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐