资源管理
概述#
Kubernetes 的资源管理是整个平台稳定性的基石。如果说调度器是"把 Pod 放在哪台机器上"的决策者,那么资源配置就是"每个 Pod 该拿多少资源"的规则书。这一章从 Request/Limit 的基本概念讲起,到 QoS 等级与驱逐优先级,再到通过 LimitRange 和 ResourceQuota 对命名空间做资源治理,帮你建立完整的资源管理认知。
Request vs Limit#
核心定义#
在 Kubernetes 中,每个容器的资源声明分为两块:
- Request(请求):调度器在给 Pod 找节点时依据的值。节点上所有 Pod 的 Request 之和不能超过节点可分配资源。如果节点资源不够满足一个 Pod 的 Request,这个 Pod 就永远调度不上去。
- Limit(限制):容器在运行时能使用的资源上限。超过这个值的行为,视资源类型而不同。
对于 CPU 和内存,两者的行为有本质区别:
| 维度 | CPU | 内存 |
|---|---|---|
| Request 的作用 | 调度依据;保底保障 | 调度依据;保底保障 |
| Limit 的作用 | 运行时 CPU 使用上限,可以超过 Request 但不超过 Limit | 硬上限,容器使用的内存绝不能超过 Limit |
| 超限后果 | CPU 是可压缩资源,超限只会被 throttling(限速),容器不会被杀 | 内存是不可压缩资源,超过 Limit 会触发 OOMKill,容器被强制杀死并重启 |
| 可以不设吗 | 可以,但不设意味着该容器可以无限抢占 CPU | 可以,但不设意味着内存泄漏时可以吃光整个节点的内存 |
一个直观对比#
节点有 4 核 CPU、8GB 内存
Pod A: Request CPU=1核, Limit CPU=2核
Pod B: Request CPU=1核, Limit CPU=2核
Pod C: Request CPU=1核, Limit CPU=2核
三个 Pod 都能调度成功(Request 之和 = 3核 ≤ 4核)
运行时 CPU:
- 空闲时,A 可以用到 2 核,B 和 C 同理
- 争抢时,按 Request 比例分配:各得 1/3 的 CPU
内存则不同:
- Request 内存 = 保底,Limit 内存 = 最后一条红线
- 一旦某个 Pod 内存使用超过 Limit,内核 cgroup 的 OOM Killer 直接把进程杀掉(kubelet 只是事后把原因上报为 OOMKilled)设置不当的后果#
| 问题 | 现象 | 后果 |
|---|---|---|
| Request 太高 | 一个只用 100m CPU 的服务设了 2000m Request | 调度器认为节点"已满",不再向该节点调度新 Pod,集群被迫拉新节点,大量资源浪费 |
| Request 太低 | 没有给突发流量留空间 | CPU 场景下 Pod 被频繁 throttle,响应变慢;内存场景下更危险——可能调度到一台内存已吃紧的节点上 |
| Limit 太低 | 业务峰值时频繁触发 Limit | CPU:throttle 导致延迟飙升;内存:直接 OOMKill,Pod 不断重启 |
| 不设 Limit | 放任自流 | 一个内存泄漏的 Pod 能拖垮整个节点,触发连锁雪崩——其他 Pod 被驱逐,节点 NotReady |
配置示例#
resources:
requests:
cpu: "200m" # 0.2 核,调度保底
memory: "256Mi" # 256MB 内存保底
limits:
cpu: "500m" # 最多用 0.5 核
memory: "512Mi" # 超过 512MB 就 OOMKill实战建议:对于大多数 Web/API 服务,
limits设为requests的 2-4 倍是比较合理的比例。先根据监控数据(Prometheus/Grafana)观察实际用量,再从保守值开始逐步调整。不要一拍脑袋设值。
QoS 三类#
Kubernetes 根据 Pod 中所有容器的 Request/Limit 配置,自动将 Pod 分为三个 QoS(Quality of Service)等级。这个等级的唯一作用就是决定节点资源紧张时,先杀谁。
分类规则#
| QoS 等级 | 判定条件 | 执行优先级 |
|---|---|---|
| Guaranteed | 每个容器的 requests.cpu == limits.cpu 且 requests.memory == limits.memory | 最高,最后被杀 |
| Burstable | 至少有一个容器设置了 Request 或 Limit,但不满足 Guaranteed 条件 | 中等 |
| BestEffort | 所有容器都没有设置任何 Request 或 Limit | 最低,最先被杀 |
判断逻辑很简单——Kubernetes 只看你有没有设、设了多少:
# Guaranteed:Request == Limit
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "512Mi"
# Burstable:有 Request 但不等于 Limit,或只设了其中一项
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "1000m" # CPU 不等于 Request → Burstable
memory: "512Mi"
# BestEffort:什么都没设
# 整个 resources 字段为空驱逐优先级#
当节点内存紧张时,kubelet 按照以下顺序驱逐 Pod:
1. BestEffort Pod(最先被杀)
2. Burstable Pod(使用量超过 Request 的先杀)
3. Guaranteed Pod(最后被杀,但超过 Limit 的仍然会被 OOMKill)关键认知:
- Guaranteed 不是"免死金牌"——超过自身 Limit 一样被 OOMKill,只是在节点资源不足时被驱逐的顺序最靠后。
- Burstable 是生产环境的大多数——合理设置 Request 提供保底,Limit 提供弹性空间。
- BestEffort 在生产环境不应出现——这意味着这个 Pod 随时可能被杀,且没有任何资源保障。
实战建议:生产环境的所有服务至少应该是 Burstable。核心服务、数据库等有状态组件可以设为 Guaranteed 以获得最高稳定性。
LimitRange#
如果每个开发者都自己决定设不设 Request/Limit,结果必然是混乱的。LimitRange 是命名空间级别的资源,用来为没设 Request/Limit 的 Pod/容器自动填充默认值,同时限制单个 Pod 或容器不能设置过高或过低的值。
解决什么问题#
- 默认值:开发者忘了或不知道要设资源,LimitRange 自动补上合理的默认值
- 上限:防止某个 Pod 设置
cpu: "100"(100 核),把整个命名空间的资源配额吃掉 - 下限:防止设置
cpu: "1m"(0.001 核),导致调度到一个已经塞满 BestEffort Pod 的节点上
配置示例#
apiVersion: v1
kind: LimitRange
metadata:
name: app-limits
namespace: production
spec:
limits:
# 针对容器的限制
- type: Container
# 默认值(没设 limits 时自动填入)
default:
cpu: "500m"
memory: "512Mi"
# 默认 Request(没设 requests 时自动填入)
defaultRequest:
cpu: "100m"
memory: "128Mi"
# 最大值:不允许超过
max:
cpu: "4"
memory: "8Gi"
# 最小值:不允许低于
min:
cpu: "50m"
memory: "64Mi"
# Limit/Request 比例上限
maxLimitRequestRatio:
cpu: "4" # limits.cpu / requests.cpu ≤ 4
memory: "2" # limits.memory / requests.memory ≤ 2这个配置的效果:
- 如果有人部署一个没有任何
resources字段的 Pod,LimitRange 会自动加上requests: cpu=100m, memory=128Mi和limits: cpu=500m, memory=512Mi - 如果有人尝试设
cpu: "10"(超过 max 的 4 核),API Server 直接拒绝 - 如果有人设
limits.cpu=4000m但requests.cpu=500m(比例 8:1,超过 maxLimitRequestRatio 的 4),也会被拒绝
查看命令#
# 查看命名空间的 LimitRange
kubectl get limitrange -n production
# 查看详细信息(默认值和限制范围一目了然)
kubectl describe limitrange app-limits -n productionResourceQuota#
LimitRange 管单个 Pod 的上限,ResourceQuota 管整个命名空间的总量。比如一个团队共用一个 namespace,你不想让某个人的 Deployment 把整个 namespace 的 CPU 额度全占满。
配置示例#
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-quota
namespace: production
spec:
hard:
# 计算资源总量
requests.cpu: "20" # 所有 Pod 的 CPU Request 之和不超过 20 核
requests.memory: "40Gi" # 所有 Pod 的内存 Request 之和不超过 40GB
limits.cpu: "40" # 所有 Pod 的 CPU Limit 之和不超过 40 核
limits.memory: "80Gi" # 所有 Pod 的内存 Limit 之和不超过 80GB
# 对象数量限制
pods: "50" # 最多 50 个 Pod
services: "10" # 最多 10 个 Service
configmaps: "20" # 最多 20 个 ConfigMap
secrets: "20"
persistentvolumeclaims: "10"配合使用#
LimitRange 和 ResourceQuota 一起使用才能形成完整的资源治理闭环:
LimitRange(个体约束)
├─ 给没设资源的 Pod 填默认值
├─ 限制单个 Pod 的 min/max
└─ 限制 Limit/Request 比例
ResourceQuota(整体约束)
├─ 限制 namespace 的所有 Pod 资源总和
├─ 限制 Pod/Service/ConfigMap 等对象的数量
└─ 超限时 API Server 直接拒绝创建查看命令#
# 查看命名空间的 ResourceQuota
kubectl get resourcequota -n production
# 查看使用情况(已用/总量)
kubectl describe resourcequota team-quota -n production多环境资源规划#
在实际工程中,QA、预发、生产三个环境承载的流量和重要性完全不同,资源策略也必须差异化。一套配置打天下,要么 QA 环境浪费严重,要么生产环境资源不足。
三环境资源特征对比#
| 维度 | QA / 测试环境 | 预发环境 | 生产环境 |
|---|---|---|---|
| 流量特征 | 内部测试,低 QPS | 灰度流量,约为生产的 10-30% | 全量真实流量,有峰值波动 |
| 副本数 | 1-2 个即可 | 2-3 个 | 按流量规划,通常 3+ 并配 HPA |
| Request 策略 | 贴近实际使用,尽量小 | 中等,留少量余量 | 保守,保底值要能扛日常峰值 |
| Limit 策略 | 紧贴 Request(Limit/Request ≈ 1.5) | 适中弹性(Limit/Request ≈ 2) | 大弹性空间(Limit/Request ≈ 3-4) |
| QoS 目标 | Burstable 即可 | Burstable | 核心服务 Guaranteed,普通服务 Burstable |
| 资源利用率目标 | 50-70%(不怕 throttle) | 40-60% | 30-50%(留足突发空间) |
| 节点规格 | 小规格节点(4C8G / 8C16G) | 中等规格(8C16G / 16C32G) | 大规格(16C32G+),按业务选型 |
QA 环境:紧凑型配置#
QA 环境的核心目标是"用最少的资源跑通功能",资源配置应尽量紧凑:
# QA 环境 — 紧凑型
resources:
requests:
cpu: "50m" # 0.05 核,够跑就行
memory: "128Mi"
limits:
cpu: "200m" # Limit/Request ≈ 4,给测试时一些弹性
memory: "256Mi" # 内存 Limit/Request ≈ 2
# 副本数:1(QA 不需要高可用)
replicas: 1QA 环境策略要点:
- Request 设到最小可运行值,让一个节点上能挤更多服务
- Limit 不需要太大弹性——QA 环境的流量是可控的
- 不怕偶尔 throttle——QA 环境的响应慢可以接受
- 用 LimitRange 强制设默认值,防止开发者忘记配置
- ResourceQuota 设得紧,防止某个测试服务吃掉整个环境资源
预发环境:中间态配置#
预发环境是生产的缩小版,流量约为生产的 10-30%,资源配置应在 QA 和生产之间:
# 预发环境 — 中间态
resources:
requests:
cpu: "200m" # 接近生产但缩减
memory: "512Mi"
limits:
cpu: "500m" # Limit/Request ≈ 2.5
memory: "1Gi"
# 副本数:2(验证多副本行为)
replicas: 2预发环境策略要点:
- Request 设为生产的 30-50%,验证调度和资源分配是否合理
- Limit 留出弹性,模拟生产突发的场景
- 副本数至少 2 个,验证滚动更新和多副本行为
- 配置应尽量贴近生产,让预发能真正暴露生产可能遇到的问题
- 预发是验证 HPA 配置的好地方——可以模拟流量验证扩缩行为
生产环境:保守型配置#
生产环境的核心目标是"稳定优先,留足余量":
# 生产环境 — 保守型
resources:
requests:
cpu: "500m" # 保底值能扛日常峰值
memory: "1Gi"
limits:
cpu: "2000m" # Limit/Request ≈ 4,给流量突发留空间
memory: "4Gi" # 内存 Limit/Request ≈ 4
# 副本数:3+ 并配 HPA
replicas: 3
# HPA 配置
hpa:
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70生产环境策略要点:
- Request 设为日常峰值的 1.2-1.5 倍——宁可浪费一点,也不要在峰值时被 throttle
- Limit 设为 Request 的 3-4 倍——应对突发流量和内存峰值
- 核心服务(数据库、网关、认证)设为 Guaranteed(Request == Limit),确保最后被驱逐
- 内存 Limit 必须设——不设内存 Limit 等于允许一个 Pod 拖垮整个节点
- 配合 HPA,让 Pod 数量随 CPU 自动伸缩,Request 是 HPA 计算利用率的分母
多环境配置管理实践#
用 Helm values 覆盖实现同一应用在不同环境的资源差异化:
# values-qa.yaml
resources:
requests:
cpu: "50m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
replicas: 1
# values-staging.yaml
resources:
requests:
cpu: "200m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "1Gi"
replicas: 2
# values-production.yaml
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"
replicas: 3
hpa:
enabled: true
minReplicas: 3
maxReplicas: 10部署命令:
helm install myapp ./myapp-chart -f values-qa.yaml -n qa
helm install myapp ./myapp-chart -f values-staging.yaml -n staging
helm install myapp ./myapp-chart -f values-production.yaml -n production多环境 LimitRange 差异化#
不同环境的 LimitRange 默认值也应该不同:
| 参数 | QA | 预发 | 生产 |
|---|---|---|---|
| defaultRequest.cpu | 50m | 200m | 500m |
| defaultRequest.memory | 128Mi | 512Mi | 1Gi |
| default.cpu (Limit) | 200m | 500m | 2000m |
| default.memory (Limit) | 256Mi | 1Gi | 4Gi |
| max.cpu | 1000m | 2000m | 8000m |
| max.memory | 2Gi | 4Gi | 16Gi |
| maxLimitRequestRatio.cpu | 4 | 3 | 4 |
常见反模式#
| 反模式 | 问题 | 正确做法 |
|---|---|---|
| 三环境用同一套 values | QA 浪费或生产不足 | 分环境 values 文件 |
| 生产 Request = QA Request | 生产峰值时大量 throttle | 生产 Request 设为日常峰值的 1.2-1.5 倍 |
| 生产不设内存 Limit | 一个内存泄漏 Pod 拖垮节点 | 内存 Limit 必须设,CPU Limit 可酌情 |
| QA 环境 Request 设太大 | 一个节点跑不了几个服务,成本浪费 | QA Request 设到最小可运行值 |
| 预发副本数 = 1 | 无法验证滚动更新和多副本行为 | 预发至少 2 副本 |
| 生产 Limit/Request = 1(全 Guaranteed) | 弹性不足,HPA 扩容前就 throttle | 只有核心服务用 Guaranteed |
实战要点#
- Request 是核心:影响调度、QoS 等级、HPA 的百分比指标计算。务必设置合理值。
- 先观察再设值:部署 Prometheus + Grafana(哪怕是最简部署),观察 1-2 天的 CPU/内存实际使用量后再定 Request/Limit。
- LimitRange 全覆盖:生产命名空间必须配置 LimitRange,防止 BestEffort Pod 出现。开发/测试环境可以放宽但不建议完全取消。
- ResourceQuota 按团队分:多团队共享集群时,每个团队一个 namespace + 各自的 ResourceQuota,避免互相影响。
- Guaranteed 不是银弹:只有对稳定性要求极高的核心组件才需要 Guaranteed。大部分服务用 Burstable 即可。
小结#
资源配置是 Kubernetes 运维中最基础也最容易被忽视的一环。Request 决定调度和 QoS,Limit 决定运行时天花板,两者缺一不可。QoS 的三级分类(Guaranteed > Burstable > BestEffort)直接影响节点压力下的驱逐顺序。LimitRange 和 ResourceQuota 分别从"个体"和"整体"两个维度做资源治理,配合使用才能真正管住集群资源。
多环境资源规划是工程实践中最常踩坑的领域——QA 紧凑省钱、预发中间态验证、生产保守留余量,三套策略对应三套 values 文件。记住:不设资源的 Pod 在生产环境就是定时炸弹,三环境用同一套配置是更大的定时炸弹。