跳转至

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 秒数据,会发生什么严重问题?"如果答案是"会",就不降采样。