你接手一个月账单 $200 万、约 3000 个微服务跑在 Kubernetes 上的平台。CFO 要求 12 个月内单位成本(cost per request、cost per active user)降 30%,但任何服务的 p99 SLO 不许破。这不是「关几台机器省钱」,而是一个约束优化问题:在 SLO 约束下求资源效率最优。机会很大——业界在线集群的平均 CPU 利用率普遍只有 10–20%,Uber 公开把全局利用率从均值 20% 提到 31%,说明 headroom 浪费是常态。
graph TD
subgraph OBS["遥测 Telemetry"]
M["运行指标
CPU · QPS · p99"]
BILL["云账单
Billing / CUR"]
end
M --> PLAN["容量规划器
预测 + 排队论 headroom"]
PLAN --> AS["Autoscaler
Reactive + Predictive"]
AS --> SCHED["调度 / Bin-packer
Karpenter · Borg"]
SCHED --> FLEET[("计算 Fleet
RI · Savings Plan · Spot")]
FLEET --> M
BILL --> ATTR["成本归因
Tag · 分摊"]
ATTR --> FIN["FinOps 看板
cost / request"]
FIN -.单位成本反馈.-> PLAN
classDef obs fill:#0e2030,stroke:#5eead4,color:#e8eef5
classDef ctrl fill:#1a2530,stroke:#64c8ff,color:#e8eef5
classDef fleet fill:#1a1a30,stroke:#ffb450,color:#e8eef5
classDef fin fill:#2a1530,stroke:#ff7ab6,color:#e8eef5
class M,BILL obs
class PLAN,AS,SCHED ctrl
class FLEET fleet
class ATTR,FIN fin
左半是「容量控制环」(指标→规划→扩缩→调度→fleet→回到指标),右半是「成本环」(账单→归因→看板),两环靠 cost/request 这个单位指标耦合。
原理:容量规划是在 SLO 约束下算「要多少资源」。三个输入:峰值负载(avg_qps × peak_factor)、单实例可承载量(压测得,不是看核数猜)、目标利用率。最反直觉的点:目标利用率不能设到 100%。排队论(M/M/c)告诉你,当利用率 ρ 趋近 1,排队等待 ∝ 1/(1-ρ) 呈非线性爆炸——利用率从 70% 升到 90%,等待时间翻数倍。所以存在一个 knee(拐点),多数在线服务把目标利用率定在 50–70%,留 headroom 吸收突发与单 AZ 故障(N+1/N+2 冗余)。
# 容量规划: 在目标利用率下算所需实例数 (pseudo)
peak_qps = avg_qps * peak_factor # 日内波峰 (如 4x)
per_inst = load_test_throughput # 单实例可承载 QPS, 压测得
target_util = 0.65 # 不是 1.0 —— knee 之前
need = ceil(peak_qps / (per_inst * target_util))
need += az_redundancy # N+1: 容忍单 AZ 挂掉
# 校验: 该利用率下 p99 是否仍在 SLO 内 —— 看压测曲线, 不要线性外推
GOGC 调优在 30 个核心服务上省下约 7 万核——侧证 headroom 浪费的量级。原理:自动扩缩容把「静态容量」变成「跟随负载」。三种范式:Reactive(看实时指标,如 K8s HPA target-tracking:指标偏离目标就调副本数)、Predictive(用历史预测未来负载、提前扩,如 Netflix Scryer)、Scheduled(已知规律,定时扩)。Reactive 的命门是 lag:指标采集 + 决策 + 实例启动 + 预热可能要几分钟,对秒杀级陡突发来不及。
# HPA target-tracking 核心公式
desired = ceil(current_replicas * (current_metric / target_metric))
# 例: 10 副本, 当前 CPU 90%, 目标 60% -> ceil(10 * 1.5) = 15 副本
# 防震荡: scale-in 看过去 5 min 最大值 (stabilization window) + cooldown
# 关键不对称: scale-out 要快, scale-in 要慢 —— 缩太快遇下一波突发就破 SLO
原理:3000 服务、500 团队,一张 $200 万总账单根本没法优化——必须把成本归因到团队/产品/请求。FinOps 的核心是 showback/chargeback:用资源 tag(team、service、env)把账单切片,再把共享成本(公用 LB、控制面、跨服务 DB、Spot 折扣)按某个 key 分摊下去。终点是 unit cost:cost per request、cost per active user——这才是能跨时间比较、能进决策的指标。
# unit economics: 把账单变成可决策指标
team_cost = direct_cost[team] + shared_cost * (team_usage / total_usage)
unit_cost = team_cost / team_requests # $ / 1k req —— 可跨季度比
# 看趋势 > 看绝对值: cost/req 环比上涨 = 有服务在变低效, 该告警
原理:利用率从 15% 提到 45% 就是省 3 倍钱。四个杠杆:① Rightsizing——requests/limits 贴合真实用量(VPA、Autopilot 自动调);② Bin-packing——调度器把 pod 紧密塞进节点、减少碎片;③ Consolidation——周期性重新打包、退掉空节点;④ 采购组合——基线用 Reserved/Savings Plan(省 ~40–60% 但锁 1–3 年)、弹性峰用 On-demand、容错批处理用 Spot(省 ~70–90% 但随时被抢占)。
| 采购类型 | 省多少 | 代价 / 适用 |
|---|---|---|
| Reserved / Savings Plan | ~40–60% | 锁定 1–3 年,架构变了会错配;放稳定基线 |
| On-demand | 0(基准价) | 最贵但不被抢;放弹性峰值兜底 |
| Spot | ~70–90% | 随时被回收(~2 min 通知);只放可中断/可重试负载 |
# 节点采购决策: 基线买承诺折扣, 弹性用 Spot 兜 On-demand
if workload.interruptible and spot_available:
launch(SPOT) # 省 70-90%, 收到抢占通知 -> 优雅迁移
elif load <= committed_baseline:
use(RESERVED) # 基线锁价, 省 40-60%
else:
launch(ON_DEMAND) # 峰值兜底, 最贵但不被抢
面试可能追问:
关键在排队论的 1/(1-ρ) 分母。利用率 70% 时排队因子 ∝ 1/0.3 ≈ 3.3;90% 时 ∝ 1/0.1 = 10——等待时间约翻 3 倍,而且越靠近 1 越陡。省下的机器是线性的(少买 ~22% 节点),但 p99 恶化是非线性的,很容易击穿 SLO。账要这么算:省下的实例成本 vs(SLO 违约赔付 + 超时触发的客户端重试放大 + 重试又进一步抬高 ρ 形成正反馈)。headroom 的价值正是「吸收突发、给排队留缓冲」,把它当浪费砍掉,是拿尾延迟的稳定性换账面数字。结论:利用率目标该由 SLO 下的延迟曲线反推,而不是拍一个「越高越好」。
预测式一旦欠配,扩容来不及就是 lag 期内 SLO 直接破——比纯 reactive 更糟,因为你「以为不用扩」而压低了基线。让它 fail-safe 的设计:① 不对称惩罚——over-predict 只多花点钱,under-predict 会违约,所以预测要带安全余量、宁可略微高估。② 分层兜底——predictive 只用来设容量下界(提前把基线抬上去),reactive 仍挂在上面负责抓住预测没料到的尖峰。③ 置信区间——预测输出带不确定度,波动大的时段自动加 headroom。④ 回退——预测信号异常(数据缺失、模型漂移)时降级为纯 reactive,绝不让一个坏预测把容量压到地板。本质是把「预测」当优化项、把「reactive + headroom」当安全网。
效率与韧性是对立的。把 headroom 榨干、Spot 拉满,等于系统性地移除了所有缓冲:① 故障吸收能力归零——单 AZ 挂掉,剩余节点本就满载,流量一转移立刻雪崩(没有 N+1 接得住)。② Spot 容量是共享池,AWS 回收往往按实例类型/AZ 批量发生,你和无数人抢同一池子,大面积回收时容量瞬间塌方。③ 自动扩缩的 lag 在高利用率下更致命——本就没冗余,扩容那几分钟无人兜底。所以「极致效率」适合可中断的批处理,绝不适合承载 SLO 的在线主路径。正确姿势是分层:核心在线服务留 headroom + On-demand/RI 保底,弹性与离线负载吃 Spot 与高利用率。省钱要省在能承受波动的地方。
它是个比值,分子分母都能被玩坏。① 分母稀释——往里灌大量廉价请求(健康检查、轮询、重试),cost/req 账面下降,实际总成本没动甚至上升。② 请求不同质——一次搜索和一次静态页成本差百倍,平均值掩盖了「贵请求」,该看 P50/P99 请求成本分布而非单一均值。③ 固定成本在低流量时主导——控制面、最小副本数是固定的,流量低谷时 cost/req 飙高,但那不代表低效。④ 跨服务成本转移——A 把计算推给 B,A 的 cost/req 好看了,全局没变。⑤ 该看边际成本——决策「值不值得多接这波流量」要的是边际成本,不是摊薄后的平均。一句话:cost/req 适合看趋势和告警,但做决策要拆到请求类型、区分固定/边际、警惕分母游戏。
典型的公地悲剧 + 二阶效应。单看这个团队:用 Spot、错峰,账单确实降了,showback 数字漂亮。但放到平台尺度:① Spot 是共享容量池,大家都被激励去抢,夜间窗口很快也变成「峰」,抢占率上升、可用容量下降,最后谁都没省到预期。② 夜间批处理扎堆会把原本的低谷填平,削峰红利消失,还可能挤占夜间的备份/维护窗口。③ 激励设计的锅——如果分摊 key 让 Spot 折扣只归使用者、却不反映「抢占给别人造成的外部性」,就会鼓励过度集中。解法是把全局视角放进归因与调度:错峰给差异化价格信号(越拥挤的时段越贵)、平台层做跨团队的 Spot 多样化与 workload 时间打散,让局部最优与全局最优对齐。这正是成本归因里「分摊 key 怎么选」会塑造行为的体现。