Kubernetes 资源限制与 QoS:requests/limits 的工程化实践

系统讲解 Kubernetes 资源治理:requests 与 limits 的正确语义、CPU/内存的可压缩与不可压缩区别、QoS 三类等级(Guaranteed/Burstable/BestEffort)、超额分配与节点资源水位、LimitRange 与 ResourceQuota 的命名空间治理、Pod 驱逐机制(Eviction Policy)与内存上限,以及从入门到生产级的资源治理方法论。

资源管理是 Kubernetes 里最容易被低估、却直接影响稳定性与账单的配置。写错 requests/limits,轻则 Pod 被驱逐、服务抖动,重则节点被打爆、集群雪崩。本指南从 requests/limits 的语义 到 QoS 分级 再到 配额治理与驱逐机制,帮你建立一套既不过度配置、也不资源超卖的资源工程体系。


目录


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)
QoSGuaranteed > Burstable > BestEffort
超卖用监控控制水位
治理LimitRange(单 Pod)+ Quota(总量)
驱逐资源告急时按 QoS 清场
内存观测峰值定 limits,别让 OOM 成常态

一句话记住:资源的本质是"承诺与上限、可压缩与不可压缩、优先级与驱逐"的组合博弈——requests 定承诺、limits 设红线、QoS 定优先级、quota 防失控、监控控超卖。写对资源,比写对业务代码更能守护稳定性。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「云原生」更多文章

  1. Kubernetes 备份容灾与数据保护:Velero、etcd 与恢复演练实战
  2. Kubernetes 渐进式交付:Argo Rollouts、金丝雀/蓝绿与流量治理实战
  3. Kubernetes 集群排障与诊断:从 Pod 症状到节点/集群根因的实战手册