【背景】 我维护一个 40+ 业务 Pod 的自建 K8s 集群(1.28,cgroup v2,节点 16C64G,业务与批处理混部比例约 7:3)。去年我们做过一次「全量 Guaranteed」改造:把所有生产服务的 requests 对齐 limits,禁止 Burstable 进生产命名空间。改造后确实再没出现过 OOMKilled,但代价也很明显——节点长期跑在 60~70% 的 request 水位,实际利用率(按 cgroup memory.current 算)经常在 45% 以下,一年下来至少多扛了 3 台节点的成本。今年新来的一位同事反向提意见:「Java 应用堆外内存天然有毛刺,卡死 limits 等于自己给自己埋雷,不如放开 Burstable + 节点级 headroom」。这两种路线我在社区和同行里都见过,没有标准答案,所以想借这里把大家的真实取舍摊开聊聊。
【常见分歧 / 几种做法】
- 全量 Guaranteed(requests=limits,禁止 Burstable):卖点是 OOMKilled 可预测、驱逐顺序清晰(QoS 排序下 Guaranteed 最抗压)、调度器行为最可推演。代价是内存类毛刺(JVM heap outside、Go 的 GOGC 回收前峰值、Node.js 的 buffer 增长)容易撞 limits,且 request 水位虚高导致装箱率低。
- 允许 Burstable,requests 按 P50/P75 定、limits 按历史 P99 加 30% headroom:装箱率高、应用不容易被杀。代价是节点内存压力下驱逐顺序不可控,出事故时「谁先被杀」基本靠运气,排障成本上升。
- 混合策略:核心交易链路 Guaranteed,边缘/批处理 Burstable 甚至 BestEffort,按 SLO 等级分命名空间治理。工程上最灵活,但策略本身要有人持续维护,团队规模小的话容易退化成「没人管」。
【我的倾向与理由】 我目前站 3(混合),但有一个硬约束:Guaranteed 只给「P99 直接对用户可见、且 SLO 有 burn rate 告警」的服务,其余一律 Burstable + 明确的 limits 上限(limits 必须存在,只是可以大于 requests)。理由有三:
- 驱逐顺序的可推演性只在「事故时」才有价值,为它全年多付 30% 节点成本,ROI 算不过来;
- Java 服务放开 requests 后,把 headroom 从「调度期」挪到「运行期」,配合 VPA(只开推荐模式,别开自动)观察 2~4 周数据再定 requests,比拍脑袋对齐 limits 更省钱;
- 但 limits 缺失的 BestEffort 内存型负载,在混部节点上就是定时炸弹,这条线我绝不放开。 同时我承认一个短板:我们还没把「节点内存压力分级(cgroup memory.pressure 的 PSI 指标)→ 驱逐预案」做成自动化的,所以 Guaranteed 的「抗压优势」目前只在文档里,没在演练里验证过。
【想听大家在什么场景下怎么选】
- 你们是纯云厂商托管集群还是自建?节点混不混批处理?这两点我猜会直接决定大家选 1 还是 2;
- 选「全量 Guaranteed」的团队:你们怎么定的 limits 值?P99 加多少 headroom?有没有因为毛刺被自己 limits 杀掉的案例?
- 选「Burstable」的团队:出过「内存压力下驱逐顺序失控」的事故吗?事后是怎么补的(PodPriorityClass?驱逐白名单?还是干脆改回 Guaranteed)?
- Go / Rust 这类内存行为更可控的语言栈,你们敢不敢把它们放到比 Java 更激进的 QoS 档位?
- VPA 只开推荐模式、人工 review 后落 request 这条路径,有人跑通过吗?周期和坑在哪里?
欢迎贴真实数据(节点规格、语言栈、request/limit 配比、事故复盘),空对空的「建议按业务分级」我就不引了。
0赞
0收藏
0楼层