在多团队共用一个 Kubernetes 集群时,资源争抢几乎是必然会遇到的问题。某个命名空间里的服务突发流量,把节点内存吃得一干二净,其他命名空间的 Pod 被逐个驱逐,这种事故在真实运维场景里屡见不鲜。Kubernetes 提供的 ResourceQuota 和 LimitRange 机制,就是用来在命名空间维度上做资源管控的:前者限制整个命名空间能申请的总量,后者约束单个容器或 Pod 的资源行为。两者配合使用,才能真正实现配额与限额的强制执行。本文将从原理、配置、报错排查到进阶用法,完整讲清楚这套机制。

一、先理解请求值与限制值,这是配额体系的基石
Kubernetes 对资源的计量建立在两个指标之上:requests(请求值)和 limits(限制值)。requests 是调度器使用的数字,kube-scheduler 根据它判断 Pod 应该被调度到哪个节点,也就是调度时承诺给这个容器的资源份额;limits 是运行时的硬上限,容器实际消耗超过这个值时,内存超限会被 OOM Kill,CPU 超限会被节流(throttling),不会立即被杀掉。
这个区分非常关键,因为 ResourceQuota 既可以按 requests 计费,也可以按 limits 计费。如果一个命名空间的配额只限制了 requests.cpu,那么所有 Pod 只要声明足够小的 requests,即使实际 limits 声明得很大,也能通过校验。反过来,如果配额限制了 limits.memory,那么 Pod 创建时 limits 的总和就受到硬性约束。理解了这一点,后面配置配额时才能明白自己在限制什么。
另外要注意,一旦命名空间里存在 ResourceQuota 对象,Kubernetes 准入控制器就会强制要求该命名空间下的每个容器都必须显式声明 requests 和 limits,否则 Pod 会直接创建失败,报错信息通常是“必须指定资源请求值和限制值”。这正是配额强制执行的第一道关卡。
二、ResourceQuota:命名空间级别的总量管控
ResourceQuota 作用于单个命名空间,限制的是这个命名空间内所有对象消耗资源的总和。它可以限制的资源大致分三类:计算资源(CPU、内存,以及新版本支持的临时存储 ephemeral-storage)、存储资源(PersistentVolumeClaim 的数量和容量)、对象数量(Pod、Service、ConfigMap、Deployment 等的数量上限)。
下面是一个比较典型的配额定义,覆盖了 CPU、内存以及对象数量:
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-quota
namespace: team-a
spec:
hard:
requests.cpu: "10"
requests.memory: 20Gi
limits.cpu: "20"
limits.memory: 40Gi
pods: "50"
services: "10"
persistentvolumeclaims: "20"
requests.storage: "500Gi"
这个配置的含义是:team-a 命名空间下所有容器的 CPU 请求总和不能超过 10 核,内存请求总和不能超过 20Gi,limits 上限分别是请求值的两倍,同时最多运行 50 个 Pod、10 个 Service、20 个 PVC,存储申请总量不超过 500Gi。任何一个维度超了,新的 Pod 创建都会被 API Server 拒绝。
值得强调的是强制执行的时机。配额检查发生在准入阶段,也就是说超配额的请求根本不会进入调度流程,kubectl 会直接返回错误,比如 exceeded quota: team-a-quota, requested: limits.memory=4Gi, used: limits.memory=38Gi, limited: limits.memory=40Gi。这条报错信息非常清楚地列出了已用量和上限,排查时按图索骥即可。查看当前用量可以用 kubectl describe resourcequota team-a-quota -n team-a,输出里会列出每个维度的 hard 值和 used 值。
一个命名空间里还可以存在多个 ResourceQuota 对象,它们的限制会同时生效,取交集。比如一个按团队总量限制,另一个专门限制 GPU 资源,两者互不冲突,任何一个不满足都会拒绝创建。这个特性在多维度管控时很好用。
三、LimitRange:为容器注入默认值和波动边界
ResourceQuota 有个明显的副作用:所有容器都必须声明 requests 和 limits,团队成员如果忘了写,部署就会失败。LimitRange 正好解决这个问题,它可以在容器没有显式声明资源时,自动注入默认值,相当于给整个命名空间兜底。
apiVersion: v1
kind: LimitRange
metadata:
name: team-a-limits
namespace: team-a
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 200m
memory: 256Mi
max:
cpu: "2"
memory: 2Gi
min:
cpu: 50m
memory: 64Mi
maxLimitRequestRatio:
cpu: "4"
逐项解释一下这些字段的含义。default 是容器未声明 limits 时注入的默认限制值;defaultRequest 是未声明 requests 时注入的默认请求值;max 和 min 划定了单个容器资源声明的上下限,比如某个容器声明了 4 核 CPU,超过了 max 的 2 核,创建时会被拒绝;maxLimitRequestRatio 则限制 limits 与 requests 的比值上限,防止有人声明极小的 requests 但巨大的 limits,规避调度超卖。
LimitRange 的 type 还支持 Pod 和 PersistentVolumeClaim。type 为 Pod 时,max 限制的是单个 Pod 内所有容器资源之和的上限;type 为 PersistentVolumeClaim 时,可以约束存储申请的最小和最大容量,比如限制 PVC 不能一次申请超过 100Gi 的存储。这在自助式平台场景下非常实用,能防止单个用户申请超大存储卷。
需要注意 LimitRange 只对之后创建的对象生效,已经存在的 Pod 不会被回溯修改。所以实践中的推荐做法是:先创建命名空间,紧接着就应用 LimitRange 和 ResourceQuota,再允许业务方部署工作负载,顺序不能颠倒。
四、落地实践与常见问题排查
把这两个对象组合起来,一套典型的命名空间管控流程是这样的:管理员创建命名空间后,先下发 LimitRange 提供默认值,再下发 ResourceQuota 锁定总量,最后通过 RBAC 把命名空间的操作权限交给业务团队。业务团队即使什么都不声明,也能获得默认限额;想申请更多资源,则受配额硬性约束。
排查配额问题有几个常用命令。kubectl get resourcequota -n team-a 查看配额清单;kubectl describe quota -n team-a 看详细用量;如果 Pod 创建失败怀疑是配额导致,看事件里的 FailedCreate 信息即可定位具体超了哪个维度。还有一种隐蔽的情况:Deployment 扩容时副本数上不去,Event 里显示 ReplicaSet 创建 Pod 失败,根因往往就是配额耗尽,而不是节点资源不足,这两种情况要区分清楚。
进阶一点,可以结合 PriorityClass 做资源抢占。给核心业务配置更高的 priorityClassName,当集群资源紧张时,低优先级 Pod 会被驱逐为核心业务让路。不过要注意,ResourceQuota 里还可以配置 scopePriorityClass 相关的作用域,针对不同优先级分别设置配额,避免高优先级业务把低优先级的配额也占光。
最后提醒两点容易踩的坑。第一,配额是按命名空间隔离的,不是按节点,所以它管的是申请总量,防不了节点层面的实际超卖,节点层面的保障要靠 requests 的调度承诺配合 Taint/Toleration 或节点池隔离。第二,CPU 的 limits 会引入节流,对延迟敏感的服务要谨慎设置过低的 CPU limits,必要时可以只限制 requests 而放开 CPU limits,用内存 limits 配合配额来兜底。资源管控从来不是一刀切,理解机制之后按业务特点灵活组合,才是这套体系发挥价值的关键。
KubernetesResourceQuotaLimitRange修改时间:2026-09-03 15:13:16