Kubernetes 多租户与资源治理:Namespace、Quota、QoS 与调度落地

深入解析 Kubernetes 多租户与资源治理体系:Namespace 作为租户边界、ResourceQuota 与 LimitRange 的配额与限制、requests/limits 与 QoS 等级(Guaranteed/Burstable/BestEffort)、多租户的隔离与安全、descheduler 与节点资源饱和度治理,以及资源预测与成本归属。

当集群从"几个团队共用"变成"几十个团队、几百个命名空间共存",资源就开始打架:一个团队狂拉资源把别人挤爆、错误的 requests 让调度器预判失准、节点利用率忽高忽低。多租户与资源治理的目标是:让每个团队在"自己的配额盒子里"自由运行,同时整个集群的资源被调度器高效、公平地使用。


目录


1. 租户边界的建立:Namespace 与资源

1.1 Namespace 是逻辑租户

Namespace 提供的隔离维度:
  - 资源名隔离:同名 Deployment 可在不同 ns 共存
  - 资源配额:ResourceQuota 按 ns 限制总量
  - 权限隔离:RBAC 按 ns 授权(Role / RoleBinding)
  - 网络隔离:NetworkPolicy 按 ns 生效
  - 成本归属:按 ns 打标签 + 成本分析

一个生产集群的常见 ns 划分:
  - platform(平台/基础设施)
  - shop,payment,inventory(业务团队)
  - dev / staging / prod(不同环境,若混合部署)
  - 注意:把"环境"和"团队"混在一个维度会导致权限与配额纠缠

ℹ️ 原则:ns 粒度要配合"团队/产品的运维边界"。太多 ns 增加管理成本,太少无法隔离。

1.2 用标签体系支撑治理

# 为每个 ns 打标准标签,作为配额/成本/权限的统一维度
metadata:
  name: payment
  labels:
    team: "payments-core"
    env: "prod"
    cost-center: "billing"
    criticality: "high"

2. ResourceQuota:给租户划定资源上限

2.1 配额 CPU/内存/对象数量

apiVersion: v1
kind: ResourceQuota
metadata:
  name: payment-quota
  namespace: payment
spec:
  hard:
    requests.cpu: "8"
    requests.memory: 16Gi
    limits.cpu: "16"
    limits.memory: 32Gi
    count/pods: "100"
    count/services: "20"
    count/persistentvolumeclaims: "30"
ResourceQuota 会拒绝"超过配额的创建":
  创建 Pod 时 requests.cpu 超了 → API 拒绝(403)
  这保证了 ns 内总资源不超过配额

scopeSelector 细粒度配额:按 QoS / 阶段 / 状态:
  spec.scopes及匹配 label 可以按 Pod 类型(qos 等级)单独配额

典型治理:
  requests 和 limits 分开配额
  → 防止"requests 很低但 limits 拉很高"的投机

2.2 配额检查命令

kubectl get resourcequota -n payment
kubectl describe resourcequota payment-quota -n payment
# 看到已用 vs 配额,快速判断一个 ns 是否吃紧

3. LimitRange:给单个 Pod 定资源范围

3.1 为什么还要 LimitRange

ResourceQuota 管"ns 总量",LimitRange 管"单个 Pod 的边界":
- 防止某个 Pod 把配额/Slot 全占
- 没写 requests/limits 的 Pod 自动给默认值
- 约束 ratio:limit 不能是 request 的任意倍(防请求/Giant)

组合:Quota 划"总量" + LimitRange 划"单件" + QoS 定义优先级

3.2 LimitRange 配置

apiVersion: v1
kind: LimitRange
metadata:
  name: payment-lr
  namespace: payment
spec:
  limits:
    - max:                    # 单个 Pod 上限
        cpu: "4"
        memory: 8Gi
      default:                # 未声明时的默认 limit
        cpu: "500m"
        memory: 512Mi
      defaultRequest:         # 未声明时的默认 requests
        cpu: "100m"
        memory: 128Mi
      type: Container
LimitRange 作用时机:Pod 创建时(Admission 阶段)
- 违反 max/min → 拒绝创建
- 未声明 requests/limits → 注入默认
→ 防止"裸奔"的 Pod 抢占,也防止限制过于宽松

4. requests 与 limits:资源预留的调度语义

4.1 requests = 调度依据,limits = 运行上限

resources:
  requests:
    cpu: 500m
    memory: 512Mi
  limits:
    cpu: "1"
    memory: 1Gi
