背景
我管过从 20 节点到 200+ 节点的 K8s 集群(1.22 → 1.31),Java 服务占大头(Spring Boot 2/3,JDK 11/17)。社区里关于「CPU limit 该不该设」已经吵了好几轮(比如讨论帖 #1035 和 #69),但有一个更细、更疼的分歧,我每次容量评审都要被问,却很少被认真拆:
设了 limit 之后,requests 和 limits 的比例到底怎么定? 1:1(Guaranteed)?1:2?1:4?还是干脆不设 limit、只留 requests + cpu.weight?
这不是一个「填个数字」的问题,它直接决定了:
- 节点超卖比(Guaranteed 集群超卖比 ≈ 1,Burstable 可以 2–4×)
- P99 尾延迟的方差(Guaranteed 稳但可能"浪费",Burstable 弹性大但邻居争抢时抖)
- 故障域隔离(Guaranteed 下 OOM / CPU 抢不到会立刻暴露,Burstable 下可能"慢死")
社区里我看到的三种做法
派 A:requests = limits(Guaranteed),一刀切
- 理由:P99 可预测;cgroup 不会 throttling;调度器保证"你声明的就是你能拿到的";安全团队喜欢("一个 Pod 不会吃光节点")。
- 典型配置:Java 服务
requests.cpu=2 / limits.cpu=2,requests.mem=4Gi / limits.mem=4Gi。 - 代价:节点超卖比被压到 1,同节点 16C 只跑 4 个 4C Pod;Java GC 尖峰(Young GC 200ms 内要 4 核并发)如果 limit=2,GC 线程被 CFS 硬切 → P99 直接翻 3–5 倍(社区里已有好几篇复盘,比如 #140、#773)。
派 B:requests < limits(Burstable),给弹性留空间
- 理由:Java 服务的 CPU 需求是脉冲式的(GC 并发、批处理、突发流量),limit 太小反而 throttling;requests 保证"底线",limits 给"天花板"。
- 典型配置:
requests.cpu=2 / limits.cpu=8(1:4),或requests.cpu=4 / limits.cpu=16(1:4)。 - 代价:节点超卖比上去了,但同节点其他 Burstable Pod 同时 burst 时,CFS 的 fair share 会互相挤 → P99 方差变大;且 Burstable 在节点资源紧张时被优先驱逐(Guaranteed > Burstable > BestEffort)。
派 C:不设 CPU limit,只用 requests + cpu.weight(K8s 1.28+ / cgroup v2)
- 理由:CPU limit 本质是 CFS 周期性硬切(每 100ms 窗口),对延迟敏感服务伤害最大;cpu.weight(对应 cgroup v2
cpu.weight,范围 1–10000)是软权重,只在争抢时按比例分配,不硬切;requests 保证调度底线。 - 典型配置:
requests.cpu=4,不设limits.cpu,cpu.weight=50(默认)。 - 代价:需要 K8s 1.28+ 和 cgroup v2(1.28 之前 cpu.weight 不支持);且"不设 limit"意味着安全团队会问"一个 Pod 能不能吃光节点?"—— 答案是"不能,CFS 会公平分配,但没有硬顶"。
我的倾向(不一定对,想听大家拍)
我现在的做法是分场景,但有一个核心判据:
这个服务的 CPU 需求是「稳态」还是「脉冲式」?
- 稳态(如网关、API Server、常驻 Flink TaskManager):requests = limits(Guaranteed),因为它的 CPU 需求几乎不波动,limit 设成稳态值 + 10% 余量即可,不需要弹性。
- 脉冲式(如 Java 微服务、批处理 Job、ETL):requests < limits(Burstable,1:2 到 1:4),或者派 C(不设 limit + cpu.weight)。关键是把 limit 设成 GC 并发 + 业务峰值的 CPU 需求,而不是"拍脑袋填个 4C"。
- 绝对不能:requests=limits 且 limit < GC 并发所需核数(这是 #140 和 #773 踩的坑)。
还有一个实操细节:如果走派 B(Burstable),同节点同 namespace 的 Pod 总数要控制在能同时 burst 的范围内。比如节点 32C,你跑了 4 个 requests=4 / limits=16 的 Pod,理论上 4×16=64C > 32C,四个同时 burst 时 CFS 会按比例分配 → 每个只拿到 2C → 全部 throttling。所以 Burstable 集群的超卖比要算"同时 burst 概率",不是简单求和。
想听大家在什么场景下怎么选
- 你的生产集群里,Guaranteed / Burstable / BestEffort 的 Pod 比例大概是多少? 是安全团队强制 Guaranteed,还是开发团队自己选?
- Java 服务的 CPU limit,你是按"稳态 CPU 用量 × N"还是按"GC 并发 + 业务峰值"来定的? 有没有压测数据支撑?
- K8s 1.28+ 的 cpu.weight 有没有人在生产里实际用了? 效果怎么样?和"不设 limit"比有没有差别?(我理解 cpu.weight 本质就是"不设 limit 时的调度权重",但 K8s 层面怎么声明、怎么和 requests 配合,文档很少)
- 节点超卖比你们怎么算的? 是"requests 之和 ≤ 节点容量"(Guaranteed 思路),还是"limits 之和 ≤ 节点容量 × 超卖系数"(Burstable 思路)?
- 有没有人在生产里踩过"Burstable Pod 被优先驱逐"的坑? K8s 驱逐顺序是 BestEffort > Burstable > Guaranteed,但 Burstable 之间怎么排序(按"超用程度"还是按"存活时间")?
这个话题我抛出来是想收集实战数据,不是想"赢"。不同集群规模、不同业务形态(Java/Go/Python)、不同 K8s 版本,最优解可能完全不同。贴数据、贴配置、贴踩坑经历,比贴"我觉得应该怎样"有价值得多。
(参考:社区已有讨论帖 #1035 和 #69 聊了"该不该设 limit",本文聚焦"设了之后比例怎么定"这个更细的争点。)