在 Kubernetes 集群从”试验田”走向”生产平台”的过程中,一个绕不开的问题就是:如何防止某个团队或某个应用吃光整个集群的资源?当你把 K8s 集群开放给多个业务团队使用时,如果没有资源配额管控,一场”公地悲剧”几乎是不可避免的——某个写错了 resources 配置的 Pod 可能会独占节点内存,某个没有设置 limit 的批处理任务可能在凌晨跑满 CPU,导致核心服务全面降级。
Kubernetes 原生提供了两个关键的资源管控机制:ResourceQuota(命名空间级配额)和 LimitRange(默认值与上下限约束)。它们配合使用,可以构建一套完整的多租户资源公平调度体系。本文将从底层原理到生产实战,带你彻底搞懂这两个对象的正确用法。
一、资源管控的必要性:为什么裸奔的集群迟早会出事
先看一个真实的故障场景。某电商平台将所有业务部署在同一个 K8s 集群上,没有做任何资源配额限制。某天凌晨,数据分析团队上线了一个新的离线计算任务,配置了
1 | resources.requests.cpu: "16" |
,但没有设置 limits。这个任务调度到一个核心交易服务所在的节点上,迅速占满了 CPU,导致交易服务的 P99 延迟从 50ms 飙升到 3 秒以上。
这类问题的根源在于:Kubernetes 的调度器只看 requests 做调度决策,但运行时容器实际消耗的资源可以远超 requests(如果没有设置 limits)。这就产生了”调度承诺”和”实际消耗”之间的巨大鸿沟。
没有资源管控时的典型问题
- 资源饥饿:低优先级应用占用大量资源,高优先级服务无节点可调度
- Noisy Neighbor:同一节点上的应用互相干扰,CPU throttling 导致延迟毛刺
- 成本失控:每个团队都申请最大资源”以防万一”,集群被迫不断扩容
- 安全风险:恶意或失控的容器可以耗尽节点资源,影响同节点的所有工作负载
ResourceQuota 和 LimitRange 就是 Kubernetes 给出的答案——前者限制”总量”,后者约束”单个体”。
二、ResourceQuota:命名空间级的资源总闸
ResourceQuota 是作用于 命名空间 级别的资源配额对象。它就像一个预算审批员,在你提交资源创建请求时检查:”这个命名空间的所有资源加起来有没有超额?”
2.1 ResourceQuota 管控的范围
ResourceQuota 可以管控三大类资源:
| 类别 | 可管控的资源 | 说明 |
|---|---|---|
| 计算资源 | cpu, memory, requests.cpu, requests.memory, limits.cpu, limits.memory | 控制命名空间内所有 Pod 的 CPU/内存总量 |
| 存储资源 | requests.storage, persistentvolumeclaims, requests.存储类名.storage | 控制 PVC 数量和存储总量 |
| 对象数量 | pods, services, replicationcontrollers, resourcequotas, secrets, configmaps 等 | 限制各类 API 对象的创建数量 |
一个关键细节:ResourceQuota 只对设置了 resources 的 Pod 生效。如果一个 Pod 没有设置任何 resources 字段,它将不受 ResourceQuota 的限制——这恰恰是最危险的情况。这就是为什么 LimitRange(设置默认值)必须和 ResourceQuota 配合使用。
2.2 ResourceQuota 的 Admission 控制流程
当你创建或更新 Pod 时,Kubernetes API Server 的准入控制器会执行以下检查:
- 如果 Pod 没有设置 resources,先从 LimitRange 获取默认值填充
- 将 Pod 的 requests/limits 加上命名空间已使用的配额
- 检查是否超过 ResourceQuota 中定义的硬性限制
- 如果超额,拒绝创建并返回 403 Forbidden
- 如果未超额,更新 ResourceQuota 的 status.used 字段,允许创建
注意:这个检查是原子性的,不存在两个 Pod 同时创建”抢额度”的竞态条件。API Server 通过 etcd 的事务机制保证了配额扣减的一致性。
2.3 生产级 ResourceQuota 配置示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 apiVersion: v1
kind: ResourceQuota
metadata:
name: team-backend-quota
namespace: team-backend
spec:
hard:
# 计算资源配额
requests.cpu: "20" # 最多申请 20 核 CPU
requests.memory: 40Gi # 最多申请 40GB 内存
limits.cpu: "40" # 最多限制 40 核 CPU
limits.memory: 80Gi # 最多限制 80GB 内存
# 对象数量配额
pods: "50" # 最多 50 个 Pod
services: "10" # 最多 10 个 Service
persistentvolumeclaims: "20" # 最多 20 个 PVC
requests.storage: "500Gi" # 最多申请 500GB 存储
# 配置与安全
secrets: "20" # 最多 20 个 Secret
configmaps: "30" # 最多 30 个 ConfigMap
# 作用域限定:仅对 BestEffort 以外的 Pod 计数
# (配合 scopes 使用)
scopes:
- NotTerminating # 仅限非终止型 Pod
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-backend-besteffort
namespace: team-backend
spec:
hard:
pods: "5" # BestEffort Pod 最多 5 个
scopes:
- BestEffort # 仅对未设置 resources 的 Pod

