深色模式
资源请求/限制与 QoS 等级
摘要:本文面向负责容量、稳定性与成本的 SRE,讲解 Pod 的
requests/limits如何决定 QoS 等级,以及 QoS 如何影响节点内存压力下的驱逐顺序与 OOM 行为。覆盖 Kubernetes v1.28+。
适用版本与前提
- Kubernetes:v1.28+
- 工具:
kubectl - 前提:理解 Pod 调度与 kubelet 驱逐机制
背景与问题
“为什么同一个节点上,资源紧张时先被杀的是我的 Pod 而不是别人的?”答案藏在 QoS(Quality of Service)里。Kubernetes 在创建 Pod 时根据 requests/limits 给它分配一个 QoS 等级,这个等级决定了两件事:调度时按什么算账,以及节点资源耗尽时被驱逐的优先级。配错 requests/limits,等于主动把你的服务排到了“先死”的队列。
requests 与 limits 的基本语义
requests:调度依据与最小保障。调度器按requests之和判断节点是否能放下 Pod;kubelet 保证容器至少拿到这么多资源。limits:硬上限。CPU 超限被节流(throttle),内存超限被终止(OOMKill)。
yaml
apiVersion: v1
kind: Pod
metadata:
name: qos-guaranteed
spec:
containers:
- name: app
image: nginx
resources:
limits:
memory: "200Mi"
cpu: "700m"
requests:
memory: "200Mi"
cpu: "700m"1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
注意
requests 设得过低 → 节点超卖 → 实际争抢导致节流/驱逐;limits 设得过低 → 内存稍涨即 OOMKill,CPU 稍高即被 throttle 变慢。两者都不是“越大越好”,需结合真实用量画像。
三级 QoS
Kubernetes 在 Pod 创建时分配且生命周期内不变的 QoS 等级:
| 等级 | 判定条件 | 驱逐优先级 |
|---|---|---|
Guaranteed | 每个容器 CPU、内存的 limits==requests 且均设置(init 容器同规则) | 最后被驱逐 |
Burstable | 不满足 Guaranteed,但至少一个容器有 requests 或 limits | 中间 |
BestEffort | 所有容器都无任何 CPU/内存 requests/limits | 最先被驱逐 |
版本相关
若容器只设了 limits 没设 requests,Kubernetes 会自动把 requests 补成等于 limits(CPU/内存均如此)。因此“只设 limit”实际会提升为接近 Guaranteed 的 requests 保障,这是常见误解。QoS 等级因 Pod 资源 resize 而可能冲突时,控制面会拒绝变更请求。
判定图示
驱逐与 OOM 行为
节点内存/磁盘压力时,kubelet 的 Node Pressure Eviction 按 QoS 排序:先驱 BestEffort,再 Burstable,最后 Guaranteed。同等级内,按 requests 超出实际使用量的程度排序(超出越多越先被踢)。
OOM 分数(oom_score_adj)也由 QoS 决定:Guaranteed 接近 -997(极难被 OOM),BestEffort 接近 1000(极易被 OOM),Burstable 介于两者之间并随用量调整。这意味着 Guaranteed Pod 在节点 OOM 时最后死,BestEffort 最先死。
生产危险
把关键业务设为 BestEffort(忘了写 resources)等于告诉集群“它最不重要”,节点一紧张它就第一个被杀。生产关键服务务必至少 Burstable,强保障需求用 Guaranteed(如 etcd、控制面组件)。
生产实践
- 基于真实用量设 requests:用 Prometheus/VPA recommender 观察 P95 用量,别拍脑袋。requests 贴近真实值可提升装箱率、降低超卖。
- 关键服务用 Guaranteed:对延迟/可用性敏感的组件(数据库、消息队列、控制面)设
limits==requests,避免被邻居拖累。 - limits 留余量:内存 limits 通常设为 P99 用量的 1.2–1.5 倍,避免偶发峰值 OOMKill。
- 命名空间级 LimitRange/ResourceQuota:防御“忘了配 resources 的 BestEffort 蔓延”,给默认值与上限。
- CPU 与内存区别对待:CPU 是可压缩资源(超限变慢不致命),内存不可压缩(超限即死),limits 策略应不同。
建议
kubectl get pod <pod> -o jsonpath='{.status.qosClass}' 可直接查看 Pod 实际 QoS 等级,排障时先确认它是否符合预期。
故障排查
| 现象 | 原因 | 处理 |
|---|---|---|
| Pod 被频繁驱逐(Evicted) | 节点内存/磁盘压力,QoS 低 | 调高 requests、限制邻居、扩容节点 |
| 容器被 OOMKill | 内存超限(limits 太低或泄漏) | 查 dmesg/kubectl describe OOMKilled;调 limits/修泄漏 |
| 节点 CPU 高但 Pod 变慢 | CPU throttle(limits 过低) | 提高 CPU limits 或用 VPA |
| 调度失败 | 节点无足够 requests 余量 | 降低 requests 或扩容 |
常见坑
- 以为“只设 limits 不设 requests”是省事——实际 requests 被补成等于 limits,反而抬高了调度门槛与保障级别。
- 把 HPA 基于 CPU 利用率与过低 CPU requests 组合,导致频繁震荡。
- 忽略
ephemeral-storage的 requests/limits,磁盘打满同样触发驱逐。
替代方案与权衡
- VPA(Vertical Pod Autoscaler)可自动调 requests/limits,但会重启 Pod,需配合滚动策略。
- 节点级 kube-reserved/system-reserved 预留,保障系统组件不被业务挤占,与 QoS 机制互补。
FAQ
Q:把 requests 调高有什么代价? A:降低节点装箱率(浪费资源、推高成本),但提升调度确定性与抗干扰能力。需平衡。
Q:Guaranteed 真的不会被 OOM 吗? A:优先级极高,但节点整体 OOM 且 Guaranteed 自身真正超限时仍可能被杀;它只是“最后才会死”。
参考资料
- Configure Quality of Service for Pods - Kubernetes 官方文档,访问日期:2026-10-08。
- Pod QoS Classes 概念 - Kubernetes 官方文档,访问日期:2026-10-08。
- Node Pressure Eviction - Kubernetes 官方文档,访问日期:2026-10-08。