Kubernetes Tips:Requests & Limits(别踩坑版)
把 CPU/内存 requests 与 limits 讲清楚:调度、限流、OOMKilled、QoS、HPA/VPA 与容量规划。
资源配额是让集群“看起来很稳”或“莫名其妙不稳”的最快开关之一。很多线上抖动并不是代码突然变差,而是 requests/limits 写法不合理、或者和 HPA/节点容量的假设不一致。
一句话结论(先记住这个)
- 所有容器都要设置 requests(CPU + 内存):调度器用它来决定 Pod 放到哪里。
- CPU limits 要谨慎:CPU 上限会触发 CFS quota,负载一上来可能被节流,延迟飙升但你还以为“CPU 不高”。
- 内存 limits 很重要:没有内存上限,单个容器可能把节点内存吃穿,拖垮同节点其他业务;有上限则要面对 OOMKilled。
- HPA 默认基于 requests:requests 不准,自动扩缩容就会不准。
调度器到底看什么
Kubernetes 调度(bin packing)主要基于 requests:
- 你写的
requests.cpu、requests.memory决定这个 Pod “占用”多少资源用于放置。 limits主要影响运行时资源限制(尤其 CPU 限流、内存 OOM)。
如果你不写 requests:
- CPU request 默认等于 0
- 内存 request 默认等于 0
这种 Pod 很容易被调度进任何节点,但也更容易成为“噪音邻居”:流量突增时它会把资源抢走,导致同节点业务整体抖动。
一个靠谱的起步模板(很多服务够用了)
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
memory: "512Mi"
这个组合背后的思路:
- requests 让调度与容量规划可预测
- memory limit 保护节点不被单个容器拖死
- 先不设 CPU limit(或留足余量),避免节流导致的延迟尖刺
CPU:requests 用于放置,limits 用于节流(并可能伤害延迟)
CPU 是“可压缩资源”。当 CPU 不够时,进程通常不会崩,而是变慢。
- requests.cpu:调度和扩容计算的基础(很多指标会以它为分母)
- limits.cpu:运行时的 CFS quota 上限,超过就会被 throttling(节流)
一个很典型的坑:
你看到 CPU usage 似乎不高,但 p95/p99 延迟突然升高;原因可能是被节流,尤其在突发流量或 GC/加密握手等瞬时 CPU 峰值场景。
建议:
- 一定保证
limits.cpu >= requests.cpu - 对突发型服务不要把 CPU limit 设得太贴近 requests;否则“短时间需要多一点 CPU”都拿不到
内存:limits 保护节点,但要接受 OOMKilled 的现实
内存不是可压缩资源。容器超过内存上限,通常会被内核 OOM killer 终止,Kubernetes 显示 OOMKilled 并重启。
排查 OOM 的第一步:
kubectl describe pod <pod> | rg -n "OOMKilled|Killed"
常见应对路径:
- 提高
limits.memory(同时评估成本与密度) - 优化程序内存(缓存策略、泄漏、对象生命周期)
- 引入 VPA 做推荐(即使不自动应用也很有价值)
requests/limits 还会影响 QoS(驱逐优先级)
Kubernetes 会按 requests/limits 计算 Pod 的 QoS:
- Guaranteed:每个容器 CPU/内存都设置 requests 与 limits,且相等(
requests == limits) - Burstable:设置了部分 requests/limits,但不满足 Guaranteed 条件
- BestEffort:完全没有 requests/limits
在节点内存压力下,通常:
BestEffort 先被驱逐,其次 Burstable,最后 Guaranteed。
快速查看某个 Pod 的 QoS:
kubectl get pod -n <ns> <pod> -o jsonpath='{.status.qosClass}{"\n"}'
HPA 的分母问题:CPU 利用率通常除以 requests
很多集群里 HPA 的 CPU utilization 算法近似是:
当前 CPU 使用量 / CPU requests
因此:
- requests 写太高:HPA 认为“利用率很低”,扩容会偏慢
- requests 写太低:HPA 认为“利用率很高”,扩容会偏激进
如果你发现:
- 峰值时扩容不及时
- 或者平时无意义扩容/缩容抖动
第一优先级就该回头检查 requests 是否贴近“真实稳定负载”。
还要考虑节点 Allocatable(不是 Capacity)
节点的物理资源(Capacity)并不等于可分配给 Pod 的资源(Allocatable)。系统预留、kubelet 预留、驱逐阈值都会吃掉一部分。
排查“理论上能放下,实际放不下”:
kubectl describe node <node> | rg -n "Allocatable|Allocated resources|Capacity"
kubectl get events -A --sort-by=.lastTimestamp | rg -n "Evicted|MemoryPressure"
Sidecar / initContainer:别忘了隐藏成本
很多生产 Pod 带 sidecar:
- service mesh(Envoy)
- 日志/监控 agent
- 安全组件
这些容器同样需要 requests/limits,并且会显著改变 Pod 的总资源画像。只给主容器配资源,常常会让你“以为 300Mi”,实际 Pod 轻松 600Mi+。
initContainers 也要注意:
- 迁移、warmup、下载等行为可能很吃 CPU/内存
- 大规模滚动升级时,initContainer 的峰值会叠加,可能造成节点压力
用 LimitRange / ResourceQuota 做“护栏”
为了避免团队成员忘记写 requests/limits,常见做法是用:
- LimitRange:提供默认值与 min/max,防止极端配置与“全空”
- ResourceQuota:限制命名空间总配额,并可强制要求 requests/limits
示例(仅示意,按你们平台标准调整):
apiVersion: v1
kind: LimitRange
metadata:
name: defaults
namespace: app
spec:
limits:
- type: Container
defaultRequest:
cpu: 100m
memory: 128Mi
default:
memory: 512Mi
选数值的实战方法(可落地的闭环)
- 先用保守模板上线(requests 写上;memory limit 写上;CPU limit 先不写)。
- 观察 1–2 周真实流量下的:
- 内存峰值(p95/p99)
- CPU 峰值与是否节流(如果设了 limit)
- OOMKilled、重启频率
- HPA 的扩缩容节奏(是否晚/是否抖)
- 迭代:
- 内存经常逼近上限:提高 limit 或优化内存
- 延迟尖刺:检查 CPU throttling、requests/limits 的比例
- HPA 不准:先修 requests,再谈 HPA 参数
- 引入 VPA 做推荐(Off/Initial 先走一段),逐步自动化。
最后一句话(真正的底线)
先把 requests 写对,再把 memory limit 写上;CPU limit 只有在你能接受并量化节流影响时再加。
参考链接
FAQ
Q: requests 要等于 limits 吗? A: 只有需要强保证时才这样设置;多数服务用 Burstable 即可。
Q: 为什么会 OOMKilled? A: memory limit 太小或峰值过高,调整 limit 并结合监控。
Q: 限制会影响调度吗? A: 调度主要看 requests;limits 用于运行时约束和驱逐压力。