资源配置不是装饰,它决定能否调度和如何失效
本项目要求 CPA 与 cpa-manager 都明确填写 CPU、内存的 requests 和 limits:
| |
这四个值既影响 Scheduler,也影响容器运行时和 Namespace 配额。它们不是“给监控看的备注”。
requests:我至少要预留多少
Scheduler 主要根据 requests 判断 Node 是否放得下 Pod。
CPU 200m 表示 0.2 个 CPU core 的请求。1000m 等于 1 core。
内存 256Mi 使用二进制单位:
| |
一个 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 重启。
因此:
| |
查看上一次终止原因:
| |
requests 与 limits 的关系
本项目校验:
- 值必须大于 0;
- 只能声明 CPU 和内存;
- requests 和 limits 都必须完整;
- limit 不能小于 request。
例如:
| |
调度时按 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 最大值:
| |
Operator 在创建 Namespace、PVC 和 Secret 等持久资源之前,先计算 CPA 与 cpa-manager 的合计申请。超出包络时报告 ResourcesExceedBaseline,不创建半套资源。
这个顺序很重要。若先创建 PVC 和 Secret,最后才发现 CPU 超限,集群里会留下难以解释的半成品。
ResourceQuota:给 Namespace 装上总闸
每个号池 Namespace 都会得到一个 ResourceQuota,hard limits 来自平台最大包络,而不是当前 CR 的实际申请:
| |
为什么不直接把 Quota 设成当前申请量?因为用户可能在允许范围内调整 CPU 和内存。如果 Quota 只等于旧值,更新工作负载时可能先被 Quota 阻止,控制器还要同步处理两个互相约束的对象。
使用平台最大包络后:
| |
这是应用层校验和 Kubernetes 原生硬限制的双层防线。
为什么没有 LimitRange
LimitRange 常用于给未声明资源的容器补默认值,或限制单个容器的范围。本项目要求 SubPool 明确声明每个容器的 requests/limits,Operator 又负责校验并投影,所以不需要 LimitRange 自动补值。
显式输入有两个好处:
- 用户提交的 CR 就能看出真实资源意图;
- 不会因不同 Namespace 的默认值而产生隐式差异。
控制器还会删除属于本 Pool 的旧版 LimitRange,避免遗留策略与新契约冲突。
PodDisruptionBudget 在保护什么
项目为 runtime 和 Operator、Mihomo 各配置了 PDB,常见形式是:
| |
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 等其他约束阻止调度。
检查:
| |
调参的正确闭环
不要仅凭“CPU 经常到 100%”就盲目加 limit,也不要为了调度成功把 request 压到极低。更可靠的闭环是:
- 观察一段有代表性的 CPU、内存和延迟;
- 区分平均值、峰值与持续峰值;
- 检查 CPU throttling、OOM、重启和探针超时;
- 调整 SubPool spec;
- 确认没有超过 Operator 包络;
- 观察滚动更新后的新基线。
资源管理的目标不是让数字尽可能小,而是在成本、调度稳定性和应用性能之间建立可解释的边界。