2. ResourceQuota 基础:定义、作用域与核心字段
ResourceQuota 是 Kubernetes 内置的准入控制机制,用于限制命名空间(Namespace)级别的资源总消耗。在多租户隔离集群中,它是防止“吵闹邻居”效应、保障租户间公平调度的第一道防线。本章将系统梳理 ResourceQuota 的 YAML 定义结构、作用域分类以及核心字段的语义与配置要点。
2.1 ResourceQuota 定义结构
一个标准的 ResourceQuota 对象包含 apiVersion、kind、metadata 和 spec 四个顶层字段。spec 下又分为 hard(硬限制)和 scopeSelector(作用域选择器)两个关键子字段。以下是一个典型的生产级示例:
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: team-alpha
spec:
hard:
requests.cpu: "4"
requests.memory: "8Gi"
limits.cpu: "8"
limits.memory: "16Gi"
requests.nvidia.com/gpu: "1"
scopeSelector:
matchExpressions:
- operator: In
scopeName: PriorityClass
values:
- high-priority
| 字段 | 说明 | 是否必填 |
|---|---|---|
spec.hard |
定义资源类型的硬性上限,超过即拒绝创建或更新 Pod | 是 |
spec.scopeSelector |
按作用域过滤配额生效范围,支持 Terminating、NotTerminating、BestEffort、NotBestEffort、PriorityClass |
否 |
spec.scopes |
旧版作用域声明方式(已弃用),推荐使用 scopeSelector |
否 |
最佳实践:始终使用
scopeSelector 而非 scopes,前者支持 PriorityClass 等更精细的匹配条件,且与未来版本兼容性更好。
2.2 作用域(Scopes)详解
作用域决定了 ResourceQuota 对哪些 Pod 生效。通过组合不同的作用域,可以实现对批处理作业、常驻服务、低优先级任务的分级管控。下表列出了 Kubernetes 内置的四种标准作用域及一种扩展作用域:
| 作用域名称 | 匹配条件 | 典型场景 |
|---|---|---|
Terminating |
Pod 的 spec.activeDeadlineSeconds 非 nil |
批处理任务、CI/CD Job |
NotTerminating |
Pod 的 spec.activeDeadlineSeconds 为 nil |
Web 服务、API 网关 |
BestEffort |
Pod 的 requests 和 limits 均未设置 |
低优先级测试、临时调试 Pod |
NotBestEffort |
Pod 设置了 requests 或 limits |
生产级工作负载 |
PriorityClass |
通过 scopeSelector 匹配特定 PriorityClass 名称 |
按优先级隔离配额 |
关键理解:作用域是“与”逻辑——一个 ResourceQuota 可以同时指定多个作用域,Pod 必须满足所有作用域条件才会被该配额统计。例如同时设置
NotTerminating 和 NotBestEffort,则只统计常驻且有资源保障的 Pod。
2.3 核心字段:计算资源
计算资源字段分为 requests(请求量)和 limits(上限)两大类,分别对应 CPU 和内存。在多租户场景中,必须同时设置 requests 和 limits 配额,否则可能出现“只设 limits 导致 requests 无限抢占”的漏洞。
| 字段名 | 单位 | 语义 | 调度影响 |
|---|---|---|---|
requests.cpu |
核(整数或小数,如 0.5) | Pod 保证可获得的 CPU 最小值 | 直接影响节点调度决策 |
requests.memory |
字节(Ki/Mi/Gi) | Pod 保证可获得的内存最小值 | 直接影响节点调度决策 |
limits.cpu |
核(整数或小数) | Pod 可使用的 CPU 硬上限 | 触发 CPU 节流(throttling) |
limits.memory |
字节(Ki/Mi/Gi) | Pod 可使用的内存硬上限 | 超过后触发 OOM Kill |
常见陷阱:只设置
limits.memory 而不设置 requests.memory 时,Kubernetes 会自动将 requests.memory 设置为与 limits.memory 相同。这可能导致配额统计值翻倍,造成意外的配额冲突。建议在 ResourceQuota 中显式声明所有四个字段。
2.4 核心字段:存储与扩展资源
除了 CPU 和内存,ResourceQuota 还支持对持久化存储声明(PVC)和扩展资源(如 GPU、FPGA)进行配额限制。这些字段在 AI 训练、大数据分析等 GPU 密集型多租户场景中尤为关键。
| 字段名 | 单位 | 说明 |
|---|---|---|
requests.storage |
字节(Gi/Ti) | 所有 PVC 的存储请求总量上限 |
persistentvolumeclaims |
整数 | 命名空间内允许创建的 PVC 数量上限 |
requests.<ext-resource> |
整数 | 扩展资源(如 nvidia.com/gpu)的请求总量上限 |
limits.<ext-resource> |
整数 | 扩展资源的使用上限(通常与 requests 一致) |
count/pods |
整数 | 命名空间内允许运行的 Pod 数量上限 |
count/services |
整数 | 命名空间内允许创建的 Service 数量上限 |
2.5 配额冲突的根源:字段语义误解
多租户集群中 80% 的配额冲突并非由资源不足引起,而是由对核心字段语义的误解导致。以下列出三种最常见的冲突模式:
- requests 与 limits 不匹配:租户只设置
limits.cpu: 4但未设置requests.cpu,导致实际请求量被自动提升为 4,超出配额。 - 存储计数与容量混淆:同时设置
requests.storage: 100Gi和persistentvolumeclaims: 10,但租户创建了 10 个 20Gi 的 PVC,总容量 200Gi 超出存储配额。 - 扩展资源单位误解:GPU 配额使用整数计数,但租户在 Pod 中填写
requests.nvidia.com/gpu: 0.5,导致配额系统无法正确统计。
防御性配置建议:在 ResourceQuota 中显式声明所有可能被使用的字段,并为每个字段添加注释说明单位。同时配合
LimitRange 设置默认的 requests/limits 比例,从源头消除语义歧义。