4. 工作负载画像
在云原生集群里,资源利用率优化从来不是“把 request 调小”这么简单。真正决定优化空间和风险边界的,是工作负载画像——也就是对每一类控制器所管理的 Pod,在时间维度上刻画它的资源需求水平、波动幅度、峰值持续时间和可中断性。只有先画像,才能判断哪些负载适合压缩 request、哪些适合用 HPA 弹性伸缩、哪些适合放到低优先级队列或混部到同一节点。
Kubernetes 的控制器类型本身就隐含了不同的资源语义。Deployment 管理无状态副本,副本之间可互换,扩缩容和驱逐代价低;StatefulSet 管理有状态副本,每个 Pod 有稳定网络标识和独立存储,重启和迁移代价高;DaemonSet 保证每个(或指定)节点上运行一个 Pod,其资源需求与节点数量强绑定;Job 是一次性批处理任务,有明确的完成语义;CronJob 则按时间表周期性创建 Job。这五类负载的资源曲线形状差异极大,必须分开建模。
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 防止同一任务重叠执行,避免瞬时资源翻倍。
把画像落到可执行的参数上
完成画像后,最终要输出的是每个工作负载的 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 次数和节点资源水位,确认无异常后再全量推广。