Kubernetes 通过资源 requests 决定 Pending Pod 能否找到节点

前言: 前几天给预览服务接上了外部入口,我又把 Deployment 的副本数从一份调成两份,想让更新时更从容。结果新 Pod 一直是 Pending ,而节点监控看着并没有跑满。我第一反应是等一会儿,或者把容器的内存上限调小。翻了 Kubernetes 的资源管理文档才发现:调度器决定"能不能安排座位",看的主要是资源 requests。于是问题来了:没有明确写需求时,我们怎么安排资源?写了 requests 后,为什么机器看着空闲,Pod 还是进不去?

这篇只讲 CPU、内存请求量与调度的关系,不展开自动扩容和驱逐。

一. 第二个副本来了,却一直没落座

  1. 场景很普通:预览服务原来只有一个副本,外部请求已经能通过 Ingress 找到它。现在把 replicas 改成 2。Deployment 的确创建了第二个 Pod ,但 kubectl get pods 里它一直显示 Pending ,NODE 一栏也是空的。

  2. 我差点把这事当成容器启动慢。可如果连节点都没选出来,镜像拉取、应用初始化和就绪检查还没轮到。先用 kubectl describe pod <pod-name> 看末尾的 Events ,若出现 FailedScheduling 和 Insufficient cpu 或 Insufficient memory,问题就在"安排到哪台机器"这一步。Pending 也可能由别的原因造成,事件内容比状态名更有用。

图里红色的停顿发生在选节点时;不要一上来就去翻应用日志。

二. 我先看实时用量,却越看越困惑

  1. 最自然的反应是打开监控:节点的 CPU 才用了不到一半,为什么放不下一个小服务?于是我想把 limits.cpu 调小,给它"腾空间"。这个办法容易找错对象:limits 管容器运行后最多能用多少,调度时要比较的是它申请了多少。

  2. 如果完全不写资源需求,应用可能先被放进去,到了业务高峰又和别的服务抢资源。稍微好一点的办法,是开发者口头约定"这个服务大概吃半核",但调度器看不到聊天记录。我们得把最低需求写进清单,让每个副本都带着自己的"座位申请"。

我先看的东西 它回答的问题 这次为什么不够
节点实时使用率 此刻实际用了多少 不能代表已经承诺给其他 Pod 的资源
limits 容器运行后的上限 不是本篇要调整的调度需求
requests 新 Pod 至少申请多少 调度器会拿它和节点可分配余量比较

重点看"已承诺"与"正在使用"两条线:它们描述的不是同一件事。

三. 给新副本写一张明确的资源申请单

  1. Kubernetes 在调度时,会把每个候选节点的可分配资源,与已经安排在该节点上的 Pod 请求量相比较。新 Pod 的请求量加进去仍能容纳,才有机会被选中。所以 requests 更像订会议室时预留的座位:有人暂时没坐满,也不能把承诺过的位置再卖一遍。

  2. 在常见的容器级写法里,给 preview 容器声明 resources.requests.cpu: 250m 和 resources.requests.memory: 128Mi,意思是它申请四分之一核 CPU 和 128 MiB 内存。一个 Pod 有多个普通容器时,调度所需的资源通常会合计它们的请求量。这里的数字只是演示格式,真实值要结合应用观测来定。

text 复制代码
没有明确请求量:大家凭经验估计,调度器缺少应用需求的信息
写了请求量:每个副本带着资源申请单,逐个找容得下它的节点
新问题:申请量写得过大,机器即使暂时很闲,也可能放不进去

绿色节点能装下新申请,红色节点不能;这张图说的是调度判断,不是实时利用率。

