容量规划方法论
一句话:容量规划是用数据回答”系统能扛多少量、什么时候要扩容、扩多少”——不是等系统扛不住了才扩,而是提前推演、预留余量、弹性伸缩。
概述
JD 直接提到”容量规划”,这是 SRE 的核心职责之一。容量规划解决三个问题:
- 当前能扛多少? → 基准测试 + 容量模型
- 什么时候不够? → 趋势预测 + 容量预警
- 怎么扩? → 弹性伸缩 + 预留策略
容量规划全景
graph TD
BASE[基准测试] --> MODEL[容量模型]
TREND[趋势预测] --> FORECAST[容量预测]
MODEL --> FORECAST
FORECAST --> ALERT[容量预警]
ALERT --> SCALE[扩容决策]
SCALE --> ELASTIC[弹性伸缩]
SCALE --> MANUAL[人工扩容]
ELASTIC --> HPA[HPA/VPA/KEDA]
MANUAL --> CAP[容量审批]
第一步:基准测试(了解单节点能力)
压测方法论
压测分层:
├── 组件级压测:单服务 / 单中间件的极限能力
│ ├── CPU bound:QPS vs CPU 利用率
│ ├── Memory bound:QPS vs 内存增长曲线
│ ├── IO bound:QPS vs 磁盘 IO / 网络 IO
│ └── Connection bound:QPS vs 连接数
│
├── 链路级压测:端到端调用链的瓶颈定位
│ ├── 逐层加压,找出链路中最弱的一环
│ └── 关注级联效应(上游压垮下游)
│
└── 全链路压测:模拟真实流量
├── 影子库 / 影子队列(不污染生产数据)
├── 流量回放(录制线上请求重放)
└── 逐步加压 + 降压(观察恢复性)
压测工具选型
| 工具 | 类型 | 适用场景 | 特点 |
|---|
| wrk / wrk2 | HTTP 压测 | 单接口性能 | 轻量、支持延迟分布 |
| vegeta | HTTP 压测 | 单接口/批量 | Go 编写、支持多 URL |
| k6 | HTTP/gRPC 压测 | 复杂场景 | 脚本化、支持多协议 |
| Locust | 分布式压测 | 全链路 | Python 脚本、Web UI |
| JMeter | 全协议 | 复杂测试计划 | GUI、插件丰富 |
| tc | 网络限制 | 模拟弱网 | Linux 内核工具 |
| stress-ng | 系统级 | CPU/内存/IO 压测 | 不依赖应用 |
压测关键指标
# 压测报告必须包含
metrics:
throughput:
qps: "请求/秒(越高越好)"
tps: "事务/秒(端到端)"
latency:
p50: "中位数延迟"
p90: "90% 请求延迟"
p99: "99% 请求延迟(SLO 关注点)"
p999: "99.9% 请求延迟"
resource_usage:
cpu: "CPU 利用率(% 核心数)"
memory: "内存使用(RSS + 缓存)"
disk_io: "磁盘 IOPS + 吞吐"
network_io: "网络带宽利用率"
connections: "活跃连接数"
error_rate:
http_5xx: "5xx 错误率"
timeout: "超时率"
reset: "连接重置率"
saturation_point:
# 系统开始降效的点
breakpoint_qps: "QPS 达到 X 后延迟急剧上升"
max_stable_qps: "在此 QPS 下 P99 < SLO 阈值"
第二步:容量模型(推算系统总能力)
容量计算公式
单节点最大稳定 QPS = min(
CPU 瓶颈 QPS, # CPU > 80% 时的 QPS
内存瓶颈 QPS, # 内存 > 85% 时的 QPS
连接瓶颈 QPS, # 连接数 > 80% max 时的 QPS
SLO 瓶颈 QPS # P99 > SLO 阈值时的 QPS
)
集群总 QPS = 单节点 QPS × 节点数 × 效率系数
效率系数 = 实际 QPS / (单节点 QPS × 节点数)
# 受负载均衡、锁竞争、共享资源影响
# 通常 0.7-0.9
容量评估表(示例)
| 服务 | 单节点 QPS | 节点数 | 集群 QPS | 日均 QPS | 峰值 QPS | 余量 | 是否需扩 |
|---|
| API 网关 | 5000 | 4 | 14000 | 8000 | 12000 | 17% | 是(>80%) |
| 订单服务 | 2000 | 6 | 8400 | 5000 | 7000 | 20% | 否 |
| MySQL | 8000 | 1(M) | 6400 | 4000 | 5500 | 16% | 是 |
| Redis | 50000 | 3 | 105000 | 60000 | 80000 | 31% | 否 |
Python 容量计算工具
from dataclasses import dataclass
@dataclass
class ServiceCapacity:
name: str
single_node_qps: float # 单节点最大稳定 QPS
node_count: int # 当前节点数
efficiency: float # 效率系数 0-1
daily_avg_qps: float # 日均 QPS
daily_peak_qps: float # 日峰值 QPS
growth_rate: float # 周环比增长率
@property
def cluster_qps(self) -> float:
return self.single_node_qps * self.node_count * self.efficiency
@property
def utilization(self) -> float:
"""峰值利用率"""
return self.daily_peak_qps / self.cluster_qps
@property
def headroom(self) -> float:
"""余量"""
return 1 - self.utilization
@property
def days_until_full(self) -> int:
"""按当前增长率,多少天后容量耗尽"""
if self.growth_rate <= 0:
return -1 # 不增长
# cluster_qps = daily_peak_qps * (1 + growth_rate) ^ n
# 求解 n
import math
ratio = self.cluster_qps / self.daily_peak_qps
if ratio <= 1:
return 0 # 已超限
return int(math.log(ratio) / math.log(1 + self.growth_rate))
def recommendation(self) -> str:
if self.utilization > 0.8:
return f"URGENT: 利用率 {self.utilization:.0%},需立即扩容"
elif self.utilization > 0.7:
return f"WARN: 利用率 {self.utilization:.0%},建议扩容"
elif self.days_until_full < 30:
return f"PLAN: {self.days_until_full} 天后容量耗尽,启动扩容评估"
else:
return f"OK: 余量 {self.headroom:.0%},{self.days_until_full} 天后需扩容"
# 使用示例
svc = ServiceCapacity(
name="API网关",
single_node_qps=5000,
node_count=4,
efficiency=0.7,
daily_avg_qps=8000,
daily_peak_qps=12000,
growth_rate=0.02, # 周环比 2%
)
print(f"集群 QPS: {svc.cluster_qps}")
print(f"利用率: {svc.utilization:.1%}")
print(f"余量: {svc.headroom:.1%}")
print(f"推荐: {svc.recommendation()}")
第三步:趋势预测(预判扩容时机)
趋势预测方法
# Prometheus 查询:预测 7 天后的 QPS
predict_linear(
rate(http_requests_total[7d])[7d:1d],
7 * 24 * 3600 # 预测 7 天后
)
# 预测磁盘空间耗尽时间
predict_linear(
node_filesystem_avail_bytes{mountpoint="/"}[7d],
7 * 24 * 3600
)
容量预警规则
# 峰值利用率 > 70% 预警
- alert: CapacityWarning
expr: |
(
sum(rate(http_requests_total[5m])) by (service)
/
(service_capacity_max_qps * node_count * 0.7)
) > 1
for: 10m
labels:
severity: ticket
annotations:
summary: "{{ $labels.service }} 容量利用率 > 70%"
# 预测 7 天后超限
- alert: CapacityForecast
expr: |
predict_linear(
rate(http_requests_total[7d])[7d:1d],
7 * 24 * 3600
) > service_capacity_max_qps * 0.8
for: 1h
labels:
severity: ticket
annotations:
summary: "{{ $labels.service }} 预测 7 天后容量超限"
第四步:弹性伸缩
K8s 弹性伸缩工具
| 工具 | 范围 | 适用场景 | 特点 |
|---|
| HPA | Pod 级 | CPU/内存/自定义指标驱动 | K8s 原生,最常用 |
| VPA | Pod 级 | 资源 request 自动调整 | 需要重启 Pod |
| KEDA | Pod 级 | 事件驱动(Kafka/Redis/PG 队列深度) | 支持缩到 0 |
| Cluster Autoscaler | 节点级 | Pod Pending 触发节点扩容 | 云厂商集成 |
| Karpenter | 节点级 | 更快的节点供给 | 灵活的节点选择策略 |
HPA 配置示例
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-gateway-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-gateway
minReplicas: 4 # 最少 4 副本(保证 HA)
maxReplicas: 20 # 最多 20 副本(成本上限)
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 65 # CPU > 65% 扩容
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 75 # 内存 > 75% 扩容
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "2000" # 每副本 > 2000 QPS 扩容
behavior:
scaleUp:
stabilizationWindowSeconds: 30 # 30 秒内不重复扩容
policies:
- type: Percent
value: 100 # 每次最多扩到当前 2 倍
periodSeconds: 60
- type: Pods
value: 4 # 每次最多加 4 个 Pod
periodSeconds: 60
selectPolicy: Max
scaleDown:
stabilizationWindowSeconds: 300 # 5 分钟稳定期才缩容
policies:
- type: Percent
value: 25 # 每次最多缩 25%
periodSeconds: 120
容量预留策略
三级预留:
L1: 弹性伸缩自动覆盖(日常波动 ±30%)
L2: 预扩容覆盖(大促/活动前人工扩容 50-100%)
L3: 紧急扩容预案(突发流量 > 200% 时的应急方案)
预留余量标准:
核心服务:峰值 + 50% 余量
非核心服务:峰值 + 30% 余量
有状态服务(DB/Redis):峰值 + 30% + 硬件级冗余
常见问题 / 坑点
| 问题 | 原因 | 解决方案 |
|---|
| 压测结果和生产不一致 | 压测环境 ≠ 生产环境 | 用流量回放 + 影子库在准生产环境压测 |
| 只压单接口不看链路 | 单接口 OK 但链路有瓶颈 | 做全链路压测,关注最弱环节 |
| 弹性伸缩太慢 | HPA 周期长 + Pod 启动慢 | 预扩容 + 优化镜像/启动时间 + KEDA |
| 缩容导致服务抖动 | 缩容太激进 | 增加缩容稳定期(5-10 min)+ 慢速缩容 |
| 容量模型不准 | 效率系数拍脑袋 | 用生产数据校准效率系数 |
| 只看 QPS 不看延迟 | QPS 没到但 P99 已超 SLO | 以 SLO 为天花板(P99 < SLO 时的 QPS 才是真容量) |
关联知识
参考资源
学习时间
| 内容 | 日期 | 状态 |
|---|
| 容量规划方法论 | 2026-08-03 | 完成:压测、模型、预测、弹性伸缩 |
| 实际压测 | | 待实践 |
状态