路线图

13 容量规划(Capacity Planning)

星辉 2026-07-02 阅读 4 min 828 字 路线图
13 容量规划(Capacity Planning) 封面

概述#

容量规划的终极目标:系统在流量增长时既不崩溃,也不过度采购资源。过少会导致故障,过多会浪费成本。SRE 的任务是用数据做容量预测、建压测基准、设资源水位线,实现"刚好的容量"。

容量规划不是一次性项目,是持续过程。流量在变、架构在变、用户行为在变——容量规划必须跟着变。一个不做容量规划的团队,要么经常被流量打挂(过载),要么账单虚高(过度采购)。

一、需求预测#

从历史数据推导未来需求#

容量规划不是摊大饼——拍脑袋说"明年流量翻倍,机器也翻倍"。正确的做法是基于历史趋势做有依据的预测

text
预测 QPS 增长 = 当前 QPS × 增长率 → 目标 QPS
目标资源 = 当前单台 QPS 承载 × 安全系数 × (1 + 增长率)

预测的三个数据源:

  1. 历史趋势:过去 6-12 个月的流量曲线,计算复合增长率
  2. 业务预测:业务方对下季度的用户增长、活动计划
  3. 外部信号:行业趋势、竞品动态、监管变化

三者结合才是靠谱的预测。只看历史会错过业务拐点,只听业务会忽略现实约束。

增长类型识别#

增长类型特征规划策略
线性增长每月稳定 +5%按月提前扩容
季节性促销/节假日峰值提前预留弹性容量
事件驱动营销活动/新功能上线活动前压测 + 临时扩容
爆发式热点事件/病毒传播依赖自动扩缩(HPA/KEDA)
阶跃式大客户签约/产品发布提前采购,不能用自动扩缩

不同增长类型需要不同的容量策略。线性增长可以精细规划,爆发式增长必须依赖自动扩缩,阶跃式需要提前采购——自动扩缩来不及。

预测周期#

  • 短期(1-4 周):基于 K8s HPA 自动扩缩容,人工盯水位
  • 中期(1-3 个月):月度容量回顾,提前采购/预留资源
  • 长期(6-12 个月):年度容量规划,与业务增长目标对齐

三个周期互相补充——短期靠自动化,中期靠流程,长期靠规划。

容量预测的常见误区#

  • 线性外推:用过去 3 个月的增长率预测未来 12 个月——很多业务有拐点
  • 忽略季节性:用淡季流量预测旺季需求——必然容量不足
  • 只看 QPS 不看数据量:用户量没变但数据量翻倍,DB 容量不够
  • 忽略依赖容量:自己服务容量够了,但下游 DB/缓存先到瓶颈

二、压测基准#

压测的目的不是"看系统能扛多少"#

而是建立容量基准线

text
单实例容量 = 在满足 SLO 的前提下,一个实例能承载的最大 QPS
所需实例数 = 目标 QPS / 单实例容量 × 安全系数

关键词是"在满足 SLO 的前提下"——单纯测"系统能扛多少 QPS"是错的。系统能扛 10000 QPS 但 P99 延迟 10s,对用户来说就是挂了。压测必须以 SLO 为约束。

压测要回答的问题#

  1. 单实例在保证 P99 < SLO 延迟的情况下,最大 QPS 是多少?
  2. 在这个 QPS 下,哪些资源先成为瓶颈(CPU/内存/DB 连接/线程池)?
  3. 流量从 50% → 100% → 150% 基线时,延迟曲线是什么样的?
  4. 系统在超载时如何表现——优雅降级还是直接崩溃?
  5. 长时间运行后是否有性能退化(内存泄漏、连接泄漏)?

压测类型#

类型目的工具时长
基准压测测单接口单实例容量上限wrk2 / vegeta / k610-30 分钟
混合压测模拟真实流量比例组合压k6 / Locust30-60 分钟
浸泡压测测内存泄漏、连接泄漏同上,长时运行24+ 小时
峰值压测测突发流量响应(2x/3x 基线)k6 + KEDA短时高压
破坏性压测找系统崩溃点k6 持续加压直到崩溃

压测关键指标#

指标看什么
P99 延迟在哪个 QPS 点开始劣化
错误率在哪个 QPS 点开始上升
CPU throttle实例是否被 CPU limits 限制
内存是否有 OOM 风险
连接数数据库连接池是否成为瓶颈
GC 时间是否有频繁 Full GC
网络吞吐是否打满带宽

压测的常见陷阱#

  • 测试环境不等于生产环境:测试环境规格低,压测结果不能直接推到生产
  • 数据量不真实:测试环境只有 1000 条数据,生产有 1 亿条,性能完全不同
  • 依赖没模拟:压测时下游用 mock,生产时下游真实响应慢
  • 缓存命中率高:压测重复请求,缓存命中率高,生产请求多样,命中率低
  • 只测正常路径:没测异常路径(如下游挂了时的表现)

正确的压测应该在类生产环境做:

  • 相同规格的硬件
  • 相同量级的数据
  • 真实的下游依赖(不是 mock)
  • 多样的请求模式(不重复)