2.4 ResourceQuota Scopes 详解
Scopes 让你可以对不同类型的资源分别设置配额,Kubernetes 内置了四种 scope:
- Terminating:匹配
1spec.activeDeadlineSeconds >= 0
的 Pod(如 Job、CronJob)
- NotTerminating:匹配没有设置
1activeDeadlineSeconds
的 Pod(长期运行的服务)
- BestEffort:匹配没有设置任何 resources 的 Pod(最危险的类型)
- NotBestEffort:匹配设置了 resources 的 Pod(受管控的 Pod)
生产环境建议 严格限制 BestEffort Pod 的数量(甚至设为 0),强制所有 Pod 都必须设置 resources,这样 ResourceQuota 才能真正发挥作用。
三、LimitRange:单 Pod 的默认值与边界约束
如果说 ResourceQuota 是命名空间的”总预算”,那么 LimitRange 就是”单项开支标准”——它为命名空间内的每个 Pod/Container 定义了资源的默认值、最小值和最大值。
3.1 LimitRange 的三种约束类型
| 约束类型 | 作用 | 适用场景 |
|---|---|---|
| default | 为未设置 limits 的容器自动填充默认 limits | 防止容器无限使用资源 |
| defaultRequest | 为未设置 requests 的容器自动填充默认 requests | 确保调度器有合理的调度依据 |
| max / min | 限制单个容器的 resources 上下界 | 防止单个容器过大或过小 |
| maxLimitRequestRatio | 限制 limits 与 requests 的最大比值 | 防止超配(overcommit)过度 |
3.2 生产级 LimitRange 配置示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 apiVersion: v1
kind: LimitRange
metadata:
name: team-backend-limits
namespace: team-backend
spec:
limits:
- type: Container
# 默认值:未设置 resources 时的自动填充
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
# 上下限:单个容器的资源边界
max:
cpu: "4"
memory: "8Gi"
min:
cpu: "50m"
memory: "64Mi"
# 超配比限制:limits 最多是 requests 的 4 倍
maxLimitRequestRatio:
cpu: "4"
memory: "4"
- type: Pod
max:
cpu: "8"
memory: "16Gi"
- type: PersistentVolumeClaim
max:
storage: "50Gi"
min:
storage: "1Gi"
3.3 LimitRange 的自动填充机制
LimitRange 的自动填充是很多人理解不透彻的地方。让我用一个具体的例子说明:
假设你提交了以下 Pod 定义,没有设置任何 resources:
1
2
3
4
5
6
7
8
9
10 apiVersion: v1
kind: Pod
metadata:
name: my-app
namespace: team-backend
spec:
containers:
- name: app
image: my-app:v1
# 没有 resources 字段!
Kubernetes 的准入控制器会根据上面定义的 LimitRange 自动填充为:
1
2
3
4
5
6
7
8
9
10
11 spec:
containers:
- name: app
image: my-app:v1
resources:
requests:
cpu: "100m" # 来自 defaultRequest
memory: "128Mi" # 来自 defaultRequest
limits:
cpu: "500m" # 来自 default
memory: "512Mi" # 来自 default
填充规则总结:
- 未设置
1limits
→ 用
1default填充
- 未设置
1requests
→ 用
1defaultRequest填充;如果没有
1defaultRequest,则用
1default填充
- 如果设置了
1limits
但没设
1requests→ requests 默认等于 limits

