资源管理是 Kubernetes 里最容易被低估、却直接影响稳定性与账单的配置。写错 requests/limits,轻则 Pod 被驱逐、服务抖动,重则节点被打爆、集群雪崩。本指南从 requests/limits 的语义 到 QoS 分级 再到 配额治理与驱逐机制,帮你建立一套既不过度配置、也不资源超卖的资源工程体系。
目录
- 1. requests 与 limits:先分清承诺与上限
- 2. CPU 与内存:可压缩与不可压缩
- 3. QoS 三类等级:Guaranteed/Burstable/BestEffort
- 4. 超额分配与节点资源水位
- 5. LimitRange 与 ResourceQuota:命名空间级治理
- 6. 驱逐机制:当节点资源告急
- 7. 内存上限与 OOM:不可压缩资源的红线
- 8. 常见坑与生产最佳实践
- 9. 总结
1. requests 与 limits:先分清承诺与上限
1.1 两者的本质区别
- requests(请求量):告诉调度器"这个 Pod 至少需要多少资源",是调度承诺。
- limits(上限):告诉 kubelet"这个容器最多能用多少",是运行时上限。
resources:
requests:
cpu: 500m # 调度时至少保证 0.5 核
memory: 256Mi
limits:
cpu: "1" # 运行时最多 1 核
memory: 512Mi # 超过即 OOMKilled
1.2 容易踩的语义混淆
| 配置 | 作用 | 常见误区 |
|---|---|---|
| requests.cpu | 调度依据 | 误以为限制 CPU |
| limits.cpu | 运行时上限 | 误以为分配保证 |
| requests.memory | 调度依据 | 不影响运行时使用 |
| limits.memory | 硬上限,超了 OOM | 误以为可无上限使用 |
一句话:requests 是"调度契约",limits 是"运行时紧箍咒"——它们管理的是不同层面的资源约束。
2. CPU 与内存:可压缩与不可压缩
2.1 两类资源的天生差异
| 资源 | 类别 | 超出 limits 的行为 |
|---|---|---|
| CPU | 可压缩 | 被节流(throttling),但进程不死 |
| 内存 | 不可压缩 | 直接 OOMKilled(强制终止) |
2.2 CPU 节流的影响
CPU limits 设得太低,Pod 会被 CFS 节流,出现"CPU 使用率没满但延迟升高"的典型现象:
容器 CPU 用到 limits → 被节流到额度内 → 请求变慢
观察:kubectl top 显示 CPU 未满,但 p99 延迟涨
2.3 内存是不可压缩的
内存没有"节流",只能被 kill。所以内存 limits 是最需要谨慎设定的参数——设太低 Pod 频繁被杀,设太高浪费配额。
一句话:CPU 超了会变慢,内存超了会被杀——写资源时的"红线"优先级,内存高于 CPU。
3. QoS 三类等级:Guaranteed/Burstable/BestEffort
3.1 三种 QoS 的判定
Kubernetes 依据 requests/limits 是否设置及是否相等 给 Pod 分 QoS 等级:
| QoS 等级 | 判定条件 | 特点 |
|---|---|---|
| Guaranteed | 所有容器 requests == limits | 最优先,几乎不驱逐 |
| Burstable | 部分容器 requests < limits 或只设 requests | 中间,可能被驱逐 |
| BestEffort | 完全没设 requests/limits | 最容易被驱逐、OOM |
3.2 QoS 对驱逐与 OOM 的影响
驱逐顺序(节点资源告急时)从最弱开始:BestEffort 最先 → Burstable → Guaranteed 最后。
OOM 场景下 kubelet 按 QoS 决定先杀谁:
节点内存不足 → BestEffort 容器先被 OOM 杀
→ Burstable 中超限的容器其次
→ Guaranteed 最后才被考虑
3.3 生产建议
- 关键服务尽量设为 Guaranteed(requests=limits),换取稳定性;
- 批量/非关键任务可 Burstable,节省成本;
- 避免 BestEffort(除非明确是可有可无的任务),否则是"待杀名单"。
# Guaranteed 示例:cpu/mem 都设且相等
resources:
requests: { cpu: "1", memory: 1Gi }
limits: { cpu: "1", memory: 1Gi }
一句话:QoS 是驱逐与 OOM 的"优先级护照"——Guaranteed 是 VIP,BestEffort 是"临时工"。关键服务别做 BestEffort。
4. 超额分配与节点资源水位
4.1 超卖(Overcommit)的由来
requests 之和常常超过节点总容量(超卖)。因为不是所有 Pod 同时用满,超卖能提高利用率:
节点 8 核,上面 Pod 的 requests 之和 = 12 核(超卖 1.5 倍)
实际高峰期只用到 6 核 → 空闲资源可再塞 Pod
4.2 超卖的风险边界
超卖太狠,节点压力升高,kubelet 触发驱逐 → 大量 Pod 被杀。需设定合理超卖比(如 CPU 1.5-2.0 倍、内存保守 1.2-1.5 倍)。
4.3 用资源水位做预警
kubectl top nodes # 当前用量
# 观察 allocatable vs allocated vs 实际使用
建议监控:节点 request 总量 / allocatable 比例(超卖率)、节点实际使用率。当实际使用逼近节点容量时,扩容或缩超卖。
一句话:超卖 = “用承诺换利用率”——但要看实际使用率而非 requests,用监控把超卖控在"挤得出、炸不了"的水位。
5. LimitRange 与 ResourceQuota:命名空间级治理
5.1 用 LimitRange 设默认值
LimitRange 给未显式设置资源的 Pod 提供默认值,或强制约束:
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
spec:
limits:
- max:
cpu: "4"
memory: 8Gi
min:
cpu: 100m
memory: 64Mi
default: # 未设置时的默认 limits
cpu: 500m
memory: 512Mi
defaultRequest: # 未设置时的默认 requests
cpu: 250m
memory: 256Mi
5.2 用 ResourceQuota 控总量
ResourceQuota 限制命名空间内资源总和,防止某团队吃光集群:
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-quota
spec:
hard:
requests.cpu: "20"
requests.memory: 40Gi
limits.cpu: "40"
limits.memory: 80Gi
pods: "50"
5.3 治理组合拳
LimitRange(单 Pod 上下限)→ 兜底 + 防极端
ResourceQuota(命名空间总量)→ 防吃光 + 成本配额
RBAC(谁有权限建 quota)→ 治理入口
一句话:LimitRange 管"单个 Pod",ResourceQuota 管"整个命名空间"——一个设边界,一个设总额,防止"某团队悄悄吃光集群"。
6. 驱逐机制:当节点资源告急
6.1 Eviction(驱逐)触发条件
kubelet 监控节点实际使用量,当触发驱逐阈值(--eviction-hard)时,按 QoS 与使用量驱逐 Pod:
# 默认驱逐阈值(kubelet)
memory.available < 100Mi
nodefs.available < 10%
nodefs.inodesFree < 5%
imagefs.available < 15%
6.2 软驱逐与优雅退出
--eviction-soft: 软阈值(触发宽限期 graceful-period)
--eviction-hard: 硬阈值(立即驱逐)
--eviction-minimum-reclaim: 释放的最小资源量
6.3 驱逐后会发生什么
被驱逐的 Pod 由控制器(Deployment/RS)重建,可能落在健康节点。若整个集群都紧张,会不断重建 → 需要容量兜底(节点扩缩容)。
一句话:驱逐 = 节点资源告急时的"减压阀"——按 QoS 优先级清场,是保护集群不过载的最后手段,但要靠容量监控让它尽量不触发。
7. 内存上限与 OOM:不可压缩资源的红线
7.1 OOMKilled 的排查
容器被 OOM 杀的迹象:
kubectl describe pod xxx | grep -A 5 "Last State"
# Reason: OOMKilled
kubectl logs --previous <pod> # 看被杀前日志
kubectl top pod # 看实际内存峰值
7.2 设定内存 limits 的方法
推荐先不给 limits 观测峰值(或给很大),用压测/灰度观测内存曲线,再设"峰值 × 1.2-1.5"作为 limits。
压测观测峰值:1.2Gi
建议 limits:1.5-1.8Gi(留余量)
建议 requests:1.0Gi(保证调度)
7.3 Java/Node 的经典坑
- JVM:-Xmx 应设为容器 limits 以下,避免堆超出被杀;
- Node:V8 有内存上限,超出也会 OOM;
- 总结:让应用自身内存上限 < 容器 limits,才有缓冲。
一句话:内存是不可压缩资源,limits 就是"死刑红线"——靠观测定合理值,让应用自身上限低于容器上限,别让 OOM 成常态。
8. 常见坑与生产最佳实践
8.1 常见坑
| 坑 | 现象 | 对策 |
|---|---|---|
| 只设 limits 不设 requests | 调度无依据 | requests/limits 一起设 |
| CPU limits 过低 | 延迟莫名变高 | 看节流指标,放宽 |
| 内存 limits 过紧 | 频繁 OOM | 观测峰值后定值 |
| 关键服务 BestEffort | 先被驱逐 | 设 Guaranteed |
| 不设 ResourceQuota | 团队吃光资源 | 命名空间配额 |
| 超卖无监控 | 节点被打爆 | 盯 request/实际使用率 |
8.2 生产配置模板
# 生产 Web 服务模板
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "2" # CPU 可留裕量(可压缩)
memory: 1Gi # 内存要严格(不可压缩)
8.3 治理闭环
命名空间:LimitRange + ResourceQuota
Pod:requests=limits(关键)/合理超卖(批量)
节点:监控超卖率与实际水位
集群:容量规划 + 自动扩缩容兜底
一句话:资源治理不是"写死数值",而是一套从 Pod 到命名空间到节点到集群的分层护栏——每一层都有默认值、有上限、有监控。
9. 总结
| 环节 | 要点 |
|---|---|
| requests/limits | 调度契约 vs 运行上限 |
| CPU vs 内存 | 可压缩(节流) vs 不可压缩(OOM) |
| QoS | Guaranteed > Burstable > BestEffort |
| 超卖 | 用监控控制水位 |
| 治理 | LimitRange(单 Pod)+ Quota(总量) |
| 驱逐 | 资源告急时按 QoS 清场 |
| 内存 | 观测峰值定 limits,别让 OOM 成常态 |
一句话记住:资源的本质是"承诺与上限、可压缩与不可压缩、优先级与驱逐"的组合博弈——requests 定承诺、limits 设红线、QoS 定优先级、quota 防失控、监控控超卖。写对资源,比写对业务代码更能守护稳定性。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。