建议收藏 | 遇到 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 componentkube-scheduler托管集群用户注意如果你用的是云厂商的托管集群如 EKS、AKS、TKE 等控制面由云厂商管理用户通常无法直接查看调度器 Pod 的状态。建议通过云厂商控制台或工单确认调度器是否正常。# 5. 筛选所有 Pending 的 Pod看看是不是大面积沦陷 kubectl get pods -A --field-selectorstatus.phasePending这几条命令跑完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 cpu或Insufficient memory。为什么kubectl top nodes显示资源充足但 Pod 还是 Pending这个问题太常见了值得单独拎出来说。kubectl top nodes显示的是节点上所有 Pod 当前瞬时的实际资源使用量总和它依赖 metrics-server 从 kubelet 的/stats/summary端点采集数据。而调度器做决策时看的是requests总和——也就是 Pod 向集群“预订”的资源量。这两者之间的差距可能非常大。一个 Podrequests: 2 CPU但实际只用 0.1 CPU在top命令里只显示 0.1但在调度器的账本里已经扣掉了 2 个核。所以你会看到top显示资源充足但调度器依然报Insufficient。我的建议排查资源问题时把kubectl describe node和kubectl 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那是污点没容忍或者是didnt match Pods 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 的 CPUrequests总和已经超过了节点的可分配容量。新 Pod 自然进不来。我踩过的坑有一次我盯着kubectl top nodes看了半天每个节点 CPU 使用率都不到 40%死活想不通为什么调度失败。后来才发现是某个团队给所有微服务都配了requests: 2相当于 2000m把整个集群的requests配额撑爆了但实际负载很低。从那以后我养成习惯——排查资源问题先看requests再看实际使用。第三步揪出“占着茅坑不拉屎”的 Pod找到资源最紧张的节点后看看是哪些 Pod 在消耗requestskubectl describe node node-name | grep -A 10 Non-terminated Pods或者更精确一点看每个 Pod 的requestskubectl get pods -n namespace -ocustom-columnsNAME:.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-selectorstatus.phaseSucceeded方案三扩容节点如果业务确实需要这么多资源那就加节点吧。云上环境可以用 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 taintkubectl describe node看 Taints给 Pod 加tolerations或移除节点污点节点亲和性不匹配didnt match Pods node affinity/selector检查 Pod 的nodeSelector和affinity调整亲和性规则或选择合适节点PVC 未绑定pod has unbound PersistentVolumeClaims或FailedBindingkubectl get pvc -n ns看状态检查 PVC 状态和 StorageClass 配置hostPort 冲突didnt have free ports for the requested pod ports检查是否有多个 Pod 抢同一个 hostPort避免用hostPort改用 Service命名空间资源配额耗尽exceeded quotakubectl 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给所有 Pod 配合理的requests——不要照抄网上的模板根据实际压测结果来。从 100m CPU、128Mi 内存开始观察后再调整。监控集群的requests水位而不是实际使用率——在 Prometheus 里加一条sum(kube_pod_container_resource_requests)的告警比只看 CPU 使用率靠谱得多。用 PriorityClass 保核心业务——给关键服务配高优先级资源紧张时能抢占低优先级 Pod 的资源。总结一下Pod 因资源不足卡 Pending核心就三件事看 Eventskubectl describe pod直接告诉你原因看requests调度器认的是requests不是实际使用三选一降 requests、清僵尸 Pod、加节点遇到类似问题按我给的 5 条急救命令走一遍基本能搞定 90% 的情况。剩下的 10%欢迎在评论区交流——你在生产环境还遇到过哪些奇葩的 Pending 原因觉得有用的话转发给团队里的小伙伴下次半夜被报警吵醒的时候至少有个现成的排查手册。