资源隔离与配额管理

核心结论

在 Kubernetes 命名空间级资源管理中,ResourceQuota(资源配额) 和 LimitRange(限制范围) 是一对互补的安全防护锁。

  • ResourceQuota 作用于全局:控制整个命名空间内所有资源的总和上限(如“开发空间总共不能申请超过 32 核 CPU”)。
  • LimitRange 作用于单体:为空间内单个容器注入默认值,并设定其 request/limit 的上下限。
  • 核心安全陷阱:一旦开启了 CPU/Memory 的 ResourceQuota,该命名空间内的所有 Pod 必须显式声明 request/limit,否则无法通过 API Server 校验。 为避免业务部署大面积失败,生产中必须同时部署 LimitRange 来提供默认值。

深度分析 / 代码实现

1. ResourceQuota 与 LimitRange 的本质区别

特性 ResourceQuota (资源配额) LimitRange (限制范围)
控制粒度 命名空间全局(聚合) 单体容器 / 单体 Pod
工作原理 统计命名空间内所有活跃 Pod 的 Request/Limit 累加值。 对新创建的 Pod 进行单体属性校验,并在缺失参数时动态注入默认值。
典型场景 “Dev 环境的总预算只有 16 核 32G 内存”。 “禁止研发创建不设资源上限的野跑 Pod”、 “默认给新容器分配 256Mi 内存”。

2. 限制命名空间资源、最大 Pod 与 PVC 数量的 YAML 示例

以下配置将限制 dev-env 命名空间的 总资源上限、最大 Pod 创建数以及最大 PVC 申请数:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-quota
  namespace: dev-env
spec:
  hard:
    # 1. 内存和 CPU 总量限制 (包括请求量 Requests 与上限值 Limits)
    requests.cpu: "8"          # 整个空间所有 Pod 的 CPU request 累加不能超过 8 核
    requests.memory: 16Gi      # 内存 request 累加不能超过 16Gi
    limits.cpu: "16"           # CPU limit 累加不能超过 16 核
    limits.memory: 32Gi        # 内存 limit 累加不能超过 32Gi

    # 2. 对象数量限制
    pods: "20"                 # 该空间内最多容纳 20 个处于活动状态的 Pod
    persistentvolumeclaims: "10" # 该空间最多只能创建 10 个 PVC
    services: "15"             # 最多只能创建 15 个 Service

3. 设置默认 CPU/Memory Request 和 Limit

使用 LimitRange 可以为没有写配置的 Pod 注入默认限制,并强制约束任何单体容器的上下限边界:

apiVersion: v1
kind: LimitRange
metadata:
  name: dev-container-limits
  namespace: dev-env
spec:
  limits:
  - type: Container            # 针对容器级别限制
    default:                    # 如果容器没写 limit,默认自动注入此上限
      cpu: "500m"
      memory: 512Mi
    defaultRequest:             # 如果容器没写 request,默认自动注入此请求值
      cpu: "100m"
      memory: 256Mi
    max:                        # 任何单体容器显式声明的 limit 不能超过该最大值
      cpu: "2"
      memory: 4Gi
    min:                        # 任何单体容器显式声明的 request 不能低于该最小值
      cpu: "50m"
      memory: 64Mi

4. 资源超出配额的行为与排查流程

① 准入检查阶段:超出 ResourceQuota 声明配额

  • 行为:ResourceQuota 检查的是 Pod 声明的 Request/Limit。如果新创建的 Pod(或副本集扩容)会导致其声明的总和超出硬性配额,API Server 将当场拒绝创建请求。
  • 表现:执行 kubectl apply 时直接报错:Error from server (Forbidden): exceeded quota...。如果是 Deployment 扩容失败,Pod 不会被创建。
  • 排查方法:
    1. 检查关联的 ReplicaSet 状态,查看报错事件:
      kubectl describe rs <replicaset-name> -n dev-env
      
    2. 直接捞取空间内的拒绝事件:
      kubectl get events -n dev-env --field-selector reason=FailedCreate
      

② 运行时阶段:Pod 实际消耗内存超出其 Limit

  • 行为:ResourceQuota 本身不监视实际物理内存的消耗(只核对 spec 声明)。一旦容器运行时消耗的物理内存超出了自身设定的单体 limits.memory,宿主机内核将触发 OOM (Out Of Memory) Killer。
  • 表现:Pod 的状态会变为 OOMKilled 或 CrashLoopBackOff。
  • 排查方法:
    # 1. 观察 Pod 的状态变化
    kubectl get pod -n dev-env
    
    # 2. 查看容器终止原因是否为 OOMKilled
    kubectl describe pod <pod-name> -n dev-env | grep -A 5 "Last State"
    

5. 查看 Namespace 当前的资源消耗

① 使用 kubectl top 实时流量查询

kubectl top 依赖 metrics-server,支持按 Namespace 维度查询正在发生物理资源消耗的 Pod(但不支持直接以单行汇总整命名空间的物理总消耗):

# 查询指定命名空间下所有 Pod 的实时物理 CPU/内存 使用量
kubectl top pod -n dev-env

② 验证 ResourceQuota 额度消耗(推荐)

查看声明配额与实际配额的对比,这是最权威的配额查询手段:

kubectl describe quota -n dev-env

输出格式示例:

Resource                         Used  Hard
--------                         ----  ----
limits.cpu                       2     16
limits.memory                    4Gi   32Gi
pods                             4     20

注意事项

1. 强制声明的“连带陷阱”

一旦激活了 ResourceQuota 限制 CPU 或内存总量,那么这个 Namespace 中创建的每一个容器都必须显式指定 resources.requests 和 resources.limits。如果有任何一个 Pod 没写,部署就会被 API Server 直接阻断并报错:is forbidden: must specify limits.cpu...。

  • 防范方案:在部署 ResourceQuota 时,必须同时配置并应用对应的 LimitRange,利用 default 和 defaultRequest 参数为没有配置的业务提供降级兜底。

2. 停滞状态 Pod 依然占用 Quota 额度

处于 Completed (已完成)、Failed (执行失败) 以及 Unknown 状态但尚未被物理删除的 Pod,其声明的资源依然算作已使用额度并计入 ResourceQuota。

  • 防范方案:生产环境中的任务类应用(如 CronJob/Job)必须配置 successfulJobsHistoryLimit 和 failedJobsHistoryLimit,避免历史残留 Pod 塞满命名空间配额。
posted @ 2026-06-07 21:45  今夕何兮  阅读(61)  评论(0)    收藏  举报