3.1 资源请求与限制:调度决策的数字依据


3.1 资源请求与限制:调度决策的数字依据

资源请求(requests) 是容器向调度器申报的最低资源量,调度器据此判断节点能否容纳它;资源限制(limits) 是容器运行时的资源封顶线,超过即被限流或回收。两个数字共同决定 Pod 被放去哪里、资源紧张时谁先被驱逐。

进入选址工位之前,先补上它的度量衡。调度器做决策时不看节点的实际占用率,只看每份声明的报价——requests 就是报价单。这一节把 requests 与 limits 的分工、QoS 等级的判定、以及"不写资源等于埋雷"的机制讲透,3.2 节的两段式选址才有数字可算。

报价单与封顶线

给 orders-api 的容器补上资源声明,是 1.2 节那份 YAML 的第一次升级:

spec: template: spec: containers: - name: orders-api image: orders-api:1.4.2 resources: requests: # 报价单:给调度器看的 cpu: 500m # 500 毫核,即半个 CPU 核 memory: 512Mi # 512 MiB 内存 limits: # 封顶线:给容器运行时看的 cpu: "1" # 最多用满 1 个核,超了被限流 memory: 1Gi # 最多 1 GiB,超了被终止

两栏数字服务两套系统,混用不得:

声明 谁在读 用途 超额后果
requests.cpu 与 memory 调度器 累加判断节点可分配量是否够 不够则不选该节点
limits.cpu 容器运行时 CPU 配额,超用被节流 进程变慢,不重启
limits.memory 内核与 kubelet 内存硬顶,超用触发回收 容器被终止,退出码 137

CPU 的单位值得记一下:500m 是 0.5 核(milli-core),写 "1" 表示整整一核——纯数字必须加引号,否则 YAML 会把它当浮点数解析,这是新手高频报错。内存用 Mi 与 Gi 这类二进制单位,与云主机的十进制 GB 略有出入,容量规划时留 5% 换算余量。

关键认知再强调一次:调度器只认 requests。假设一台节点有 4 核可分配,上面已经跑了报价合计 3.5 核的容器,哪怕它们实际只用了 0.3 核,一个报价 1 核的新 Pod 也不会被放到这台节点上——账面满了就是满了。反过来,不写 requests 的容器报价为零,调度器认为它不要钱,会把它塞进任何节点,这就是下一小节的雷。

QoS:不写资源等于自弃

requests 与 limits 的组合方式,决定 Pod 的服务质量等级(QoS)。等级本身不写在 YAML 里,由集群按规则推定:

  • Guaranteed(最高):每个容器 requests 与 limits 完全相等。集群承诺你申报的量,资源紧张时最后被动;
  • Burstable(居中):声明了资源但 requests 不等于 limits。平时可突用到 limits,紧张时按超用比例先被撵;
  • BestEffort(最低):完全不写 resources。调度白给、紧张时最先被驱逐,命运等同于野草。

用一条命令查看现网 Pod 的等级(输出里的 QoS_CLASS 就是判定结果):

kubectl get pods -n production -o custom-columns=NAME:.metadata.name,QOS:.status.qosClass # NAME QOS # orders-api-6d9f7c8b5-2vxk7 Burstable # debug-tools BestEffort

节点内存耗尽时,kubelet 的驱逐逻辑按等级从低到高清场:先杀 BestEffort,再按超用比例杀 Burstable,Guaranteed 最后被动。这意味着资源声明同时也是一份"被杀顺序投票":写得越认真,活得越靠后。

⚠️ 常见坑:认为"不写 resources 就是让它随便用,性能更好"。实际效果正相反——报价为零的 Pod 会被随机塞进本已拥挤的节点,一旦内存吃紧它是第一个被驱逐的,表现为"莫名重启、飘忽不定"。生产环境应当用命名空间级的默认值机制(LimitRange)给漏写的声明兜底。

演练:一次资源不足的调度实验

背景:团队怀疑某测试集群"明明很闲却调度不进去",我们复现并解释这个现象。

操作:先看节点的账面,再故意提交一个报价超大的 Pod:

# 第一步:看节点可分配量与已申报量(requests 的账面) kubectl describe node node-a | grep -A4 "Allocated resources" # Allocated resources: # Resource Requests Limits # cpu 3500m / 4000m ... # memory 9Gi / 15Gi ... # 账面 CPU 只剩 500m 可申报 # 第二步:提交一个报价 2 核的 Pod kubectl run big-worker --image=busybox:1.36 --requests=cpu=2 -- sleep 3600 # 第三步:观察它的状态与事件 kubectl get pod big-worker # NAME READY STATUS RESTARTS AGE # big-worker 0/1 Pending 0 30s kubectl describe pod big-worker | tail -3 # Events: # Warning FailedScheduling ... 0/6 nodes are available: # 6 Insufficient cpu. preemption: 0 Preemptible.

结果:Pod 常驻 Pending,事件一语道破:6 台节点全部 CPU 不足(Insufficient cpu)。

解读:节点实际 CPU 占用率可能不到 30%,但账面申报已达 3500m。调度器按报价单工作,报满即拒。这类"账满机闲"要么是存量 Pod 报价虚高(压测时调大后没改回),要么是节点从未做过报价盘点。处置方向有三个:下调虚高声明、扩容节点、或给该工作负载降报价。

变式:把 requests 从 2 核改成 400m,Pod 立刻调度成功——同一节点、同一时刻,报价决定命运。若该任务可中断,还可声明为低优先级并允许被抢占,让调度器在账满时挤走别人的能力换来自己的入场,代价与规则在第7章的资源治理话题中再展开。

本节要点回顾

  • 两套系统各读一栏:调度器只认 requests,运行时只认 limits,混写混读必出事故;
  • 账满即拒:节点资源判定看申报总和,不看实际占用,"很闲却调不进"先查账面;
  • QoS 三等级由声明推定:requests 等于 limits 是 Guaranteed,不写是 BestEffort,等级决定被驱逐顺序;
  • CPU 超限被节流、内存超限被终止:两种超额的表现与处置完全不同,退出码 137 是内存击穿的签名;
  • 生产必写资源:用 LimitRange 兜底,别让任何声明裸奔进集群。

报价单备好,下一节进入调度器本部:它如何用过滤与打分的两段式,在候选节点中为每个 Pod 挑出唯一归宿。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U