Requests、Limits、ResourceQuota 与 PDB

资源配置不是装饰,它决定能否调度和如何失效

本项目要求 CPA 与 cpa-manager 都明确填写 CPU、内存的 requests 和 limits:

1
2
3
4
5
6
7
resources:
  requests:
    cpu: 200m
    memory: 256Mi
  limits:
    cpu: 300m
    memory: 512Mi

这四个值既影响 Scheduler,也影响容器运行时和 Namespace 配额。它们不是“给监控看的备注”。

requests:我至少要预留多少

Scheduler 主要根据 requests 判断 Node 是否放得下 Pod。

CPU 200m 表示 0.2 个 CPU core 的请求。1000m 等于 1 core。

内存 256Mi 使用二进制单位:

1
2
1 Mi = 1024 × 1024 bytes
1 Gi = 1024 Mi

一个 Pod 中有多个普通容器时,调度通常考虑它们 requests 的总和。本项目 runtime Pod 同时运行 CPA 与 cpa-manager,因此号池的基础计算需求是二者之和。

InitContainer 按顺序运行,Kubernetes 会把普通容器总和与单个 InitContainer 的最大值进行有效资源计算。本项目初始化容器复用 CPA 的资源声明,而普通容器总和包含 CPA 加 Manager,因此有效值仍由普通容器总和主导。

requests 太低的后果不是“应用只能用这么多”,而是集群可能在一台 Node 上塞入过多 Pod,压力出现时应用性能剧烈抖动或被驱逐。

limits:最多允许使用多少

CPU 超过 limit 时通常被 throttling,也就是被限制执行时间,不会只因为超过 CPU limit 就直接杀死容器。

内存不同。容器实际内存超过 limit 时,可能被 OOM kill,然后由 kubelet 按 restart policy 重启。

因此:

1
2
CPU limit 太紧 → 延迟变高、吞吐下降、探针可能超时
内存 limit 太紧 → OOMKilled、容器重启、CrashLoopBackOff

查看上一次终止原因:

1
2
kubectl -n subpool-demo-pool get pod runtime-0 \
  -o jsonpath='{range .status.containerStatuses[*]}{.name}{"\t"}{.lastState.terminated.reason}{"\n"}{end}'

requests 与 limits 的关系

本项目校验:

  • 值必须大于 0;
  • 只能声明 CPU 和内存;
  • requests 和 limits 都必须完整;
  • limit 不能小于 request。

例如:

1
2
requests.cpu = 500m
limits.cpu   = 1

调度时按 0.5 core 预留,运行时最多允许使用约 1 core。

若 request 与 limit 相差很大,集群可以提高利用率,但资源争抢时性能更不稳定。数值应基于真实监控调整,而不是照抄示例。

QoS Class:压力下谁更容易受影响

Kubernetes 根据每个容器的 requests/limits 给 Pod 分 QoS class:

  • Guaranteed:所有容器的 CPU、内存 request 都等于对应 limit;
  • Burstable:至少声明了一些 request/limit,但不满足 Guaranteed;
  • BestEffort:完全没有声明 request/limit。

项目要求完整声明资源,但示例中 request 与 limit 不相等,因此 runtime Pod 通常是 Burstable,而不是 Guaranteed。

QoS 会影响节点内存压力下的驱逐优先级,但它不是唯一因素,实际还要看使用量相对 request、优先级和节点状态。

Operator 的资源包络

Operator Configuration 定义单 Pool 最大值:

1
2
3
4
5
6
7
8
9
resourcePolicy:
  maxPerPool:
    requests:
      cpu: "3"
      memory: 4Gi
    limits:
      cpu: "4"
      memory: 8Gi
    storage: 20Gi

Operator 在创建 Namespace、PVC 和 Secret 等持久资源之前,先计算 CPA 与 cpa-manager 的合计申请。超出包络时报告 ResourcesExceedBaseline,不创建半套资源。

这个顺序很重要。若先创建 PVC 和 Secret,最后才发现 CPU 超限,集群里会留下难以解释的半成品。

