背景
在 K8s 集群里,requests / limits 怎么填,直接决定了调度器的 bin-packing 效率和 Pod 运行时的 QoS 等级。我见过太多团队在这件事上"一刀切":
- 有些团队(尤其金融、电商中台)直接
requests = limits,追求全量 Guaranteed QoS,理由是"可预测、不会 OOM、不会被邻居挤"; - 有些团队(偏互联网、流量波动大)只设
requests,limits留空或拉高,理由是"limits 在 CPU 上会触发 CFS bandwidth throttling,反而把 P99 打上去"; - 还有些团队干脆不配 limits,全靠 LimitRange 兜底,理由更简单——"设了 limits 就少了一次 OOM 的'优雅降级'机会"。
三种姿势都有人跑,线上都没"炸",但各自的代价不同。
常见分歧(我总结的三派)
派系 A:全量 Guaranteed(requests = limits)
- 优点:调度器 100% 可预测;节点上不会有两个 Pod 抢 CPU;OOM 时 kubelet 优先杀 BestEffort/Burstable,Guaranteed Pod 几乎不会被误杀。
- 代价:节点装箱率上不去(实际利用率常年在 40–55%);突发流量时 CPU 被 throttle,P99 反而比不设 limits 更差(cgroup v1 的
cpu.cfs_quota_us粒度是 100ms 一个周期,throttle 期间整个 Pod 所有线程都卡住);内存 limits 一旦设了就是硬上限,GC 尖峰直接 OOMKilled,没有"弹性缓冲"。
派系 B:Burstable(requests < limits,limits 留 1.5–2× 余量)
- 优点:日常按 requests 调度,突发时能"借"邻居的 headroom;CPU 不会被 throttle(只要不超 limit);内存有 buffer,JVM Full GC 时不至于瞬间 OOM。
- 代价:极端情况下(整节点所有 Pod 同时突发)还是可能触发 OOM;调度器只看 requests,节点实际负载可能远超 requests 总和("超卖");排查性能问题时多一个变量。
派系 C:只设 requests,不设 limits
- 优点:CPU 永远不 throttle(cgroup 无
cpu.max限制);内存无硬顶,GC 有充分空间;最简单。 - 代价:一个内存泄漏的 Pod 可以拖垮整节点(cgroup v1 下 OOM killer 可能杀错人);调度器看到的 requests 和实际用量可能差好几倍;多租户场景下"吵闹邻居"问题严重。
我的倾向:按 workload 类型分治,不要一刀切
我目前的实践(K8s 1.28+,cgroup v2,Java + Go 混合工作负载):
| 类型 | 策略 | 理由 | |------|------|------| | 在线核心服务(Java,JVM) | requests = 稳态 P95 内存 × 1.2;limits = 稳态 × 2.0(Burstable) | JVM 堆外 + Metaspace + DirectBuffer 波动大,硬 limit 在 Full GC 时必 OOM | | 在线核心服务(Go/Rust) | requests = limits = 压测 P99 | Go GC 内存可预测,CPU 敏感,Guaranteed 避免 throttle | | 批处理 / 大数据 ETL | 只设 requests,不设 limits | 跑完就结束,允许"借"节点资源,不在线 | | 中间件(Kafka/ZK/etcd) | requests = limits,且单独 DaemonSet / 独占节点 | 数据一致性 > 弹性,且需要磁盘 I/O 隔离 | | 无状态 Web / API | requests 按 P95,limits 不设或设 3× | 弹性优先,HPA 兜底容量 |
关键前提:cgroup v2 + CPU Manager 的 static policy 只给 Guaranteed Pod 绑核。如果集群还在 cgroup v1,CPU throttling 的行为和 v2 不完全一致(v2 的 cpu.max 粒度更细),这个策略要重新校准。
想听大家
- 你们线上实际用的是哪种策略? 如果全量 Guaranteed,节点利用率大概多少?CPU throttling 的 P99 影响量级是什么?
- JVM 服务到底要不要设内存 limit? 我见过设了 limit 后 Full GC 期间被 OOMKilled 的 case(堆外内存 + GC overhead 超出 limit),也见过不设 limit 后一个 Pod 泄漏拖垮整节点(32G 节点上跑了 6 个 Java Pod)。你们怎么平衡?
- cgroup v1 → v2 迁移后,你们观察到 CPU throttling 行为有变化吗? v2 的
cpu.max和 v1 的cpu.cfs_quota_us在 throttle 粒度上有区别吗?有没有实测数据? - 多租户 / 多团队共享集群的场景,你们有没有遇到过"一个 Burstable Pod 突发把同节点 Guaranteed Pod 的 P99 打上去"的情况?怎么做的 I/O 和 CPU 隔离?
欢迎贴你们的 resource YAML 配置片段、kubectl top / node_exporter 的实际数据。空谈"应该设 limits"的就不必了——我想知道你们实际怎么配、配完之后观察到了什么。