背景
做了 8 年嵌入式,最根深蒂固的一条信念是:资源必须确定性。DMA buffer 多大、任务栈多深、定时器周期多少,全部在编译期/启动期锁死,运行时不允许"弹性"——因为"弹性"在硬实时系统里就是"不确定",不确定就是 bug。
到了 K8s 世界,资源管理突然变成了一套"软承诺"体系:
requests:调度时保证你能"拿到"的量(但运行中可以被压)limits:运行时硬上限(超了就 OOMKill / CPU throttle)- 两者组合出三种 QoS:Guaranteed(requests=limits)、Burstable(只设 requests 或 requests<limits)、BestEffort(都不设)
这和嵌入式里"硬隔离"的直觉完全相反——K8s 默认是"共享池 + 软限",而且 Node 内存压力下驱逐顺序严格按 QoS 排序:BestEffort 先走、Burstable 次之、Guaranteed 最后。
常见分歧 / 几种做法
A 派:核心服务一律 Guaranteed(requests=limits)
- 理由:驱逐优先级最高,Node 内存紧张时"我的服务最晚死";CPU 不被 throttle(因为 limit=request,不存在 burst 超出的概念)
- 代价:资源利用率被锁死。一个平时只用 20% 内存的服务,如果 requests 设了 512Mi,那 512Mi 就"占着茅坑",Node 调度器认为你"需要"它,但实际你可能 128Mi 就够。多 Pod 节点上,Guaranteed Pod 越多,碎片化越严重。
B 派:只设 requests,不设 limits(Burstable)
- 理由:给峰值留余量。比如一个批处理 Pod,平时 256Mi,跑大批量时飙到 1Gi,设 limit=512Mi 直接 OOMKill,不如不设 limit 让它"自然用"
- 代价:OOM 风险完全暴露。Node 内存耗尽时,Burstable Pod 被驱逐的顺序取决于"超 requests 的比例",不是"谁更重要"——你无法控制"先死谁"
C 派:不设 limits(或 BestEffort),靠 HPA + 副本数兜底
- 理由:K8s 哲学就是"无状态 + 可重启",单 Pod 死了 HPA 补一个,没必要在单 Pod 级别做硬限制
- 代价:Node 级 OOM 时 BestEffort 最先被杀,而且 CPU throttle 完全不受控(没有 limit 约束,cgroup 不限制,但 Node 整体 CPU 抢占时你的 Pod 最先被调度器"冷落")
我的倾向与理由
我线上实践是分层策略,但不是按"服务重要性"分,而是按资源行为特征分:
- 内存行为可预测的服务(如 API 网关、订单查询):Guaranteed。这类服务内存曲线很平,requests 设准了就是 limits,弹性空间很小,不如锁死换驱逐优先级。
- 内存有尖峰的服务(如报表导出、ML 推理):Burstable,requests 设 P50,limit 设 P99+20% 余量。关键:给这类 Pod 打
priorityClassName,用 Priority 而不是 QoS 来控制驱逐顺序——因为 Burstable 之间驱逐看"超 requests 比例",但 Priority 是 Node 级硬排序,更可控。 - Dev/Canary/实验 Pod:BestEffort 或极低 requests,让它们"该死就死",不占生产资源。
核心原则一句话:QoS 决定"Node 级 OOM 时谁先走",Priority 决定"驱逐时谁更晚走",两者是不同维度,别混用。
还有一个嵌入式视角的"反直觉":在 K8s 里,limits 不是"保护你的上限",而是"限制你的弹性"。嵌入式里你设 limit 是为了"别越界",K8s 里你设 limit 之后,超出的那部分"本来可以用的资源"直接变成"OOMKill 触发器"。这个语义差异,很多从传统运维过来的人没意识到。
想听大家
- 你线上生产集群里,Guaranteed / Burstable / BestEffort 的比例大概是多少?这个比例是"设计出来的"还是"长出来的"?
- 有没有因为 QoS 策略选错,导致"该保的服务先被驱逐、该让的服务还活着"的事故?具体是什么场景?
- Burstable Pod 的驱逐顺序(按超 requests 比例)你在生产里验证过吗?和文档描述一致吗?
- 如果你只能给新团队定一条 QoS 规则(不能分情况),你会定哪条?为什么?
欢迎贴你的 kubectl get pods -o wide 里 QoS 分布截图(脱敏),或者 Node 驱逐日志片段,一起对一对。
0赞
0收藏
0楼层