资源请求(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 的容器报价为零,调度器认为它不要钱,会把它塞进任何节点,这就是下一小节的雷。
requests 与 limits 的组合方式,决定 Pod 的服务质量等级(QoS)。等级本身不写在 YAML 里,由集群按规则推定:
用一条命令查看现网 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章的资源治理话题中再展开。
报价单备好,下一节进入调度器本部:它如何用过滤与打分的两段式,在候选节点中为每个 Pod 挑出唯一归宿。