四、ResourceQuota 与 LimitRange 的协作模式
单独使用任何一个都无法实现完整的资源管控。它们的协作关系如下:
4.1 三层防护体系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 ┌─────────────────────────────────────────────┐
│ Layer 1: LimitRange (个体约束) │
│ · 设置默认值,确保无 resources 的 Pod 被填充 │
│ · 限制单个容器的资源上下界 │
│ · 限制超配比(maxLimitRequestRatio) │
├─────────────────────────────────────────────┤
│ Layer 2: ResourceQuota (总量约束) │
│ · 限制命名空间的总资源消耗 │
│ · 限制各类对象的数量 │
│ · 配合 scopes 细化管控粒度 │
├─────────────────────────────────────────────┤
│ Layer 3: PriorityClass (优先级) │
│ · 资源不足时,低优先级 Pod 被抢占 │
│ · 保证核心服务的资源供给 │
└─────────────────────────────────────────────┘
4.2 常见配合模式
模式一:严格管控型(金融/支付场景)
1
2
3
4
5
6
7
8
9
10
11 # LimitRange: 小超配比,强约束
maxLimitRequestRatio:
cpu: "1.5" # limits 最多是 requests 的 1.5 倍
memory: "1" # 内存不允许超配(limits = requests)
# ResourceQuota: 精确配额
hard:
requests.cpu: "30"
limits.cpu: "45" # 30 * 1.5
requests.memory: "60Gi"
limits.memory: "60Gi" # 不允许超配
模式二:弹性管控型(互联网/Web 场景)
1
2
3
4
5
6
7
8
9
10
11 # LimitRange: 较大超配比,适度约束
maxLimitRequestRatio:
cpu: "8" # CPU 允许 8 倍超配(大部分时间 CPU 不会满载)
memory: "2" # 内存 2 倍超配
# ResourceQuota: 按超配比设定 limits 上限
hard:
requests.cpu: "50"
limits.cpu: "400" # 50 * 8
requests.memory: "100Gi"
limits.memory: "200Gi" # 100 * 2
五、生产实战:从零构建多租户资源管控体系
5.1 命名空间模板化配置
在多租户场景下,每个团队应该有独立的命名空间,并配套一致的 ResourceQuota 和 LimitRange。以下是一个可复用的命名空间初始化模板:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 # namespace-init.yaml — 每个新团队初始化时应用
apiVersion: v1
kind: Namespace
metadata:
name: {{TEAM_NAME}}
labels:
env: production
team: {{TEAM_NAME}}
resource-quota: enabled
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: {{TEAM_NAME}}
spec:
hard:
requests.cpu: "{{CPU_REQUESTS}}"
requests.memory: "{{MEMORY_REQUESTS}}"
limits.cpu: "{{CPU_LIMITS}}"
limits.memory: "{{MEMORY_LIMITS}}"
pods: "{{MAX_PODS}}"
services: "10"
secrets: "20"
configmaps: "30"
persistentvolumeclaims: "15"
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: besteffort-quota
namespace: {{TEAM_NAME}}
spec:
hard:
pods: "0" # 禁止 BestEffort Pod
scopes:
- BestEffort
---
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: {{TEAM_NAME}}
spec:
limits:
- type: Container
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
max:
cpu: "4"
memory: "8Gi"
min:
cpu: "50m"
memory: "64Mi"
maxLimitRequestRatio:
cpu: "4"
memory: "3"
5.2 配额动态调整策略
业务发展过程中,团队的资源需求会变化。配额调整应该遵循以下原则:
- 基于监控数据:使用
1kubectl get resourcequota -n <ns>
查看 used/hard 比率,当使用率持续超过 80% 时考虑扩容
- 预留 buffer:ResourceQuota 的 hard 值应该留 20%-30% 的余量,避免正常扩容被拒绝
- 渐进式调整:先调大 ResourceQuota,观察一周实际消耗后再决定是否永久生效
- 成本归属:结合 Kubernetes 成本分析工具(如 Kubecost),让每个团队”看到”自己的资源消耗和成本
1
2
3
4
5
6
7
8
9
10
11
12
13
14 # 查看命名空间配额使用情况
kubectl get resourcequota -n team-backend -o yaml
# 输出示例:
# status:
# hard:
# requests.cpu: "20"
# requests.memory: 40Gi
# used:
# requests.cpu: "14.5" # 72.5% 使用率
# requests.memory: 28Gi # 70% 使用率
# 当使用率 > 80% 时,考虑扩容:
kubectl patch resourcequota compute-quota -n team-backend --type merge -p '{"spec":{"hard":{"requests.cpu":"30","requests.memory":"60Gi"}}}'