requests(请求/保留):
  - 调度器据此决定"放在哪个节点"(节点剩余 >= 所有 Pod requests)
  - 实际运行中 requests 是被"保证"的最低值
  - 例子:requests.cpu=500m 保证能拿到约半个核

limits(上限):
  - 超过上限会被限制/被杀
  - CPU limit:CPU 限制(不硬 kill)
  - 内存 limit:超过即 OOM(内核 kill,QoS 决定谁先死)

常见认知误区:
  - 只有 limits 没 requests → 调度不保护,且对钱包不利
  - requests 设太高 → 节点利用率虚胖,调度挤爆
  - limits 设太低 → 高峰 OOM

4.2 推荐设置策略

理想:requests 反映"稳态基准使用",limits 反映"可容忍的峰值"
  - 先用 VPA 或 观察 看真实用量(prometheus 分位数)
  - requests ≈ P50~P75 峰值用量(安全冗余)
  - limits ≈ 1.5x ~ 2x requests(给峰值弹性)
  - 保留多容器/僵尸团队的限值审评

注意:requests 太保守 → 节点利用率虚低 → Cluster Autoscaler 误判需要扩容,过早加节点。
      requests 太高 → 调度器按 requests 预留,节点实际跑不满却"看起来满",利用率虚高。

5. QoS 等级:Guaranteed / Burstable / BestEffort

5.1 三个等级的判定

QoS 由 requests 与 limits 是否一致决定:
  Guaranteed:
    requests == limits(cpu 与 memory 都不缺)
    例:requests.cpu=500m limits.cpu=500m
  Burstable:
    有 requests,且至少一个维度 requests != limits
  BestEffort:
    完全不设 requests / limits

内存不足时内核先杀谁:
  BestEffort > Burstable(超配较多) > Burstable(超配较少) > Guaranteed
→ QoS 等级 = Pod 在资源紧张时的"生存优先级"

5.2 建议把关键服务设为 Guaranteed

# 关键服务:requests == limits,最稳也最贵
resources:
  requests: { cpu: "1", memory: 2Gi }
  limits:   { cpu: "1", memory: 2Gi }
为什么关键服务用 Guaranteed:
- 内存上限确定 → 不会因兄弟 Pod 抢占被 OOM
- 调度与运行行为可预测
- 监控/排障清晰(不会因为 OOM 而"错杀")

Burstable 适合弹性批处理:可以用较高 limits、较低 requests
BestEffort 基本别用:随时可能被 OOM,且不能预测

6. 多租户隔离:网络、安全与命名空间治理

6.1 网络隔离 NetworkPolicy

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-deny-all
  namespace: payment
spec:
  podSelector: {}          # 该 ns 所有 Pod
  policyTypes: [Ingress, Egress]
  ingress: []
  egress: []
---
# 允许来自 shop 的 80 端口入站
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-allow-shop
  namespace: payment
spec:
  podSelector:
    matchLabels: { app: api }
  ingress:
    - from:
        - namespaceSelector:
            matchLabels: { name: shop }
      ports: [ { protocol: TCP, port: 8080 } ]
租户间网络隔离的原则:
- 默认拒绝(deny-all),显式放行
- 只开业务必需通道
- 依赖 CNI 支持(Calico/Cilium 都支持 NetworkPolicy)

注意:NetworkPolicy 只是"网络侧"隔离,
  资源的 RuntimeClass(如 gVisor / kata-container)可做更强的运行时隔离。

6.2 权限与 RBAC 视角

租户 = 一组 namespace + 一组 RBAC 权限 + 一组配额 +
      网络策略 + 成本归属标签
推荐用"租户/项目 CRD"(如 HNC,见第 7 节)统一管理这五者,
避免手写散件的 namespace/role/quota/networkpolicy。

- Role / RoleBinding 限 namespace 内权限
- 集群管理员避免 Team 随便建 Global 特权

7. 层级命名空间(HNC)与 Quota 罐

7.1 层级命名空间 Hierarchical Namespaces

apiVersion: hnc.x-k8s.io/v1alpha2
kind: SubnamespaceAnchor
metadata:
  name: payments-core
  namespace: org-root
spec:
  hierarchicalQuota: true   # 继承父级配额
HNC(sig-arch)的作用:
  - 父 ns 与子 ns 建立层级(组织树)
  - 配额、RBAC、策略可"继承"到子 ns
  - 子 ns 创建的 quota 会汇总到父级(配额罐 Quota Pool)
  - 适合"部门 → 团队 → 项目"的组织结构映射

