概述
某次大促前夜,测试环境的 Pod 集体卡在 Pending。kubectl top nodes 显示节点 CPU 使用率才 35%,内存也只用了不到一半。翻 Deployment YAML,requests 和 limits 设置都合理。问题出在哪?——不是资源不够,是"账本"被算爆了。某个命名空间的 ResourceQuota 已经触顶,后续所有 Pod 创建请求被 admission controller 直接拒绝。
这件事让我重新审视了 K8s 命名空间级资源治理的完整链路。很多人以为设了 LimitRange 就万事大吉,或者觉得 ResourceQuota 只是"给开发组限个额度"的简单操作。实际上,ResourceQuota、LimitRange 和 QoS 三者之间的协同关系,直接决定了多租户集群的资源公平性和稳定性。
本文围绕 4 个生产级决策展开:LimitRange 与 ResourceQuota 必须成对出现、配额维度要覆盖算力+对象+存储三层、用 ScopeSelector 做精细化分流、配额耗尽的告警与自动治理。每个决策都附带了完整可复制的 YAML 模板和真实踩坑数据。如果你正在管理一个多团队共享的 K8s 集群,这些配置能帮你避开大部分资源争抢的坑。
ResourceQuota 和 LimitRange 到底在管什么
先说人话。K8s 的资源管理分两个层次:
- Pod 级:
requests和limits,管的是单个容器能用多少资源。调度器根据 requests 决定 Pod 放到哪个节点,运行时根据 limits 做限流和 OOM Kill。这部分在 相关文章:Kubernetes 资源管理:Requests 与 Limits 里有详细讲解。 - 命名空间级:
ResourceQuota和LimitRange,管的是整个命名空间能吃多少集群资源。
打个比方。LimitRange 是"采购审批员"——每个容器提交资源申请时,它检查你的 requests/limits 是否在合理范围内,不合理直接驳回。ResourceQuota 是"财务审计员"——它盯着整个命名空间的资源消耗总量,一旦超标,新的 Pod 直接不让创建。
两者缺一不可。只配 LimitRange 不配 ResourceQuota,用户可以无限创建符合 LimitRange 规范的 Pod,最终把节点资源耗尽。只配 ResourceQuota 不配 LimitRange,用户可能提交一个 request=10m、limit=64Gi 的 Pod,调度器觉得 10m 很小就放进去,但运行时这个 Pod 可以吃掉 64Gi 内存——其他 Pod 的资源保障形同虚设。
ResourceQuota 能限制什么
ResourceQuota 作用在命名空间级别,可以限制三大类资源:
| 类别 | 可限制项 | 说明 |
|---|---|---|
| 计算资源 | requests.cpu、requests.memory、limits.cpu、limits.memory | 命名空间内所有 Pod 的 requests/limits 总和上限 |
| 存储资源 | requests.storage、persistentvolumeclaims、requests.ephemeral-storage | 存储 PVC 数量和临时存储总量 |
| 对象数量 | pods、services、secrets、configmaps、replicationcontrollers、services.nodeports、services.loadbalancers | 各类 K8s 对象的数量上限 |
一个关键细节:如果你在 ResourceQuota 里限制了 cpu 或 memory 的 requests/limits,那么该命名空间内每个新 Pod 必须显式设置这些资源的 requests 和 limits,否则 admission controller 会拒绝创建。很多人在这里踩坑——加了 ResourceQuota 后,原本不设资源限制的 Pod 突然全部创建失败。
LimitRange 能做什么
LimitRange 是命名空间内的"默认值注入器"和"范围校验器":
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: production
spec:
limits:
# 默认值:Pod 不设 resources 时自动注入
- type: Container
default: # 默认 limits
cpu: "500m"
memory: "512Mi"
defaultRequest: # 默认 requests
cpu: "100m"
memory: "128Mi"
# 范围校验:超出范围的 requests/limits 会被拒绝
max:
cpu: "4"
memory: "8Gi"
min:
cpu: "50m"
memory: "64Mi"
# Pod 级总量限制
- type: Pod
max:
cpu: "8"
memory: "16Gi"
min:
cpu: "100m"
memory: "128Mi"
# 存储范围
- type: PersistentVolumeClaim
max:
storage: "100Gi"
min:
storage: "1Gi"
这段配置做了三件事:
- 注入默认值:开发提交 Pod 时如果忘了写 resources,LimitRanger 准入控制器会自动填入 default 和 defaultRequest。Pod 不会因为缺 resources 被拒绝。
- 校验范围:任何容器的 cpu/memory 超出 max 或低于 min,创建直接失败。防止有人提交 request=10m、limit=64Gi 的畸形配置。
- 限制 PVC 大小:防止单个 PVC 申请过大存储。
场景复盘:一次"配额不算账"引发的生产故障
把这个故障讲透,你就能理解为什么后面四个决策缺一不可。
某电商平台(脱敏处理),集群 6 个节点,每个节点 16C/64G。总共 96C/384G。按团队划分了 4 个命名空间:order、payment、logistics、common。每个命名空间配了 ResourceQuota,限制 requests.cpu=24、requests.memory=96Gi。看起来很均匀——每个团队分到 1/4 的资源。
问题出在 common 命名空间。这个命名空间放了一批工具类应用:配置中心、任务调度、消息队列消费者。开发在这批应用里普遍只设了 requests 不设 limits(因为"工具类应用不会突发"),LimitRange 也没配 max 限制。
大促当天,消息队列积压,消费者 Pod 开始疯狂吃内存。没有 limits 约束,每个 Pod 可以吃到节点全部可用内存。6 个节点里有 3 个被 common 命名空间的 Pod 占满,order 和 payment 的 Pod 被挤到剩下 3 个节点上。CPU 调度延迟从 5ms 涨到 80ms,支付接口 P99 从 200ms 飙到 1200ms。
根因是什么?不是 common 命名空间超了 ResourceQuota——它的 requests.cpu 确实没超 24 核。但 requests 只管调度,不管运行时。没有 limits,运行时没有上限。ResourceQuota 管的是 requests/limits 的声明总量,管不了运行时的实际消耗。
这就是第一个决策的核心:ResourceQuota 管总量,LimitRange 管单笔,两者缺一不可。
四个生产级治理决策
决策一:LimitRange 和 ResourceQuota 必须成对出现
这是最基础也最容易忽略的一条。很多人配了 ResourceQuota 就觉得安全了,但如果没有 LimitRange 兜底,用户可以提交极端配置:
| 配置方式 | requests | limits | 调度行为 | 运行时行为 |
|---|---|---|---|---|
| 畸形配置 A | 10m | 64Gi | 调度器觉得很小,随便放 | 可吃掉 64Gi 内存 |
| 畸形配置 B | 8 | 8 | 调度器占满一个节点 | Guaranteed QoS,不会被驱逐 |
| 健康配置 | 500m | 1 | 合理调度 | 运行时上限 1 核 |
畸形配置 A 最危险。调度器看 requests=10m,觉得这个 Pod 几乎不占资源,往哪个节点塞都行。但运行时没有内存限制,一旦内存飙升就吃掉整个节点。
我的建议:每个业务命名空间必须同时配置 ResourceQuota 和 LimitRange。LimitRange 的 max 值根据该命名空间分配到的资源总量的 1/3 到 1/2 设置。比如命名空间分了 24 核 CPU,单个容器的 max.cpu 设为 8(1/3),max.memory 设为分配总量的 1/4。
# 生产级 LimitRange 模板
apiVersion: v1
kind: LimitRange
metadata:
name: production-limits
namespace: order-service
spec:
limits:
- type: Container
default:
cpu: "1000m"
memory: "1Gi"
defaultRequest:
cpu: "200m"
memory: "256Mi"
max:
cpu: "8"
memory: "16Gi"
min:
cpu: "50m"
memory: "64Mi"
- type: Pod
max:
cpu: "16"
memory: "32Gi"
这个模板的关键点:
default和defaultRequest确保忘写 resources 的 Pod 也有合理默认值max限制单容器上限,防止一个容器吃掉整个命名空间的配额min防止 requests 过小导致调度器误判- Pod 级 max 限制一个 Pod 内所有容器资源总和
决策二:配额维度要覆盖"算力 + 对象 + 存储"三层
大部分团队配 ResourceQuota 只写了 cpu 和 memory。但这远远不够。生产环境的配额应该覆盖三个维度:
apiVersion: v1
kind: ResourceQuota
metadata:
name: comprehensive-quota
namespace: order-service
spec:
hard:
# ===== 算力维度 =====
requests.cpu: "24"
requests.memory: "96Gi"
limits.cpu: "48"
limits.memory: "192Gi"
# ===== 存储维度 =====
requests.storage: "500Gi"
persistentvolumeclaims: "20"
requests.ephemeral-storage: "200Gi"
# ===== 对象数量维度 =====
pods: "100"
services: "30"
services.loadbalancers: "2"
services.nodeports: "5"
secrets: "50"
configmaps: "50"
replicationcontrollers: "0" # 禁止使用 RC,统一用 Deployment
为什么要覆盖三个维度?看几个真实故障:
故障 1:ConfigMap 爆炸。某团队用 ConfigMap 存配置文件,每个微服务一个 ConfigMap,每个 ConfigMap 包含完整的 YAML 配置。50+ 微服务跑了一年后,ConfigMap 总量超过 200 个,etcd 单个 ConfigMap 的大小限制是 1MB,200 个 ConfigMap 让 etcd 的内存使用涨了 30%。更糟的是 kubectl get cm -n order 返回结果需要 15 秒。
故障 2:Service NodePort 耗尽。开发给每个服务都加了 NodePort 类型的 Service,节点池的可用端口(默认 30000-32767)很快用完。新 Service 创建失败,报错 provided port is already allocated。
故障 3:PVC 无限增长。某日志分析服务每创建一个新索引就申请一个 50Gi 的 PVC,3 个月用了 2TB 存储。集群的存储后端是云盘,费用直接涨了 5 倍。
所以配额不能只看 CPU 和内存。对象数量和存储配额同样关键。
我在实际项目中推荐的比例关系:
| 配额项 | 推荐值 | 计算依据 |
|---|---|---|
| pods | 命名空间 CPU 配额 / 单 Pod 平均 request | 如 24 核 / 0.3 核 ≈ 80,留 20% buffer |
| services | pods 数 / 2 | 不是每个 Pod 都需要独立 Service |
| secrets | pods 数 / 2 | 多个 Pod 共享 Secret |
| configmaps | pods 数 | 每个应用一个 ConfigMap |
| PVC | 10-20 | 根据有状态服务数量 |
| storage | 根据业务需求 | 预估 + 30% buffer |
决策三:用 ScopeSelector 做精细化配额分流
这是 ResourceQuota 最容易被忽略的高级特性。同一个命名空间里,长期运行的服务和 CI/CD 的短生命周期 Job 对资源的需求完全不同。如果用同一个 ResourceQuota 管,CI 的 Job 可能一口气吃掉大量配额,导致正常服务无法扩容。
ResourceQuota 支持 4 种 scope:
| Scope | 含义 | 典型用途 |
|---|---|---|
| Terminating | 会终止的 Pod(设了 activeDeadlineSeconds 或属于 Job) | CI/CD Job |
| NotTerminating | 不会终止的 Pod(正常服务) | 长期运行的服务 |
| BestEffort | 没有设 requests/limits 的 Pod | 不推荐在生产使用 |
| NotBestEffort | 设了 requests/limits 的 Pod | 生产服务 |
我的推荐:在同一个命名空间内创建多个 ResourceQuota,用 scope 做分流。这样 CI Job 的资源消耗不会挤占正常服务的配额:
# 生产服务配额:只管不会终止的 Pod
apiVersion: v1
kind: ResourceQuota
metadata:
name: long-running-quota
namespace: order-service
spec:
hard:
requests.cpu: "20"
requests.memory: "80Gi"
limits.cpu: "40"
limits.memory: "160Gi"
pods: "60"
scopeSelector:
matchExpressions:
- scopeName: NotTerminating
---
# CI/CD Job 配额:只管会终止的 Pod,额度单独切出
apiVersion: v1
kind: ResourceQuota
metadata:
name: ci-job-quota
namespace: order-service
spec:
hard:
requests.cpu: "4"
requests.memory: "16Gi"
limits.cpu: "8"
limits.memory: "32Gi"
pods: "20"
scopeSelector:
matchExpressions:
- scopeName: Terminating
这样配置后,即使 CI 流水线一口气起了 20 个 Job,最多消耗 4 核 CPU 和 16Gi 内存。正常服务的 20 核配额完全不受影响。
多个 ResourceQuota 在同一命名空间内是叠加生效的。K8s 的 admission controller 会检查所有匹配的 ResourceQuota,任何一个触顶都会拒绝新 Pod 创建。这个机制在设计时就要考虑——别让 scope 交叉导致配额互相冲突。
决策四:配额耗尽的告警与自动治理
配额耗尽不是"设了就行"的问题,而是需要持续监控和治理。我在某出行项目中踩过一个坑:配额设了但没告警,直到开发反馈"Pod 起不来"才发现 ResourceQuota 已经触顶好几天了。
告警配置
ResourceQuota 的使用情况可以通过 kubectl describe quota 或 Prometheus 的 kube_resourcequota 指标获取。关键指标:
| Prometheus 指标 | 含义 |
|---|---|
kube_resourcequota{type="hard"} | 配额硬限制 |
kube_resourcequota{type="used"} | 当前使用量 |
kube_resourcequota{type="hard"} - kube_resourcequota{type="used"} | 剩余配额 |
推荐告警规则(PromQL):
# 配额使用率超过 80% 告警
100 * (
kube_resourcequota{type="used"}
/ kube_resourcequota{type="hard"}
) > 80
# Pod 数量接近上限告警(剩余不足 5 个)
kube_resourcequota{resource="pods", type="hard"}
- kube_resourcequota{resource="pods", type="used"} < 5
在 Prometheus 告警规则里建议设两级阈值:80% 走预警通知(钉钉/飞书群),95% 走紧急告警(电话/短信)。80% 时还来得及评估是否需要扩容配额,95% 时已经离触顶一步之遥了。
关于告警体系的建设,可以参考 相关文章:告警策略设计:从噪声到信号 的分层思路。
自动化治理:命名空间巡检
配额治理不只是设值,还包括清理无效命名空间。我在某电商大促前夜踩过一个坑——CI/CD 流水线每次构建都新建临时命名空间(如 ci-build-20261015-001234),跑完忘了清理。两周后集群堆了 300+ 个空命名空间,每个挂着 ResourceQuota 和 LimitRange 对象。虽然没 Pod,但 300+ 个 ResourceQuota 对象让 controller-manager 的同步循环 CPU 持续飙高,APIServer 响应延迟从 100ms 涨到 2s。
修复方案是一个 CronJob,每天凌晨扫描并清理 7 天前无 Pod 的命名空间:
apiVersion: batch/v1
kind: CronJob
metadata:
name: namespace-cleaner
namespace: kube-system
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
serviceAccountName: namespace-cleaner-sa
containers:
- name: kubectl
image: bitnami/kubectl:1.31
command:
- /bin/sh
- -c
- |
# 清理 7 天前创建且无 Pod 的 ci-build- 命名空间
kubectl get namespaces -o json | jq -r '
.items[]
| select(.metadata.name | startswith("ci-build-"))
| select(.metadata.creationTimestamp
| . < (now - 604800 | todate))
| .metadata.name
' | while read ns; do
pod_count=$(kubectl get pods -n "$ns" --no-headers 2>/dev/null | wc -l)
if [ "$pod_count" -eq 0 ]; then
echo "Deleting empty namespace: $ns"
kubectl delete namespace "$ns" --ignore-not-found
fi
done
restartPolicy: OnFailure
这个 CronJob 的逻辑很简单:找所有以 ci-build- 开头、创建超过 7 天、且没有 Pod 的命名空间,删掉。加 7 天的 buffer 是防止误删正在使用中的命名空间。
配额与调度器的交互:容易被忽略的陷阱
ResourceQuota 的配额检查发生在 admission 阶段,也就是 API 请求被写入 etcd 之前。但调度器的调度决策发生在 Pod 创建之后。这意味着一个 Pod 通过了配额检查(被允许创建),但不一定能被调度成功。
这个时间差会导致一个现象:配额显示"已用 24/24 核",但实际运行的 Pod 可能只有 20 个在 Running,其余 4 个卡在 Pending。配额被"预扣"了但没实际使用。
在 相关文章:Kubernetes 调度器原理与调优 中详细讲过调度器的工作机制。这里只说它和配额的交互影响:
配额虚高:如果命名空间里有大量 Pending Pod,配额显示已满但实际资源没用完。其他 Pod 创建被拒绝,但节点资源其实还有富裕。解决方案是监控 Pending Pod 数量,超过阈值时告警,人工介入清理。
删除 Pod 不立即释放配额:删除一个 Pod 后,配额的 used 值不会立刻减 1。controller-manager 的 ResourceQuota 控制器有同步周期(默认 5 分钟),在这期间配额数据是"旧账"。如果你在删除 Pod 后立刻创建新 Pod,可能因为配额显示已满而被拒绝。遇到这种情况等几分钟再试。
HPA 扩容被配额卡住:相关文章:Kubernetes 自动扩缩容:HPA/VPA/CA 深度解析 中讲过 HPA 的扩容逻辑。HPA 根据 CPU 利用率自动扩副本数,但如果命名空间配额已满,新 Pod 创建失败,HPA 的扩容动作白做。生产环境配 HPA 的命名空间,配额要留 30% 以上 buffer 给扩容用。
QoS 等级与配额的协同设计
QoS(Quality of Service)是 K8s 给 Pod 分的三个等级,决定了节点资源紧张时的驱逐顺序:
| QoS 等级 | 条件 | 驱逐优先级 | 适用场景 |
|---|---|---|---|
| Guaranteed | requests == limits(所有容器) | 最低(最后被驱逐) | 核心生产服务 |
| Burstable | requests < limits | 中间 | 一般业务服务 |
| BestEffort | 不设 requests 和 limits | 最高(最先被驱逐) | 不推荐生产使用 |
ResourceQuota 和 QoS 的协同点在于:如果 ResourceQuota 限制了 cpu/memory 的 requests,那么命名空间内的 Pod 必须设置 requests。这意味着 BestEffort Pod(不设 resources 的)无法在该命名空间创建。这其实是好事——生产环境本来就不应该有 BestEffort Pod。
但有个坑:如果你用了 scope BestEffort 的 ResourceQuota 来限制 BestEffort Pod 的数量,而主 ResourceQuota 限制了 requests.cpu,那 BestEffort Pod 根本无法创建(因为不设 requests 会被拒绝)。这两个配置互相矛盾。解决方式是:生产命名空间不配 BestEffort scope 的 ResourceQuota,直接用 NotBestEffort scope 的 ResourceQuota 把 BestEffort Pod 挡在外面。
# 只允许设置了 requests/limits 的 Pod
apiVersion: v1
kind: ResourceQuota
metadata:
name: no-besteffort-quota
namespace: production
spec:
hard:
pods: "100"
scopeSelector:
matchExpressions:
- scopeName: NotBestEffort
这样配置后,任何不设 resources 的 Pod 都会被拒绝创建。生产环境强制所有 Pod 至少是 Burstable,核心服务设成 Guaranteed。
生产环境配额模板
把我验证过的完整模板贴出来,可以直接用。这是一个中等规模团队(30-50 个微服务)的生产命名空间配额方案:
# ============ LimitRange ============
apiVersion: v1
kind: LimitRange
metadata:
name: production-limits
namespace: team-order
spec:
limits:
- type: Container
default:
cpu: "1000m"
memory: "1Gi"
defaultRequest:
cpu: "200m"
memory: "256Mi"
max:
cpu: "8"
memory: "16Gi"
min:
cpu: "50m"
memory: "64Mi"
- type: Pod
max:
cpu: "16"
memory: "32Gi"
min:
cpu: "100m"
memory: "128Mi"
- type: PersistentVolumeClaim
max:
storage: "100Gi"
min:
storage: "5Gi"
---
# ============ 长期服务配额 ============
apiVersion: v1
kind: ResourceQuota
metadata:
name: service-quota
namespace: team-order
spec:
hard:
requests.cpu: "20"
requests.memory: "80Gi"
limits.cpu: "40"
limits.memory: "160Gi"
requests.storage: "500Gi"
persistentvolumeclaims: "20"
requests.ephemeral-storage: "100Gi"
pods: "60"
services: "30"
services.loadbalancers: "2"
services.nodeports: "3"
secrets: "40"
configmaps: "40"
scopeSelector:
matchExpressions:
- scopeName: NotTerminating
---
# ============ CI/CD Job 配额 ============
apiVersion: v1
kind: ResourceQuota
metadata:
name: ci-job-quota
namespace: team-order
spec:
hard:
requests.cpu: "4"
requests.memory: "16Gi"
limits.cpu: "8"
limits.memory: "32Gi"
pods: "20"
scopeSelector:
matchExpressions:
- scopeName: Terminating
---
# ============ 禁止 BestEffort Pod ============
apiVersion: v1
kind: ResourceQuota
metadata:
name: block-besteffort
namespace: team-order
spec:
hard:
pods: "0"
scopeSelector:
matchExpressions:
- scopeName: BestEffort
最后一组 block-besteffort 的设计很巧妙:把 BestEffort scope 的 pods 配额设为 0,效果是不设 resources 的 Pod 无法创建。比单独配 LimitRange 更直接。
配额初始化自动化
新建命名空间时手动应用这些 YAML 不现实。推荐用 K8s 的 Namespace 初始化机制——通过 Admission Webhook 或 Kyverno 策略自动注入:
# Kyverno 策略:新建命名空间时自动注入 LimitRange 和 ResourceQuota
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: inject-namespace-quota
spec:
rules:
- name: inject-limits
match:
resources:
kinds:
- Namespace
generate:
apiVersion: v1
kind: LimitRange
name: default-limits
namespace: "{{request.object.metadata.name}}"
data:
spec:
limits:
- type: Container
default:
cpu: "1000m"
memory: "1Gi"
defaultRequest:
cpu: "200m"
memory: "256Mi"
max:
cpu: "8"
memory: "16Gi"
min:
cpu: "50m"
memory: "64Mi"
这样每次 kubectl create namespace 后,LimitRange 自动就位。不用等运维手动配,也不会有"裸奔"的命名空间。
配额管理的常见反模式
最后列几个我反复见到的反模式,帮你避开:
反模式 1:所有命名空间用同一套配额
测试环境、预发环境、生产环境的资源需求差异很大。给所有命名空间统一配 24 核 CPU,测试环境浪费、生产环境不够。正确做法是按环境分档:测试 8 核、预发 16 核、生产 32 核。
反模式 2:配额设了就不调
业务在增长,配额也需要定期调整。建议每季度复盘一次配额使用率,低于 50% 的考虑缩减,高于 80% 的考虑扩容。配额不是"设了就固定"的,它应该跟着业务节奏走。
反模式 3:用配额替代容量规划
ResourceQuota 是资源治理工具,不是容量规划工具。配额设得再好,集群总资源不够也是白搭。配额解决的是"公平分配"问题,容量规划解决的是"够不够用"问题。两者不能互相替代。
我在自研运维平台时做过一个功能:每周自动生成一份"命名空间资源使用报告",列出每个命名空间的配额、实际使用量、Pending Pod 数量、配额使用率趋势。运维不用手动 kubectl describe quota 巡检,报告自动推送到群里。这个功能上线后,配额相关的工单减少了 70%。
反模式 4:忽略 requests 和 limits 的差值
有些团队为了"安全",把 requests 和 limits 设成一样的值(Guaranteed QoS)。这在核心服务上是对的。但对批处理任务或 CI Job,requests=limits 意味着节点资源利用率低下——调度器会按 limits 预留资源,即使 Pod 实际只用了 requests 的量。Guaranteed 适合核心服务,Burstable 适合一般业务,别一刀切。
反模式 5:不监控 Pending Pod 的配额原因
Pod 卡在 Pending 有很多原因:节点资源不足、调度约束不满足、镜像拉不下来。但有一个原因是配额耗尽——这个原因经常被忽略,因为 Pod 的 Events 里会显示 Forbidden: exceeded quota,但很多人不看 Events。建议在告警体系里加一条:Pending Pod 数量超过 5 个时,自动检查是否是配额原因。
总结
回到开头那个故障。最终的处理方式是:给 common 命名空间补上 LimitRange(设了 max.memory=4Gi),强制每个 Pod 设 limits。同时拆分了 ResourceQuota,把 CI Job 的配额用 scope 隔离。大促第二天重启后,再没出现过资源争抢导致的服务降级。
四条决策的核心逻辑:
- LimitRange + ResourceQuota 成对出现:LimitRange 管单 Pod 的"畸形配置",ResourceQuota 管命名空间的"总量天花板"。少了任何一个都有漏洞。
- 三层配额维度:算力(CPU/内存)、存储(PVC/ephemeral)、对象数量(pods/services/secrets/configmaps),缺一不可。对象数量爆炸和存储失控一样会搞垮集群。
- ScopeSelector 精细化分流:长期服务和 CI Job 用不同配额池,避免 CI 吃掉服务资源。多个 ResourceQuota 叠加生效,用 scope 隔离互不干扰。
- 配额告警 + 自动治理:80% 预警、95% 紧急、Pending Pod 检查配额原因、CronJob 自动清理无效命名空间。配额不是设了就完事,需要持续运维。
K8s 的资源治理本质是"账本管理"——ResourceQuota 是总账,LimitRange 是审批规则,QoS 是优先级。三者协同才能让多租户集群跑得稳。别等 Pod 集体 Pending 了才想起来配配额。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- Resource Quotas — Kubernetes 官方文档,ResourceQuota 的完整概念定义、配额类型、scope 机制说明
- Limit Ranges — Kubernetes 官方文档,LimitRange 的默认值注入和范围校验机制
- Pod Quality of Service Classes — Kubernetes 官方文档,QoS 等级划分标准与驱逐优先级
- Configure Memory and CPU Quotas for a Namespace — Kubernetes 官方教程,命名空间配额配置实操
- Kubernetes 中的资源限制管理 — 腾讯云开发者社区,ResourceQuota、LimitRange、Resource Limits 三者协同关系的图解
- Kubernetes 防不胜防的 11 个陷阱 — 百家号,不设置资源请求的 BestEffort Pod 风险和 QoS 优化建议