57.4 FinOps 实践

一句话

FinOps 的产出不是“省了多少钱”,而是**“每个业务单位花了多少钱、为什么”**:先让成本可见(标签 + 分摊),再让成本可比(单位经济),最后才是优化(rightsizing / 承诺折扣 / 存储分层)。

三步闭环

步 做什么 产出
可见 资源打标签(业务/环境/负责人),账单按标签分摊 每个业务线的真实成本
可比 单位经济:每次请求/每单/每用户的成本 “这个功能值不值”的判断依据
优化 rightsizing、承诺折扣、存储分层、闲置回收 降本清单 + 预算

🔑 顺序不能反:没有可见性就优化,等于蒙眼砍预算——砍掉的可能是核心链路,留下的却是闲置资源。

代码:单位经济与三把刀

package main

import "fmt"

type cost struct {
	name  string
	amount float64 // 月成本
}

func main() {
	// 一个月度账单(示意)
	bill := []cost{
		{"计算(EC2/K8s)", 120_000},
		{"存储", 60_000},
		{"数据库", 40_000},
		{"网络/CDN", 30_000},
		{"可观测性", 20_000},
	}
	total := 0.0
	for _, c := range bill {
		total += c.amount
	}
	const orders = 500_000.0
	fmt.Printf("月成本 %.0f,月订单 %.0f → 单位成本 %.3f/单\n\n", total, orders, total/orders)

	// 三把刀:作用在同一项上的手段要“按顺序作用在剩余成本上”,不能相加
	computeBase := 120_000.0
	computeCuts := []struct {
		name  string
		ratio float64
	}{
		{"rightsizing(降配)", 0.30},
		{"承诺折扣(RI/SP)", 0.40},
		{"闲置回收", 0.05},
	}

	fmt.Printf("%-26s %-16s %-10s %-10s %s\n", "手段", "作用项", "可省比例", "月省", "剩余基数")
	computeSaving := 0.0
	base := computeBase
	for _, c := range computeCuts {
		s := base * c.ratio
		computeSaving += s
		base -= s
		fmt.Printf("%-26s %-16s %-10.0f%% %-10.0f %.0f\n", c.name, "计算", c.ratio*100, s, base)
	}
	storageSaving := 60_000.0 * 0.50
	obsSaving := 20_000.0 * 0.40
	fmt.Printf("%-26s %-16s %-10.0f%% %-10.0f %.0f\n", "存储分层", "存储", 50.0, storageSaving, 60_000.0-storageSaving)
	fmt.Printf("%-26s %-16s %-10.0f%% %-10.0f %.0f\n", "日志采样与保留期", "可观测性", 40.0, obsSaving, 20_000.0-obsSaving)

	saving := computeSaving + storageSaving + obsSaving
	naive := computeBase*(0.30+0.40+0.05) + storageSaving + obsSaving
	fmt.Printf("\n按顺序作用(正确):省 %.0f(%.1f%%),优化后单位成本 %.3f/单\n",
		saving, saving/total*100, (total-saving)/orders)
	fmt.Printf("把比例直接相加(错误):省 %.0f —— 高估了 %.0f\n", naive, naive-saving)

	fmt.Println("\n=== 注意:三把刀有重叠,不能简单相加 ===")
	fmt.Println("rightsizing 之后再用 RI,折扣基数变小;存储分层后预留容量也要重算")
}

本机实测(macOS,Apple M1,Go 1.27):

月成本 270000,月订单 500000 → 单位成本 0.540/单

手段                         作用项              可省比例       月省         剩余基数
rightsizing(降配)            计算               30        % 36000      84000
承诺折扣(RI/SP)                计算               40        % 33600      50400
闲置回收                       计算               5         % 2520       47880
存储分层                       存储               50        % 30000      30000
日志采样与保留期                   可观测性             40        % 8000       12000

按顺序作用(正确):省 110120(40.8%),优化后单位成本 0.320/单
把比例直接相加(错误):省 128000 —— 高估了 17880

=== 注意:三把刀有重叠,不能简单相加 ===
rightsizing 之后再用 RI,折扣基数变小;存储分层后预留容量也要重算

读数:

手段 作用项 可省比例 说明
rightsizing 计算 30% 把长期低利用率的实例降配
承诺折扣 计算 40% 只对“稳定负载”买预留
存储分层 存储 50% 冷数据下沉(46.6)
闲置回收 计算 5% 未挂载盘、空闲实例、僵尸负载
日志采样 可观测性 40% 保留期与采样率

⚠️ 这几项的比例不能直接相加:rightsizing 降了基数后,RI 的折扣金额随之变小;存储分层后预留容量也要重算。本节实测:按顺序作用省 110,120(40.8%),把比例直接相加算出来是 128,000(47.4%)——高估 17,880。预算按前者做,后者只配叫“上限估算”。

成本可见性的最小要求

要求 做法
每笔资源有主 标签:team、env、service、owner
账单能分摊 按标签聚合,共享资源(网络/DB)按用量分摊
预算有告警 月度预算的 80%/100%/120% 三档
异常能定位 日环比突增 > 30% 自动告警,附 Top 变化项

反模式

反模式 后果
没有标签就谈优化 不知道省的是谁的钱、会不会伤业务
只看总账单,不看单位成本 业务翻倍、成本翻倍被当成“正常”
为省小钱砍掉可观测性 故障定位变慢,损失远大于省下的钱
一刀切削峰 大促时容量不足,损失更大
买了 RI 却持续降配 承诺用不完,反而浪费
成本优化一次性运动 三个月后回到原点,没有机制

自检

Q1 为什么“单位成本”比“总成本”更适合做 FinOps 的北极星?

▸ 答案:因为总成本会随业务增长自然上升,无法判断“效率是否变好”。单位成本(每单/每千次请求/每用户)把规模因素除掉:业务翻倍、单位成本不变,说明效率持平;单位成本下降,说明优化真实有效。它也能直接支撑定价与毛利判断。

Q2 为什么“rightsizing 30% + RI 40%”不能简单相加成 70%?

▸ 答案:因为两者作用在同一个基数上,且有先后依赖。先 rightsizing 把计算成本从 120k 降到 84k,RI 的 40% 只能作用在 84k 上(省 33.6k),而不是原来的 48k。正确算法是“按顺序重算账单”,或对每一项用“优化后的成本”作基数。任何“百分比相加”的降本预测都会高估。

Q3 一个团队要砍 30% 云成本,你会先看哪三样?

▸ 答案:① 闲置与低利用率(未挂载盘、CPU < 10% 的实例、长期无人访问的存储)——这是纯浪费,砍了不影响业务;② 存储分层与保留期(冷数据下沉、日志采样)——影响面小、收益确定;③ 承诺折扣与规格匹配(稳定负载买 RI、按实际用量选规格)。最后才动“容量与冗余”,因为那会直接影响可用性。

Q4 为什么账单优化通常从 “闲置资源” 开始?

▸ 答案:因为闲置资源的优化收益确定、风险最低:未挂载的云盘、闲置的负载均衡、长期 0 请求的服务、开发环境整月开机——删掉不影响业务,收益立刻体现在账单上。相比之下 “换实例规格/改架构” 有风险、需要验证,应放在后面。

Q5 为什么用 “单位成本” 而不是 “总成本” 做决策?

▸ 答案:因为总成本会随业务增长自然上升,看不出效率;单位成本(每千次请求成本、每单成本、每 GB 处理成本)才能回答 “我们变贵了还是变高效了”。FinOps 的核心指标是单位成本随规模下降(规模效应),而不是 “总账单变小”。

关联