4. 工作负载画像

在云原生集群里,资源利用率优化从来不是“把 request 调小”这么简单。真正决定优化空间和风险边界的,是工作负载画像——也就是对每一类控制器所管理的 Pod,在时间维度上刻画它的资源需求水平、波动幅度、峰值持续时间和可中断性。只有先画像,才能判断哪些负载适合压缩 request、哪些适合用 HPA 弹性伸缩、哪些适合放到低优先级队列或混部到同一节点。

Kubernetes 的控制器类型本身就隐含了不同的资源语义。Deployment 管理无状态副本,副本之间可互换,扩缩容和驱逐代价低;StatefulSet 管理有状态副本,每个 Pod 有稳定网络标识和独立存储,重启和迁移代价高;DaemonSet 保证每个(或指定)节点上运行一个 Pod,其资源需求与节点数量强绑定;Job 是一次性批处理任务,有明确的完成语义;CronJob 则按时间表周期性创建 Job。这五类负载的资源曲线形状差异极大,必须分开建模。

五类工作负载的资源需求模式与波动特征 横轴:时间 → 纵轴:资源使用量(示意) 曲线形状决定优化策略 Deployment 无状态 · 副本可互换 阶梯式波动 · 随流量伸缩 StatefulSet 有状态 · 稳定标识与存储 高位平稳 · 迁移代价高 DaemonSet 每节点一个 · 与节点数绑定 随节点扩容阶跃上升 Job 一次性 · 有完成语义 脉冲式 · 短时高占用 CronJob 周期调度 · 可预测 周期性脉冲 · 可错峰 画像四维度 → 优化策略映射 ① 基线水平(P50 使用量) ② 波动幅度(P95/P50 比) ③ 峰值持续时间 ④ 可中断性 / 优先级 request 贴近 P50,limit 留出 P95 余量;避免 request 长期虚高 波动大 → HPA/VPA 弹性;波动小 → 固定 request,减少调度抖动 短脉冲 → 提高 limit、放宽 CPU throttling;长峰值 → 预留容量或扩容 可中断 → 低优先级 + 混部;不可中断 → 高优先级 + 独占或反亲和

Deployment:阶梯式波动,优化空间最大

Deployment 的典型资源曲线是“阶梯形”:白天流量上来,副本被 HPA 拉起,单 Pod 使用量上升;夜间流量回落,副本缩容。它的关键特征是副本可互换、可随时重建,因此优化容忍度最高。画像时重点看两个量:单副本的 P50 使用量和 P95 使用量。如果 P95/P50 比值在 1.3 以内,说明负载平稳,可以把 request 设到接近 P50,limit 设为 P95 的 1.2 倍左右;如果比值超过 2,说明存在明显突发,此时 request 不宜压得太低,否则节点资源不足会触发驱逐,反而造成抖动。

一个可操作的判断方法是:用 kubectl top pod 或 Prometheus 的 container_cpu_usage_seconds_total 按 5 分钟粒度取 7 天数据,计算每个副本的 P50、P95、P99。若 P99 与 P95 差距很小,说明没有极端尖峰,可以放心压缩 request;若 P99 远高于 P95,则要保留更多余量,或改用 VPA 的 Off 模式只做推荐、人工确认后再改。

StatefulSet:高位平稳,但迁移代价高

StatefulSet 常见于数据库、消息队列、分布式存储。它的资源曲线通常“高位平稳”——因为每个副本承载固定分片,流量不会在副本间自由漂移,单副本使用量长期贴近其容量上限。画像时不能只看平均值,还要看“单副本峰值是否接近 limit”。如果某个分片是热点,它的使用量可能显著高于其他副本,此时按平均值设 request 会导致热点副本频繁被限流。

对 StatefulSet 的优化要更保守:request 应覆盖该副本的 P95,而不是全局 P50;limit 可以略高于 P99,但不宜过高,否则节点超卖后一旦多个 StatefulSet 同时冲高,会互相挤压。更重要的是,StatefulSet 的 Pod 重建涉及数据恢复和主从切换,不能像 Deployment 那样随意驱逐,因此它通常应绑定较高的 PriorityClass,并配合 PodDisruptionBudget 限制并发中断数量。

