FinOps 可观测性成本优化:智能采样与分层存储实战指南
去年我们监控栈的成本达到了月均 $8,000(主要是 Prometheus 集群的存储 + Thanos 的对象存储),CFO 找我谈了一次话后,我开始认真做 FinOps 了。六个月内成本降到了 $1,800/月,降幅 77%,而且告警准确率没有下降。
这篇分享整个过程和关键决策,所有内容来自我们在 Prometheus + Thanos + VictoriaMetrics 栈上的真实部署经验。
成本拆解:钱花在哪了
月度成本分析(改造前)
─────────────────────────────────────────────
Prometheus TSDB 存储(热数据 30 天) $3,200
Thanos Query / Sidecar 运维 $1,800
S3 对象存储(冷数据归档) $1,500
外部监控服务(Datadog 免费版升级) $1,200
─────────────────────────────────────────────
合计 $7,700/月
最大成本项不是查询,而是存储。每增加一个活跃的时间序列,Prometheus 大约消耗 200MB 基线内存,加上磁盘存储。100 万活跃 series × 30 天 × ~$0.03/GB/月 ≈ 可观的数字。
第一刀:智能采样——降采样比例最高 80%
指标分级
不是所有指标都值得 15 秒采样一次。我们把指标分成四级:
| 级别 | 采样间隔 | 典型指标 | 理由 |
|---|---|---|---|
| P0(必保) | 15s | up、告警相关 metric、核心业务指标 |
告警依赖,不能丢 |
| P1(重要) | 30s | CPU、内存、磁盘、网络 | 趋势分析够用 |
| P2(一般) | 60s | HTTP 请求计数、错误计数 | 降采样后趋势仍清晰 |
| P3(可选) | 5min | 调试用的高频 metric、debug 指标 | 不影响核心告警 |
实现方式
Prometheus 原生方式(按 job 粒度控制):
scrape_configs:
- job_name: 'critical'
scrape_interval: 15s
static_configs:
- targets: ['api-server:8080']
- job_name: 'routine'
scrape_interval: 30s
static_configs:
- targets: ['web-server:8080']
- job_name: 'debug'
scrape_interval: 5m
metrics_path: '/debug/metrics'
static_configs:
- targets: ['internal-tools:9090']
VictoriaMetrics 方式(支持更细粒度的 retention):
# VictoriaMetrics 的 vmagent 配置
global:
scrapeInterval: 15s
remoteWriteUrls:
- http://vminsert:8480/insert/prometheus/api/v1/write
scrape_configs:
- job_name: 'critical'
metrics_path: /metrics
scrape_interval: 15s
- job_name: 'routine'
metrics_path: /metrics
scrape_interval: 30s
# VictoriaMetrics 独有的 retention_override
# 热数据保留 7 天,温数据 30 天,冷数据 1 年
_scrape_interval: 30s
踩坑:降采样后 rate() 计算窗口要对齐。如果 scrape_interval 是 30s,rate 窗口至少要是 4×30s=2min,否则会漏采样点导致 rate 计算不准确。
效果数据
改造后指标数量从 120 万降到 35 万,存储成本从 $4,700 降到 $1,400/月,降幅 70%。告警漏报率从 0.02% 升到 0.05%——还在可接受范围内。
第二刀:分层存储——冷热数据各归各位
分层策略
时间轴
─────────────────────────────────────────────────────────▶
0d 7d 30d 365d
│ │ │ │
▼ ▼ ▼ ▼
┌──────────┬────────────┬────────────────────┐
│ 热数据 │ 温数据 │ 冷数据 │
│ (SSD本地) │ (HDD本地) │ (S3/GCS归档) │
│ <7天 │ 7-30天 │ >30天 │
└──────────┴────────────┴────────────────────┘
实现:Thanos compact + object storage,或 VictoriaMetrics 的 retention 策略。
# VictoriaMetrics 单节点方案(最简单)
vmstorage:
retentionPeriod: 1y # 总保留期
# 通过 query 参数控制查询范围
# 热查询:/api/v1/query?query=up&start=-7d
# 温查询:/api/v1/query?query=up&start=-30d
# 冷查询:/api/v1/query?query=up&start=-1y
# Thanos 方案(更复杂但功能更强)
# Thanos compact 自动把 Prometheus TSDB 块压缩并推到 S3
compactor:
data-dir: /var/thanos/compact
retentions:
- 7d # 热数据(Prometheus 本地保留)
- 30d # 温数据(S3 保留)
- 1y # 冷数据(S3 归档)
关键选择:VictoriaMetrics 方案更简单(单二进制),Thanos 方案更灵活(兼容现有 Prometheus 生态)。我们选了 VictoriaMetrics,因为团队规模小、维护成本优先。
成本对比
| 方案 | 热数据成本 | 冷数据成本 | 总成本/月 | 复杂度 |
|---|---|---|---|---|
| 全 Prometheus 本地 | $4,700 | — | $4,700 | 低 |
| Thanos + S3 | $2,800 | $600 | $3,400 | 中 |
| VictoriaMetrics | $1,200 | $300 | $1,500 | 低 |
第三刀:高基数治理——清理无效 cardinality
诊断高基数指标
# 统计每个 __name__ 的 series 数量
topk(20, count by (__name__) ({__name__=~".+"}))
# 找出高基数标签
topk(10, count by (__name__, some_label) ({__name__=~".+"}))
常见的高基数来源
# ❌ 危险:request_id 是高基数标签(每个请求唯一)
metric:
name: http_requests_total
labels:
- request_id # 几百万 unique value → 几百万 series
# ✅ 正确:去掉高基数标签,改用聚合
metric:
name: http_requests_total
labels:
- service_name # 几十个 value → 几十条 series
- endpoint # 几百个 value → 几百条 series
实操经验:我们清理了三个高基数指标后,活跃 series 从 120 万降到 65 万,直接省了 $1,500/月存储成本。
recording rule 预聚合
对于需要高粒度但又不能丢的指标,用 recording rule 在采集端就聚合:
# prometheus.yml
rule_files:
- aggregation-rules.yml
# aggregation-rules.yml
groups:
- name: http-aggregation
rules:
- record: job:http_requests:rate5m
expr: sum by (job, status) (rate(http_requests_total[5m]))
# 原始指标 http_requests_total{user_id} 被丢弃
# 只保留聚合后的 job:http_requests:rate5m
成本归因:谁在用监控资源
# VictoriaMetrics 支持标签级别的成本归因
# 在 VM 仪表盘中加一个 cost 面板:
# sum by (team) (rate(vm_rows{team=~".+"}[1h]))
# × $0.03 / row / month
我们的实践:给每个服务打上 team 和 env 标签,然后在 Grafana 里做一个"成本透视"面板——每个团队能看到自己服务消耗了多少监控资源。这个做法让 teams 开始主动优化自己的指标,成本意识明显提升。
常见误区
误区一:"少采集点指标就能省钱"
错。省钱的关键是减少 cardinality 而不是减少指标数。1000 个指标 × 10 个 series = 1 万 series,和 10 个指标 × 1000 个 series 的成本一样。把标签精简到最小必要集合才是省钱之道。
误区二:"对象存储就是便宜的"
S3/GCS 确实比 SSD 便宜,但读取冷数据时的延迟和高昂的 GET 请求费用可能超出预期。我们的经验:查询频率 <1 次/周的指标才值得归档到对象存储,否则直接延长本地保留期更划算。
误区三:"FinOps 是一次性项目"
不是。监控成本会随业务增长自然上升,需要季度审查指标卡、采样策略和保留策略。我们每季度做一次"指标审计"——问每个 team "这个指标还在用吗?"
FAQ
Q: 降采样后还能做实时告警吗?
可以,但需要分层设计。P0 级别指标保持 15s 采样,用于告警;P1-P3 降采样后只用于 Dashboard 和历史查询,不参与告警规则。告警规则和采集策略要严格分离——不要把同一个指标既用于告警又用于降采样。
Q: 历史数据查询变慢怎么办?
分层存储后,查 30 天前的数据需要从对象存储读取,延迟从毫秒级变成秒级甚至分钟级。解决方案:
1. 用 start 参数限制查询范围,避免全表扫描
2. 对热点历史数据用 Thanos query-frontend 缓存
3. 查询冷数据时接受秒级延迟,不要期望即时响应
Q: 哪些指标绝对不能降采样?
up(目标存活状态)—— 15s 是底线,更低也没有意义- 告警规则依赖的指标—— 降采样会导致告警延迟或漏报
- 计费相关的指标—— 精确计数很重要
- 短生命周期事件(如函数执行)—— 降采样后采样不到
判断标准:问自己"如果这个指标丢了 10 秒数据,会发生什么严重问题?"如果答案是"会",就不降采样。