ResourceQuota:给 Namespace 装上总闸

每个号池 Namespace 都会得到一个 ResourceQuota,hard limits 来自平台最大包络,而不是当前 CR 的实际申请:

1
2
3
4
5
6
requests.cpu
requests.memory
limits.cpu
limits.memory
requests.storage
persistentvolumeclaims: 1

为什么不直接把 Quota 设成当前申请量?因为用户可能在允许范围内调整 CPU 和内存。如果 Quota 只等于旧值,更新工作负载时可能先被 Quota 阻止,控制器还要同步处理两个互相约束的对象。

使用平台最大包络后:

1
2
3
SubPool spec 决定当前想用多少
Operator 校验决定申请是否合法
ResourceQuota 防止 Namespace 内总用量突破平台上限

这是应用层校验和 Kubernetes 原生硬限制的双层防线。

为什么没有 LimitRange

LimitRange 常用于给未声明资源的容器补默认值,或限制单个容器的范围。本项目要求 SubPool 明确声明每个容器的 requests/limits,Operator 又负责校验并投影,所以不需要 LimitRange 自动补值。

显式输入有两个好处:

  • 用户提交的 CR 就能看出真实资源意图;
  • 不会因不同 Namespace 的默认值而产生隐式差异。

控制器还会删除属于本 Pool 的旧版 LimitRange,避免遗留策略与新契约冲突。

PodDisruptionBudget 在保护什么

项目为 runtime 和 Operator、Mihomo 各配置了 PDB,常见形式是:

1
2
spec:
  minAvailable: 1

PDB 限制的是自愿中断中的并发不可用数,例如:

  • 管理员执行 node drain;
  • 集群自动缩容驱逐 Pod;
  • 某些受 eviction API 管理的维护操作。

它告诉执行驱逐的组件:“至少保留一个满足 selector 的可用 Pod。”

PDB 不是什么

PDB 不会:

  • 创建额外副本;
  • 阻止 Node 突然宕机;
  • 阻止容器崩溃;
  • 阻止用户直接删除 Pod 或控制器;
  • 修复探针或应用故障;
  • 让一个 RWO 单副本应用自动变成高可用。

runtime 只有一个 replica,minAvailable: 1 意味着 drain 可能无法驱逐它,除非使用者处理预算或接受中断。它更像“维护刹车”,不是“永不断线保险”。

Operator 与 Mihomo 各有两个副本,PDB 才能在自愿中断时允许一个副本离开、同时保留另一个可用。但 Operator 还需要 leader election,Mihomo 两个副本则各自维护连接选择状态。

ResourceQuota 与实际 Node 容量是两回事

Quota 允许一个 Pool 请求 3 CPU,不代表集群此时一定有 Node 能提供 3 CPU。Quota 是“最多准许申请多少”,Scheduler 仍要根据实际 Node 可分配资源决定能否调度。

因此 Pod Pending 时要区分:

  • API 创建被 ResourceQuota 拒绝;
  • Pod 已创建,但 Scheduler 报 Insufficient cpu/memory;
  • PVC 或 topology 阻止调度;
  • taint、affinity 等其他约束阻止调度。

检查:

1
2
3
kubectl -n subpool-demo-pool describe resourcequota
kubectl -n subpool-demo-pool describe pod runtime-0
kubectl get nodes

调参的正确闭环

不要仅凭“CPU 经常到 100%”就盲目加 limit,也不要为了调度成功把 request 压到极低。更可靠的闭环是:

  1. 观察一段有代表性的 CPU、内存和延迟;
  2. 区分平均值、峰值与持续峰值;
  3. 检查 CPU throttling、OOM、重启和探针超时;
  4. 调整 SubPool spec;
  5. 确认没有超过 Operator 包络;
  6. 观察滚动更新后的新基线。

资源管理的目标不是让数字尽可能小,而是在成本、调度稳定性和应用性能之间建立可解释的边界。

使用 Hugo 构建
主题 StackJimmy 设计