六、常见坑与排障指南
6.1 Pod 处于 Pending 状态且事件提示 ExceededQuota
这是最常见的配额问题。当 ResourceQuota 不足以容纳新 Pod 时:
1
2
3
4
5
6
7
8
9
10
11
12
13 # 查看事件
kubectl describe pod my-app -n team-backend
# Events:
# Warning FailedCreate ... exceeded quota: compute-quota,
# requested: requests.cpu=1,requests.memory=1Gi,
# used: requests.cpu=19.5,requests.memory=39Gi,
# limited: requests.cpu=20,requests.memory=40Gi
# 解决方案:
# 1. 调大 ResourceQuota
# 2. 删除不需要的 Pod 释放配额
# 3. 减小新 Pod 的 requests
# 4. 联系平台团队申请更多配额
6.2 LimitRange 阻止了 Pod 创建
当 Pod 的 resources 超过 LimitRange 的 max 或低于 min 时:
1
2
3
4
5
6 kubectl describe pod my-app -n team-backend
# Warning FailedCreate ... LimitRange
# max limit to admit: cpu=4,memory=8Gi
# but request: cpu=8,memory=16Gi
# 解决方案:调整 LimitRange 的 max,或者减小 Pod 的资源申请
6.3 maxLimitRequestRatio 校验失败
1
2
3
4
5
6
7 # 错误:limits.cpu=4, requests.cpu=500m,比值 8 超过了 maxLimitRequestRatio 4
# 解决:要么增大 requests,要么减小 limits
resources:
requests:
cpu: "1" # 4/1 = 4,刚好满足
limits:
cpu: "4"
6.4 删除命名空间后配额”残留”
ResourceQuota 的 status.used 是实时计算的,不存在真正的”残留”问题。但如果你发现 used 值不为 0 且命名空间内没有 Pod,检查是否有以下对象占用配额:
1
2
3
4
5 # 检查所有占用配额的对象
kubectl get all,pvc,secret,configmap -n team-backend
# 常见遗漏:ReplicaSet 的副本虽然 Pod 未运行,但可能已经占用了配额
kubectl get replicasets -n team-backend
七、进阶:结合 PriorityClass 和 Preemption 实现资源分级保障
ResourceQuota 和 LimitRange 解决了”总量控制”的问题,但没有解决”资源不足时谁该让路”的问题。PriorityClass + Preemption(抢占)机制填补了这个空白。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 # 定义优先级
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: critical
value: 1000000
globalDefault: false
preemptionPolicy: PreemptLowerPriority
description: "核心交易服务"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: normal
value: 100000
globalDefault: true
description: "普通业务服务"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: batch
value: 10000
preemptionPolicy: PreemptLowerPriority
description: "批处理任务,可被抢占"
配合 ResourceQuota 使用时,可以为不同优先级的 Pod 设置不同的配额:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 apiVersion: v1
kind: ResourceQuota
metadata:
name: critical-quota
namespace: team-backend
spec:
hard:
pods: "20"
requests.cpu: "15"
requests.memory: "30Gi"
scopeSelector:
matchExpressions:
- operator: In
scopeName: PriorityClass
values: ["critical"]
这样,critical 级别的 Pod 有独立的配额池,不会被普通 Pod 占满;当集群资源紧张时,低优先级的 batch Pod 会被抢占,为 critical Pod 腾出空间。
八、监控与告警:让配额管理可视化
配额设置了不等于万事大吉,持续监控才是关键。以下是推荐的监控指标和告警规则:
8.1 关键监控指标
1
2
3
4
5
6 # Prometheus 指标
kube_resourcequota{type="hard"} # 配额硬限制
kube_resourcequota{type="used"} # 配额已使用量
# 计算使用率
rate_used = kube_resourcequota{type="used"} / kube_resourcequota{type="hard"}
8.2 推荐告警规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 # 配额使用率超过 85%
- alert: ResourceQuotaNearLimit
expr: |
kube_resourcequota{type="used"} / kube_resourcequota{type="hard"} > 0.85
for: 15m
labels:
severity: warning
annotations:
summary: "Namespace {{ $labels.namespace }} 的 {{ $labels.resource }} 配额使用率超过 85%"
# 配额使用率超过 95%
- alert: ResourceQuotaCritical
expr: |
kube_resourcequota{type="used"} / kube_resourcequota{type="hard"} > 0.95
for: 5m
labels:
severity: critical
annotations:
summary: "Namespace {{ $labels.namespace }} 的 {{ $labels.resource }} 配额即将耗尽"
此外,建议在 Grafana 上建立配额使用率的趋势图,帮助团队提前规划扩容需求,而不是等到 Pod 创建失败时才紧急处理。
总结
ResourceQuota 和 LimitRange 是 Kubernetes 多租户资源管控的基石,但它们需要配合使用才能发挥完整效果:
- LimitRange 先行:为所有容器设置默认值和边界,确保没有”裸奔”的 Pod
- ResourceQuota 守门:限制命名空间的总资源消耗,防止单个团队过度占用
- PriorityClass 兜底:资源不足时保证高优先级服务优先获得资源
- 监控告警持续运行:让配额管理从被动响应变为主动规划
记住一个核心原则:永远不要让用户的 Pod 不设置 resources 就能创建成功。通过 LimitRange 的 default/defaultRequest 自动填充 + BestEffort scope 的 ResourceQuota 设为 0,可以从机制上杜绝无资源约束的 Pod。这不仅是稳定性保障,更是成本优化的起点——只有当你清楚每个 Pod 需要多少资源时,才能准确规划集群容量,避免过度配置带来的浪费。
汤不热吧