四. 用一份清单看清 requests 写在哪里

  1. 下面只保留和本文相关的配置。假设集群已有 preview 镜像,给容器写请求量,同时给它一个运行上限。replicas: 2 表示要安排两个副本,每个副本都各自申请资源;不是两份共用 250m。清单用的镜像名是占位值,使用时要替换。
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: preview
spec:
  replicas: 2
  selector:
    matchLabels:
      app: preview
  template:
    metadata:
      labels:
        app: preview
    spec:
      containers:
        - name: preview
          image: example.invalid/preview:v1 # !!! 注意:换成实际可拉取的镜像
          resources:
            requests:
              cpu: 250m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 256Mi
  1. 检查时先执行 kubectl get pods -o wide,确认哪个 Pod 没有节点,再执行 kubectl describe pod <pod-name> 看它的 Requests 和 Events 。接着用 kubectl describe node <node-name> 看节点的 Allocatable 与 Allocated resources 。这里不能只拿物理内存容量做减法:系统和 Kubernetes 组件要留资源,调度使用的是可分配口径。

  2. 本文清单依据官方字段编写,没有在这篇文章里实际部署并制造资源不足 ,因此不把示意中的 FailedScheduling 当成本机实验结果。你的集群是否报资源不足,还取决于节点配置、其他工作负载和准入策略。

这次要对照两边:Pod 请求了多少,节点已经承诺了多少、还剩多少。

五. 写了申请单,仍可能要调整座位安排

  1. requests 写得太小,Pod 可能顺利启动,但高峰时资源压力仍会出现;写得太大,则可能一直 Pending 。处理时先确认应用真实需求,再考虑调低请求量、减少同时运行的副本,或者给集群增加容量。别为了让状态变绿,就把数值随手改成 0。

  2. 还要分清 requests 和 limits 的时机。前者主要参与放置决策,后者影响容器运行时的约束。即使调度成功,也不保证后续永远不会 OOM 或遇到 CPU 限速。反过来,节点此刻使用率低,也不能推翻一次明确的 Insufficient cpu 事件。

  3. 回到会议室的比方:预订表写满了,即使大家此刻都出去喝咖啡,前台也不能随便把下一场会议安排进来。你可以核实谁真的需要大房间,也可以多开一间;但先要看预订表,而不是只拍一张空座位照片。

这张图的中间值不是固定推荐数字,重点是根据观察修正申请量。

六. 总结:先看申请量,再看节点余量

  1. 新 Pod 停在 Pending 时,先看它有没有分配到节点,再读 Events 。若事件明确指向资源不足,就把 requests、节点 Allocatable 和已分配请求量放在一起看。

  2. 记住一个区别就够了:requests 是调度时的资源申请,limits 是运行后的使用边界,实时使用率只是"现在用了多少"。下次扩副本失败,我会先找申请单和座位表,再决定改申请量还是扩节点。

图里先读事件,再决定要比较哪种资源;若事件不是资源不足,就别沿这条路径硬查。

本文参考: Kubernetes 资源管理 · 调试运行中的 Pod

相关推荐
用户360055579001 小时前
Kestrel_VLLM - 18 · 投机解码位级对拍收口:batch 与 M=1 一致,注意力路径与 CPU 逐字节对齐
后端
Thneonl1 小时前
git reset --hard 之后先别哭:reflog 里存着你刚弄丢的那个分支
运维·后端
弦儿1 小时前
后端 Web 实战:员工管理模块(多表关系 + 多表查询 + 分页查询 + 条件分页)
后端·sql
王中阳Go1 小时前
网页上答对、接口里答错:同一个知识库,差的不是模型是入口
人工智能·后端·架构
郝琳1 小时前
真实 flaky 18.3%,CI 报 0%,让 Agent 修测试,它删掉了被测的变量
后端·ai编程
子兮曰1 小时前
Bun v1.4.4 实测:2,025 个文件,bun fmt 2.6 秒跑完
前端·后端·bun
萧瑟余晖2 小时前
Spring Data JPA高级特性详解
后端·spring
利威尔xu2 小时前
Go IM 源码实战(2):WebSocket 握手 11 步,少 1 个参数连不上
后端·websocket·微服务·面试·golang
轻舟技术A3 小时前
Rust 重写 Pi:会话启动耗时降 91%,就绪内存少 78%
开发语言·后端·rust