容量规划的本质是回答一个问题:未来一段时间内,集群需要多少计算、存储和网络资源才能稳定承载业务负载。传统做法常常依赖人工经验,例如按历史峰值乘以一个固定系数预留资源,或者等到告警触发后再紧急扩容。这种方式在业务形态稳定时勉强可用,一旦流量出现突发、周期性变化或新功能上线,就容易造成资源浪费或容量不足。更合理的做法是让容量决策建立在可观测数据之上,通过持续采集的指标、日志和链路信息,刻画集群的真实资源消耗规律。

一、从可观测数据中提取容量信号
集群可观测性通常包含三类数据:指标、日志和链路追踪。对容量规划来说,指标是最直接的输入,因为它以数值形式反映了资源消耗和业务负载。基础设施指标包括CPU使用率、内存占用、磁盘IO、网络吞吐等;应用指标包括每秒请求数、响应时间、错误率、队列长度等。这些指标需要按照固定频率采集,并通过聚合计算形成趋势数据。采集频率过高会引入噪声,过低则可能漏掉短时峰值。一般建议基础设施指标按15秒到1分钟采集,业务指标按1分钟到5分钟聚合,再根据容量评估窗口做进一步汇总。
从原始指标中直接判断容量是不够的,还需要提取一些派生信号。饱和度是最重要的一个,它表示资源使用量与该资源总量之间的比例。CPU饱和度接近100%时,调度延迟会快速上升;内存饱和度超过85%后,操作系统可能开始频繁回收缓存甚至触发OOM。另一个关键指标是峰值因子,也就是峰值负载与平均负载的比值。峰值因子越大,说明流量波动越剧烈,容量规划时就越不能只看平均值。下面的PromQL示例用于从Prometheus中查询节点级资源使用情况。
# 计算节点CPU使用率(5分钟平均值)
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# 内存饱和度:已用内存 / 总内存
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
# 磁盘读写吞吐,用于判断IO瓶颈
rate(node_disk_read_bytes_total[5m]) + rate(node_disk_written_bytes_total[5m])
这些查询结果可以接入容量评估系统,按命名空间、服务、节点池等维度分别统计。例如某个服务所在节点池的CPU饱和度长期处于40%以下,说明存在资源闲置;如果饱和度频繁超过75%,则需要考虑扩容。日志和链路数据在容量规划中主要用于解释指标波动,比如发现某次CPU飙升对应一次大事务或者缓存击穿,这类知识能帮助排除异常样本,避免把偶发抖动误判为长期增长趋势。
二、构建容量模型与预测方法
容量模型的目标是根据历史指标数据推导出未来一段时间内的资源需求。最简单的做法是基于历史峰值和安全水位线计算所需核心数或副本数。先取过去30天的CPU使用率序列,计算平均值和P95值,再结合当前总核数得到建议值。安全水位线通常设在60%到70%之间,既留有处理突发流量的余量,又不会造成太大浪费。下面这段Python代码演示了这种基于分位数的容量估算方法。
import statistics
# 假设已从Prometheus拉取过去30天的CPU使用率采样,单位为百分比
cpu_samples = [...] # 这里填充采样列表
avg_cpu = statistics.mean(cpu_samples)
p95_cpu = statistics.quantiles(cpu_samples, n=20)[18] # 第95百分位
peak_factor = p95_cpu / avg_cpu if avg_cpu else 1.0
# 当前总核数
current_cores = 120
# 目标安全水位线为65%,预留35%余量
target_cores = current_cores * (p95_cpu / 65.0)
print(f"平均CPU: {avg_cpu:.2f}%")
print(f"P95 CPU: {p95_cpu:.2f}%")
print(f"峰值因子: {peak_factor:.2f}")
print(f"建议核心数: {target_cores:.0f}")
基于分位数的方法实现简单,但它只反映了历史情况,没有考虑业务增长。更完善的模型可以引入趋势预测,例如对过去多个周期的CPU使用率做线性回归,或者使用时间序列分解方法把长期趋势、周期波动和随机噪声分开。假设某服务每周流量以约8%的速度增长,那么当前P95值为60%的集群,在四周后可能达到80%以上,此时就需要提前扩容。预测模型的价值在于把扩容决策从被动响应变为主动规划。
容量预测还应该输出一个资源预算区间,而不是单一数值。例如根据P50、P95和P99分别计算最低、推荐和最高配置,再结合业务可接受的过载概率选择档位。过载概率越高,需要的资源越多。对于无状态服务,可以通过水平扩容来平滑增加容量;对于有状态服务,则需要考虑分片、副本和数据迁移成本。这类差异也必须体现在容量模型中,否则预测结果无法直接用于执行。
三、自动化容量评估与弹性策略落地
可观测性数据只有进入自动化流程才能真正驱动容量规划。一个典型的容量评估流水线包含五个阶段:指标采集、聚合计算、容量预测、决策生成和执行扩缩。采集阶段由Prometheus、Grafana Agent或云监控完成,结果写入时序数据库。聚合阶段按服务、集群、区域等维度汇总,生成小时级和日级统计。预测阶段调用上一节提到的模型,输出未来24小时到未来30天的资源需求。决策阶段将预测值与当前容量比较,给出扩容或缩容建议。执行阶段通过Kubernetes HPA、VPA或者自定义控制器调整副本数、节点池大小。
自动化决策需要清晰的规则,否则会频繁抖动。下面是一个简化的容量决策函数,它根据CPU和内存指标返回扩缩容动作。生产环境中可以加入冷却时间、最小步长和预算约束,避免短时间内反复扩缩。
def capacity_decision(cpu_usage, memory_usage, policy):
decisions = []
if cpu_usage > policy["cpu_scale_out"]:
replicas_delta = int((cpu_usage - policy["cpu_target"]) / 10) + 1
decisions.append(("scale_out", replicas_delta))
elif cpu_usage < policy["cpu_scale_in"]:
decisions.append(("scale_in", 1))
if memory_usage > policy["memory_scale_out"]:
decisions.append(("scale_out", policy["memory_step"]))
return decisions
policy = {
"cpu_target": 65,
"cpu_scale_out": 75,
"cpu_scale_in": 30,
"memory_scale_out": 80,
"memory_step": 2,
}
print(capacity_decision(82, 60, policy))
这段逻辑看起来很直接,但实际落地时需要把决策结果与可观测性指标再次关联。例如执行扩容后,需要持续观察CPU使用率和请求延迟是否回到目标范围。如果扩容后饱和度下降不明显,说明可能存在单机热点或依赖瓶颈,单纯增加副本数并不能解决问题。闭环反馈是自动化容量规划区别于一次性脚本的关键。每一次扩缩容都应记录触发指标、执行动作和事后效果,这些数据又可以反过来优化预测模型和决策阈值。
弹性策略也要区分应用层和基础设施层。应用层扩容通常以副本数为单位,响应速度快,适合无状态服务;基础设施层扩容涉及节点创建和加入集群,延迟较高,但能解决资源池整体不足的问题。容量规划系统可以把短期波动交给应用层弹性处理,把长期趋势交给基础设施层预算管理。两个层级共享同一套可观测性数据源,但采用不同的时间窗口和触发条件。
四、常见误区与迭代优化
在基于可观测性做容量规划时,最常见的一个误区是把平均值当作容量依据。假设某服务平均CPU使用率只有35%,但每天晚高峰会冲到90%以上,如果按平均值预留资源,高峰期必然出现延迟飙升。因此容量模型必须使用高分位数或峰值窗口,而不是简单的算术平均。另一个误区是只看集群整体指标,忽略单机热点。整体CPU饱和度可能只有50%,但某个热节点已经达到95%,此时请求会被调度到该节点并产生排队。可观测性需要支持按实例、按Pod、按分片下钻,才能发现这类局部瓶颈。
还有一些团队把容量规划看成一次性项目,在年初做一次预算后就不再更新。业务流量、功能迭代、数据规模都在持续变化,容量规划也必须变成持续迭代的过程。每月或每季度重新训练预测模型,根据最近的数据调整峰值因子和安全水位线,能让预算更贴合实际。同时,容量规划还要与成本分析结合,定期回顾资源利用率,回收长期闲置的节点或降低过大的副本数。
最后要强调的是,可观测性本身也会产生数据和分析成本。采集过多指标会占用存储和网络,拖慢查询性能。容量规划所需的指标应当精挑细选,优先保留那些与资源饱和度、请求负载和稳定性直接相关的数据。对于已经证明无用的指标,应及时从采集列表中移除。只有让可观测性体系保持精简高效,容量规划才能长期稳定地运行下去,真正成为集群治理的基础能力。