配额罐(Quota Pool):
  - 整个组织一个总池,子团队在此池内分配
  - 比每个团队独立配额更易治理、防闲置

7.2 硬件隔离与软治理权衡

软隔离(默认,推荐起步):
  Namespace + quota + RBAC + NetworkPolicy
  → 共享底层,成本低,管理简单

硬隔离(高安全要求):
  独立集群 / 独立节点池 / RuntimeClass 沙箱 / 专用存储
  → 成本高、管理复杂,留给合规/强安全租户

决策:先软隔离,的确有安全/合规要求叠加硬隔离

8. 资源饱和度与 descheduler 调度

8.1 资源饱和与"热点节点"

调度只看 requests(创建时刻),会随时间漂移:
  - 起初均衡的节点,慢慢一个被掏空、一个闲置
  - 新增 Pod 又继续往空闲节点堆 → 惰性不均匀
  
descheduler 的作用:
  - 识别"过度饱和/空转"的节点
  - 触发受影响 Pod 重调度(逐驱逐到更均衡的节点)
  - 需配合集群容忍短暂资源变动(除 pod PDB)

8.2 descheduler 配置

apiVersion: v1
kind: ConfigMap
metadata:
  name: descheduler-policy
  namespace: kube-system
data:
  policy.yaml: |
    apiVersion: descheduler/v1alpha2
    kind: DeschedulerPolicy
    strategies:
      - name: "LowNodeUtilization"
        params:
          nodeResourceUtilizationThresholds:
            thresholds:
              cpu: 20
              memory: 20
            targetThresholds:
              cpu: 60
              memory: 60
    # 节点 cpu<20% 视为 low,>60% 视为 high;均衡覆盖
  nodeResourceUtilizationThresholds:
    ...
descheduler 是"被动纠偏"(不保证实时),
适合:节点负载长期失衡 / 多可用区流量不均
不适合:想即时再平衡(要等规则触发)

注意:descheduler 会驱逐 Pod → 必须配合 PDB 与 controller

9. 生产最佳实践与避坑

9.1 Checklist

□ 用 Namespace 划分租户 + 标准标签(team/env/cost)
□ 每个租户:ResourceQuota(总量)+ LimitRange(单件边界)
□ 关键服务设 Guaranteed QoS(requests==limits)
□ requests 基于真实用量度量(VPA/监控),limits 留峰值弹性
□ 默认拒绝网络 + 显式放行(NetworkPolicy)
□ RBAC 最小权限、按 ns 授权
□ 组织结构复杂 → 用 HNC 层级 + 配额罐
□ 长期负载失衡 → descheduler 自动疏通
□ 监控:quota 使用率、QoS 分布、节点饱和度、OOM 次数

9.2 常见坑

坑现象对策
只设 limits 无 requests调度挤爆/利用率低requests 设基准用量
requests 虚高节点放几个就满用真实度量校准
无 ResourceQuota团队无限拉配额每租户设 Quota
无 LimitRange单 Pod 独占资源设定单个 Pod 边界
关键服务 BestEffort高峰被 OOM设 Guaranteed
无 NetworkPolicy租户相互可达默认拒绝
无 descheduler节点长期失衡引入 rebalance

9.3 一句话原则

Quota 圈住总量,LimitRange 圈住单件,
Guaranteed 保住关键,descheduler 疏解失衡。

小结

多租户与资源治理 = 给每个团队一个"有边界、可计量、能隔离"的盒子:Namespace 划边界、ResourceQuota 圈总量、LimitRange 定单件、QoS 分优先、NetworkPolicy 隔离网络、descheduler 调平衡。落地记住五件事:Namespace 定租户 + 打标准标签、Quota+LimitRange 圈住总量与单件、requests 用真实用量校准 + 关键服务 Guaranteed、网络默认拒绝、长期失衡交给 descheduler。当每个团队都在自己清晰的配额盒子里运行,集群资源的分配才从"争抢的混沌"变成"有秩序的经济学"。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「云原生」更多文章

  1. Kubernetes 备份容灾与数据保护:Velero、etcd 与恢复演练实战
  2. Kubernetes 渐进式交付:Argo Rollouts、金丝雀/蓝绿与流量治理实战
  3. Kubernetes 集群排障与诊断:从 Pod 症状到节点/集群根因的实战手册