DaemonSet:与节点数强绑定,关注“每节点固定开销”

DaemonSet 的资源需求不随业务流量变化,而随节点数量阶跃变化。日志采集、监控 Agent、网络插件、安全 Agent 都属于这一类。画像的核心不是时间曲线,而是“每节点固定开销”:单个 DaemonSet Pod 的 request 乘以节点数,就是它在集群中占用的总资源。如果每个节点上跑了 5 个 DaemonSet,每个 request 0.2 核,那么每节点就有 1 核被固定占用,100 个节点就是 100 核。

优化 DaemonSet 的关键是核实它的真实使用量。很多 Agent 的 request 是“拍脑袋”设的,实际使用可能只有 request 的 20%。可以用 kubectl describe node 查看 Allocated resources,再对比 kubectl top pod -n kube-system 的实际值。若实际使用长期低于 request 的 30%,就可以下调 request,把释放出来的资源留给业务 Pod。但要注意:DaemonSet 通常承担可观测性和网络转发职责,limit 不宜压得太紧,否则 Agent 被限流会导致监控断点或网络抖动。

Job 与 CronJob:脉冲式占用,重点在错峰与可中断

Job 的资源曲线是脉冲形:启动后短时间内冲高,任务完成后归零。它的优化重点不是压缩 request,而是“让脉冲不干扰在线业务”。具体做法包括:给 Job 设置较低的 PriorityClass,使其在节点资源紧张时优先被驱逐;用 activeDeadlineSeconds 限制最长运行时间,避免僵尸任务长期占资源;用 backoffLimit 控制重试次数,防止失败任务反复拉起。

CronJob 是 Job 的周期版本,曲线呈现规律性脉冲。它的最大优势是“可预测”——既然知道它什么时候跑,就可以主动错峰。例如把大数据聚合任务从整点调到凌晨 2 点,避开业务高峰;或者用 startingDeadlineSeconds 允许调度延迟,让它在节点空闲时再启动。对于多个 CronJob,还可以用 concurrencyPolicy: Forbid 防止同一任务重叠执行,避免瞬时资源翻倍。

画像不是一次性工作。业务流量结构、副本数、节点规模都会变化,建议每两周重新拉取一次 P50/P95 数据,观察 request 与实际使用量的偏离度。偏离度持续超过 40% 的负载,就是下一轮优化的优先对象。

把画像落到可执行的参数上

完成画像后,最终要输出的是每个工作负载的 request/limit 建议值、优先级、是否启用 HPA/VPA、以及是否允许混部。下面给出一个可直接填写的画像记录表示例,字段按本节实际需要设计:

工作负载 类型 P50 CPU P95 CPU 波动比 可中断 建议 request 建议 limit 弹性策略
web-frontend Deployment 180m 260m 1.44 是 200m 400m HPA 3–12 副本
mysql-primary StatefulSet 1.6 核 2.1 核 1.31 否 1.8 核 2.5 核 固定副本 + PDB
log-agent DaemonSet 45m 70m 1.56 否 60m 200m 随节点数固定
report-job Job — 2.4 核 脉冲 是 500m 3 核 低优先级 + 超时
daily-etl CronJob — 1.8 核 周期 是 400m 2 核 错峰至 02:00

这张表的价值在于把“画像”从描述性分析变成了可执行的参数决策。每一行都对应一个明确的优化动作:Deployment 调 request 并接 HPA,StatefulSet 保余量并加 PDB,DaemonSet 按节点核算固定开销,Job 和 CronJob 用优先级与错峰降低对在线业务的干扰。实际执行时,建议先在非生产环境或单个副本上验证,观察 24 小时内的 throttling 次数、OOMKilled 次数和节点资源水位,确认无异常后再全量推广。

不要用同一套 request/limit 模板套所有工作负载。Deployment 可以压到 P50,StatefulSet 必须覆盖 P95,DaemonSet 要按节点总数核算,Job/CronJob 则优先保证不干扰在线业务。画像的意义就在于让这些差异被看见,而不是被一个默认值抹平。