压测结果的应用#

压测完得到"单实例最大承载 QPS"后:

text
所需实例数 = 目标 QPS / 单实例容量 × 安全系数(通常 1.5-2)

例:目标 10000 QPS,单实例承载 1000 QPS,安全系数 1.5
所需实例 = 10000 / 1000 × 1.5 = 15 个

安全系数是为了吸收突发流量——不留余量意味着任何流量波动都会导致过载。

三、资源水位线#

为什么要设水位线#

text
红色线(危险): CPU 使用率 > 80%(或 throttle > 5%)
黄色线(预警): CPU 使用率 > 65%
绿色线(安全): CPU 使用率 < 50%

水源类比:水库不能等水满了再泄洪——容量管理也一样,不能等 CPU 100% 了再加机器。

各类资源的水位线建议#

资源安全线预警线危险线说明
CPU 使用率< 50%50-70%> 80%留余量吸收突发
CPU throttle< 1%1-5%> 5%throttle 即被限制
内存< 60%60-80%> 85%OOM 前必须扩容
磁盘< 70%70-85%> 90%满了日志写不进去
DB 连接池< 60%60-80%> 85%连接耗尽 = 服务不可用
Pod 数量留有 30% 余量maxSurge 不足滚动更新需要额外容量
网络< 50%50-70%> 80%留余量吸收突发
线程池< 60%60-80%> 85%队列堆积 = 饱和

水位线告警#

水位线的告警级别应该低于 SLO 告警(见 Ch08)——它是预警,不是事故:

text
CPU > 65%(黄色)→ Ticket,工作时间处理
CPU > 80%(红色)→ Page,需要立即扩容

水位线告警和 SLO 告警的区别:

  • 水位线告警:预测性——还没影响用户,但即将影响
  • SLO 告警:结果性——已经影响用户

好的容量管理是在水位线告警阶段就处理,不要等到 SLO 告警。

水位线的动态调整#

水位线不是固定的,要根据实际情况调整:

  • 流量波动大的服务:安全线降低(更保守)
  • 流量稳定的服务:安全线可以提高(更激进)
  • 自动扩缩配置好的服务:可以接近上限运行
  • 没有自动扩缩的服务:必须留更多余量

四、自动扩缩容#

HPA(Horizontal Pod Autoscaler)#

K8s 原生的水平扩缩容,基于 CPU/内存/自定义指标:

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0  # 立即扩容
      policies:
      - type: Percent
        value: 100  # 每次最多扩容 100%
        periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300  # 5 分钟稳定后缩容
      policies:
      - type: Percent
        value: 10  # 每次最多缩容 10%
        periodSeconds: 60

HPA 的局限性#

  1. 扩容有延迟:Pod 启动 + 健康检查 + JIT 预热需要时间,流量尖刺可能已经过了
  2. 缩容可能过早:刚扩容完就缩容可能导致频繁抖动
  3. 不适合常驻内存型服务:内存型 HPA 在常驻内存型服务中会误判——加副本不降单 Pod 内存(见 A 旗舰生产教训)
  4. 指标滞后:HPA 基于 CPU 平均使用率,但 CPU 飙高到扩容完成有 1-3 分钟延迟
  5. 不能预测:HPA 是响应式的,不能预测流量高峰

实战教训(摘自 A 旗舰故障复盘)#

一个文件代理服务同时挂 CPU(70%)和内存(80%)HPA。该服务常驻内存卡在 84-92%,触发内存 HPA 持续扩容,从 min 4 一直扩到 13 个副本——但加副本根本无法降低单 Pod 的基线内存(与业务量脱钩)。13 个副本 × ~2Gi ≈ 24Gi 内存伺候 0.09 核的活——完全资源浪费,还撞满专用节点池导致发版超时。

教训:常驻内存型服务禁用 memory-Utilization HPA,只用 CPU 或自定义 QPS。

VPA(Vertical Pod Autoscaler)#

垂直扩缩容——调整 Pod 的 CPU/内存 requests:

yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
spec:
  updatePolicy:
    updateMode: Auto  # 自动调整

VPA 的限制:

  • 重启 Pod 才能应用新的 requests
  • 和 HPA 不能同时用于同一维度(CPU 或内存)
  • 适合"不知道该给多少资源"的服务,不适合精细控制

KEDA(Event-Driven Autoscaling)#

基于事件的扩缩容,支持多种指标源:

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
spec:
  scaleTargetRef:
    name: my-deployment
  minReplicaCount: 2
  maxReplicaCount: 100
  triggers:
  - type: prometheus
    metadata:
      query: rate(http_requests_total[2m])
      threshold: "1000"

KEDA 的优势:

  • 支持自定义指标(不只是 CPU/内存)
  • 支持 scale-to-zero(无流量时缩到 0)
  • 适合事件驱动型工作负载(消息队列、Cron)

Cluster Autoscaler / Karpenter#

HPA 只扩 Pod,但 Pod 需要节点来运行:

text
HPA 触发 → Replicas 增加 → Pending Pod → Cluster Autoscaler/Karpenter 扩节点

关键检查

  • 节点池有没有足够资源类型(GPU/特定 instance type)
  • 冷启动延迟(新节点启动 + 镜像拉取 + 加入集群)
  • 节点数上限是否合理
  • Spot 实例中断的处理

Karpenter vs Cluster Autoscaler

Cluster AutoscalerKarpenter
节点选择按预配置的 node group动态选择最优实例
启动速度慢(分钟级)快(秒级)
配置复杂度高(要预定义 ASG)低(只需 provisioner)
适用规模中小规模中大规模

扩容链路全流程测试#

扩容不是"配好 HPA 就完事"——完整链路必须测过:

text
HPA 触发 → Replicas 增加 → Pending Pod → CA/Karpenter 扩节点
  → 节点启动 → Pod 调度 → 镜像拉取 → 服务就绪

任一跳卡住都是容量故障。常见卡点:

  • 节点池满了(达到 max)
  • 镜像拉不下来(跨境源、镜像仓库限流)
  • Pod 调度失败(resource 不够、taint 不匹配)
  • 服务启动慢(JIT 预热、连接池初始化)

五、容量规划与成本#

容量规划的另一个维度是成本优化

策略做法节省幅度风险
预留实例/承诺用量长期稳定负载用预留30-60%锁定 1-3 年
Spot/抢占式实例可中断负载用 Spot60-90%可能被中断
定时扩缩已知高峰时段提前扩容按比例配置错误导致容量不足
合理设 maxReplicas限制最大副本数防成本爆炸过低导致服务挂
资源 right-sizing调整 requests/limits20-40%过低导致 OOM/throttle
多租户共享多服务共享节点30-50%隔离性差

成本可观测性#

容量优化需要成本可观测:

  • 每个服务/团队的资源消耗
  • 每个服务的单位成本(如每千次请求的成本)
  • 资源利用率(低利用 = 浪费)
  • Spot/On-Demand 比例

没有成本可观测性,容量优化就是"凭感觉"——可能省了小钱赔了大钱。

六、容量规划流程#

月度容量回顾会议#

每月 1 小时,内容:

  • 上月流量 vs 预测对比(预测准确度)
  • 当前各服务资源利用率
  • 即将到容量的服务清单
  • 下月预期流量变化
  • 扩容/采购计划

容量规划文档#

每个核心服务应该有容量规划文档:

markdown
# 服务容量规划:order-api

## 当前容量
- 实例数:10(min 5, max 20)
- 单实例最大 QPS:1000(P99 < 500ms)
- 总容量:10000 QPS
- 当前峰值 QPS:6000(60% 利用率)

## 预测
- 下月预期峰值:7500 QPS(+25%)
- 三个月预期:10000 QPS(+67%)

## 扩容计划
- 下月:HPA max 提到 25
- 三个月:评估是否需要架构优化(不能只靠加机器)

## 压测结果
- 最近压测:2026-06-15
- 单实例在 P99 < 500ms 下最大 QPS = 1200
- 瓶颈:DB 连接池(单实例 50 连接)

## 风险
- DB 连接池是瓶颈,加实例会加重 DB 压力
- 建议优化连接池配置或引入读副本

实战要点#

  1. 压测必须有 SLO 基准。不留"单实例在保证 SLO 的前提下最大 QPS"这个数据,不算做完压测。
  2. 水位线 ≠ SLO 告警。水位线是容量预警,SLO 是用户体验告警。两者独立但互补。
  3. 内存 HPA 陷阱:常驻内存型服务别用内存 HPA——加副本不降单 Pod 内存,只会浪费资源。
  4. 扩容链路全流程测试:HPA → Pending Pod → CA/Karpenter 扩节点 → Pod 调度 → 服务就绪。任一跳卡住都是容量故障。
  5. 容量规划文档必须维护。不维护的文档等于没有,关键时刻找不到数据。
  6. 预测要保守。预测 10000 QPS,按 12000 准备——多 20% 余量比容量不足好。
  7. 成本优化不能影响可靠性。Spot 实例省钱但有中断风险,核心服务不用 Spot。
  8. 定期压测。架构变了、流量模式变了、数据量变了——都要重新压测。每年至少一次完整压测。
  9. 关注依赖容量。自己的服务容量够了,下游 DB/缓存可能先到瓶颈。
  10. 容量事件要复盘。容量不足导致的事故要做 postmortem,改进容量规划流程。

小结#

容量规划的核心三件事:压测建基准(知道单机天花板在哪)、设水位线(知道什么时候该扩容)、配自动扩缩(让系统自己响应大部分波动)。但自动扩缩不替代容量规划——它只解决短时间尺度的问题,中长期趋势仍然需要人工规划和采购。

判断一个团队的容量管理是否成熟,看三件事:核心服务有压测基准数据、有月度容量回顾会议、有水位线告警。三件事都做到是成熟团队;只做到一两件是"在建立中";一件都没做,团队就是在"靠运气"——下次流量爆发就是事故。