存储与成本:采样、下采样、冷热分层、对象存储
可观测性账单的第一反应往往是”日志太多”。半真半假——在多数中等规模团队里,Logs 占 40%–60%、Traces 20%–35%、Metrics 10%–20%、Profiles < 5% 是常见结构(工程经验区间,非厂商报价)。真正的问题是:这些字节里,有多少是从未被查询过的冷数据?
本文给出显式假设下的成本估算 worksheet(不编造云厂商单价),说明采样、保留期、下采样、冷热分层的杠杆顺序,并对 Prometheus/Mimir、Loki、Tempo 给出可操作的 retention 配置策略。采样理论见 埋点哲学;Trace 采样实战见 Traces 栈;数据模型见 数据模型 与 TSDB 内核。
一、可观测性成本拆解
1.1 五大成本维度
| 维度 | 包含 | 典型占比(假设模型) |
|---|---|---|
| 计算 | Collector、Querier、Compactor CPU/RAM | 25%–35% |
| 存储 | 热 SSD + 温 HDD + 冷对象存储 | 40%–55% |
| 网络 | 跨 AZ 复制、公网 egress、S3 API | 10%–20% |
| 索引 | ES/Loki index、Tempo 元数据 | 5%–15% |
| 人力 | 管道维护、容量规划 SRE | 10%–25%(常被忽略) |
1.2 四大支柱成本结构
| 支柱 | 成本占比区间 | 主要驱动 | 最大杠杆 |
|---|---|---|---|
| Logs | 40%–60% | 体积 + 索引策略 | 采样 + 保留期 |
| Traces | 20%–35% | Span 数量 × 大小 | 头部/尾部采样 |
| Metrics | 10%–20% | Series 数 × retention | 基数 + recording rules |
| Profiles | < 5% | 采样频率 × 符号表 | 按需 profiling |
1.3 80/20 法则:高基数服务
通常是 20% 的服务产生 80% 的可观测数据量。定位步骤:
# Metrics:按 job 排序 series 数(需 prometheus 抓取)
topk(20, count by (job) ({__name__=~".+"}))
# 日志量:按 service label 的 ingest GB/day(Loki 指标因部署而异)
# sum by (service) (rate(loki_distributor_bytes_received_total[1d]))
对 Top 5 “大户”单独制定 retention 和采样 policy——全局一刀切浪费或误删。
1.4 与 SLO / 告警的交叉
二、成本估算 Worksheet(显式假设)
重要:以下数字来自假设模型,用于相对对比和容量规划——不是任何云厂商报价。部署时请填入你环境的实际
$ / GB-month 和
$ / million samples。
2.1 场景假设(Scenario A)
| 假设项 | 取值 | 说明 |
|---|---|---|
| 微服务数量 | 200 | 含 batch job |
| Pod 数量 | 5000 | K8s |
| 总 QPS | 50000 | 峰值 80000 |
| Metrics scrape | 15s | 每 target |
| 平均 series/target | 800 | 含 histogram |
| Log 行/请求 | 3 | JSON 结构化,平均 512 B/行 |
| Trace 采样(当前) | 100% head | 假设未治理 |
| Span/请求 | 8 | 微服务链 |
| Span 大小 | 1 KB | protobuf 后 |
| Profile 频率 | 1/min/pod | 生产常更低 |
2.2 日增量估算公式
Metrics 样本数/天(单 Prometheus _shard 简化):
\[N_{samples/day} \approx N_{series} \times \frac{86400}{scrape\_interval}\]
Scenario A:假设 \(N_{series} = 4 \times 10^6\)(5000 pod × 800),15s scrape:
\[N \approx 4 \times 10^6 \times 5760 \approx 2.3 \times 10^{10} \text{ samples/day}\]
Logs 体积/天:
\[V_{logs} = QPS \times 86400 \times lines/request \times bytes/line\]
\[V \approx 50000 \times 86400 \times 3 \times 512 \approx 6.6 \times 10^{12} \text{ B} \approx 6.1 \text{ TiB/day}\]
(全量 INFO——故意夸大以说明为何必须采样)
Traces 体积/天(100% 采样):
\[V_{traces} = QPS \times 86400 \times spans/request \times bytes/span\]
\[\approx 50000 \times 86400 \times 8 \times 1024 \approx 3.5 \times 10^{13} \text{ B} \approx 32 \text{ TiB/day}\]
2.3 成本代入(占位符)
令:
- \(C_m\) = 每百万 samples 存储成本($/M samples-month)— 读者自填
- \(C_l\) = 每 GiB 日志存储成本($/GiB-month)— 读者自填
- \(C_t\) = 每 GiB Trace 存储成本 — 读者自填
| 支柱 | 月存储量(Scenario A 粗算) | 月成本公式 |
|---|---|---|
| Metrics | 按 retention 30d 累加 | \(\approx N_{samples/month} \times C_m\) |
| Logs | \(V_{logs} \times retention\_days\) | \(V_{logs} \times 30 \times C_l\) |
| Traces | \(V_{traces} \times retention\_days\) | 同上 |
相对结论(不依赖绝对单价):Trace 100% 采样 + Log 全量 INFO 时,Trace+Log >> Metrics——与 1.2 节占比区间一致。
2.4 Scenario B:治理后(假设)
| 杠杆 | 调整 | 存储倍数变化 |
|---|---|---|
| Trace head 1% + tail error 100% | 见 §3 | Trace ≈ ×0.05–0.15 |
| Log INFO 10% | 见 §3 | Log ≈ ×0.3–0.4 |
| Metrics recording + 降 cardinality | 见 §5 | Series ≈ ×0.5 |
| Retention 30d→14d(非 SLO 日志) | 见 §4 | ×0.5 on 该部分 |
Worksheet 应用:先填 Scenario A 自填单价得 baseline,再填 Scenario B 倍数得 target——差值即 ROI 优先级。
三、采样:成本的最强杠杆
降本杠杆排序:采样 > 保留期 > 压缩 > 冷热分层 > SSD→S3。
3.1 与埋点哲学的关系
埋点哲学 §采样:四象限决策——ERROR/慢请求全保留,正常流量可采样。
3.2 Logs 采样
# OpenTelemetry Collector — 合成示例
processors:
probabilistic_sampler/logs:
sampling_percentage: 10
hash_seed: 42
filter/logs_info:
logs:
include:
match_type: strict
record_attributes:
- key: level
value: INFO
pipelines:
logs:
receivers: [otlp]
processors: [filter/logs_info, probabilistic_sampler/logs, batch]
exporters: [loki]策略:
| 级别 | 采样率 | 理由 |
|---|---|---|
| ERROR | 100% | 排障资产 |
| WARN | 100% 或 50% | 视 volume |
| INFO | 1%–10% | 主要体积来源 |
| DEBUG | 0% prod | 仅临时开启 |
3.3 Traces 采样
见 Traces 栈。头部 1% + 尾部保留 error/slow:
processors:
tail_sampling:
decision_wait: 10s
policies:
- name: errors
type: status_code
status_code: {status_codes: [ERROR]}
- name: slow
type: latency
latency: {threshold_ms: 500}
- name: baseline
type: probabilistic
probabilistic: {sampling_percentage: 1}代价:未采样请求无 Trace——排障靠 Metrics + 采样日志 + 关联 trace_id。
3.4 Metrics “采样” = 预聚合
Recording rules 将高 card 原始指标聚合为低 card SLI:
- record: service:http_requests:rate5m
expr: sum by (service, status) (rate(http_requests_total[5m]))删除原始 endpoint label 级
series——存储与查询双赢。
3.5 Profiles 采样
Pyroscope/Parca 连续 profiling 默认低频率(100ms–10ms wall time 采样率因实现而异)——Profiles 通常不是账单第一来源。
四、保留期(Retention)工程策略
4.1 基线建议
| 支柱 | 热(原始精度) | 温(降精度) | 冷(归档) |
|---|---|---|---|
| Metrics | 7–14d @ 15s | 30–90d @ 5m | 1y @ 1h |
| Logs | 7–30d | — | 合规归档 |
| Traces | 3–7d | — | 通常不长期存 |
| Profiles | 14d | — | 可选 S3 |
底线:retention ≥ 平均事故调查窗口 + 安全余量。支付链日志 30d、内部工具 7d。
4.2 Prometheus 本地 retention
# prometheus 启动参数
--storage.tsdb.retention.time=15d
--storage.tsdb.retention.size=500GB两者同时生效——先到者触发删块。见 TSDB 内核 compaction 时序。
坑:retention 过短 + 慢 compaction → WAL 堆积 → OOM。
4.3 Mimir / Thanos 长期 retention
Mimir compactor 配置(合成示例):
compactor:
compaction:
block_ranges: [2h, 12h, 24h]
retention:
retention_period: 365dThanos:--retention.resolution-raw=15d
--retention.resolution-5m=90d
--retention.resolution-1h=365d。
4.4 Loki retention
limits_config:
retention_period: 744h # 31d
compactor:
retention_enabled: true
delete_request_store: s3按 tenant/stream
差异化:table_manager(旧版)或 per-tenant
overrides。
4.5 Tempo retention
compactor:
compaction:
block_retention: 168h # 7d
retention:
retention: 168hTrace 超 7d 查询价值陡降——优先采样而非延长 retention。
4.6 强制删除 vs 归档
删除不可逆;合规日志可 S3 Glacier——恢复延迟小时级,成本降数量级(单价读者自填)。
五、下采样(Downsampling)与聚合
5.1 Metrics 分辨率阶梯
原始 15s ──7d──► 5m 聚合 ──30d──► 1h 聚合 ──365d──► 删除或 Glacier 元数据
Mimir/Thanos compact 自动产生 multi-resolution blocks。
5.2 查询影响
30d 前 p99 精确值不可查——仅 5m/1h 平均。SLO 回顾用 recording rule 预聚合的 SLI,不依赖原始 15s。
5.3 日志 pattern 聚合
Drain/Spell 类算法:存 template + count,不存每条 raw——排障看 pattern 分布。适合安全审计以外的 INFO 洪水。
5.4 与数据模型对照
数据模型:Loki chunk 结构决定删除 retention 后 S3 对象何时 compact 真正释放——可能有延迟。
六、冷热分层与对象存储
flowchart TB
subgraph hot [热层 SSD/NVMe 1-3d]
PH[Prometheus Head]
LI[Loki Index]
end
subgraph warm [温层 HDD/标准S3 3-30d]
PM[Mimir Blocks]
LC[Loki Chunks]
TT[Tempo Blocks]
end
subgraph cold [冷层 Glacier 30d+]
AR[Log Archive]
MR[Metrics 1h blocks]
end
PH -->|remote write| PM
LI --> LC
LC --> AR
PM --> MR
TT --> warm
6.1 Loki boltdb-shipper + S3
- Index:本地/SSD boltdb
- Chunk:S3 标准
- 老 chunk:S3 IA / Glacier(生命周期 policy)
6.2 Tempo
默认全对象存储——“热”在 querier 缓存,非全量 SSD。
6.3 Prometheus
本地 TSDB 仅热;Thanos sidecar → S3 为温/冷。
6.4 自动化迁移
S3
Lifecycle:Transition: 30d → STANDARD_IA,90d → GLACIER。无需人工搬数据。
七、压缩与存储放大
7.1 Metrics:Gorilla + ZSTD
见 TSDB 内核:10:1–20:1 典型。Mimir block ZSTD 再 2:1–3:1。
7.2 Logs:ZSTD 块
Loki chunk ZSTD——相对 ES 倒排索引 5:1–10:1 体积比(同 workload 假设下,见 05-data-model 讨论)。
7.3 Traces:ProtoBuf + ZSTD
Jaeger → Tempo 迁移动机之一:去 ES 索引成本。
八、成本建模与预测
8.1 线性增长模型
\[Data/month \propto QPS \times (log\_lines + spans \times sample\_rate + series)\]
若 QPS 升 2× 而 observability 配置不变 → 账单升 ~2×。若 series 因 label 泄漏升 10× → Metrics 账单升 10× 而 QPS 仅升 20%——80/20 大户常是 label 问题。
8.2 何时考虑自建 vs SaaS
见 自建 vs 托管。粗判:日 ingest > 10TB logs 或 series > 50M 时 SaaS 边际成本陡升——须用 Scenario A/B worksheet 自算。
8.3 Prometheus 容量粗算
\[Disk \approx N_{series} \times retention\_sec / scrape \times bytes\_per\_sample\]
\(bytes\_per\_sample\) 取 1–2 B(压缩后经验值)——仅 order-of-magnitude。
九、工程坑点
9.1 Loki 大查询扫对象存储
max_entries_limit_per_query 过大 + 宽 label
查询 → S3 GET 费用与延迟爆炸。
9.2 Prometheus retention 与 compaction 竞态
删块快于 compact → 查询空洞 + WAL 压力。
9.3 采样率配置错误
sampling_percentage: 0 → 零
Trace。变更后验证 ingest rate。
9.4 retention 压到 3d
事故 T+5 调查时日志已删——保留期底线原则。
9.5 为省成本删 SLO Metrics
短 retention 应用在 DEBUG 日志,不应用在 SLI recording rules 依赖的 raw metrics。
十、降本路径图
- 度量:各支柱 GB/day、series 数、Top 20 服务(§1.3)
- 标记冷热:过去 30d 查询日志——无查询 stream 优先缩 retention
- 采样:INFO log 10%、Trace 1% head + tail error(§3)
- Retention:非核心 30d→14d(§4)
- Downsample:Mimir/Thanos 1h 长期(§5)
- 冷热分层:S3 lifecycle(§6)
- 季度 review:数据增速 vs 业务增速
十一、与告警、SLO 的联合治理
| 数据类型 | SLO 需求 | 成本策略 |
|---|---|---|
| SLI raw metrics | 30d+ 可用 | 独立 retention policy,不与其他混删 |
| Burn Rate recording | 90d | Mimir 5m 块 |
| Debug logs | 7d | 激进采样 |
| Trace | 7d | tail sampling |
告警 Ticket 历史可依赖 Grafana annotations,不需全量 log 365d。
十二、关键概念回顾
- 杠杆顺序:采样 > retention > 压缩 > 分层 > 介质
- Worksheet:Scenario 假设自填单价,算相对 ROI
- 80/20:Top 服务单独 policy
- 底线:retention 长于事故窗口
- SLO 数据:不可作为降本首刀
十三、下一步
成本可控后,多租户隔离与成本分摊是平台化必经之路。下一篇 多租户与安全。
上一篇:告警体系
下一篇:多租户与安全
参考资料
- Grafana Mimir, Compactor, https://grafana.com/docs/mimir/latest/operators-guide/architecture/components/compactor/
- Grafana Loki, Storage, https://grafana.com/docs/loki/latest/operations/storage/
- Grafana Tempo, Retention, https://grafana.com/docs/tempo/latest/operations/retention/
- Thanos, Compaction, https://thanos.io/tip/components/compact.md/
- OpenTelemetry, Sampling, https://opentelemetry.io/docs/concepts/sampling/
附录 A:Worksheet 空白模板
| 假设项 | 你的环境取值 |
|---|---|
| QPS | |
| Series 数 | |
| Log GB/day | |
| Trace GB/day | |
| $/GiB-month (logs) | |
| $/M samples-month | |
| 月总成本 |
附录 B:Retention 变更 checklist
附录 C:高基数服务治理
见 埋点哲学:禁止 user_id/trace_id 作 metric label。
附录 D:分服务 Retention Policy 模板
| 服务 tier | Logs | Traces | Metrics raw | 审批人 |
|---|---|---|---|---|
| Tier0 支付 | 30d | 7d,error 100% | 30d | SRE Lead |
| Tier1 核心 | 14d | 7d,1% head + tail | 15d | Team TL |
| Tier2 内部 | 7d | 3d,1% head | 7d | Team |
| Tier3 batch | 3d | 1d,error only | 7d | Team |
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【可观测性工程】数据模型:时间序列、日志、Span、Profile 的内部表达
拆解 Metrics、Logs、Traces、Profiles、Events 五大支柱在磁盘和内存中的内部数据模型。字段级对照 Prometheus TSDB block、Loki chunk、Tempo block,给出带假设的存储成本估算公式,并解释索引策略如何决定账单与查询延迟。
【可观测性工程】多租户与安全:数据隔离、标签治理、PII 清洗
可观测性平台全公司共享时,查询隔离、写入限流、标签治理、PII 清洗与成本分摊的工程实现。以 Grafana Mimir/Loki/Tempo 的 X-Scope-OrgID 为主线,给出 Collector 配置与合规检查清单。
【可观测性工程】真实事故复盘剧本:从指标抖动到根因的全链路追查
虚构但可复现的 checkout 服务事故全链路:SLO Burn Rate 告警后按 Golden Minute→Metrics→Traces→Logs→Profile→Events 五阶递进排障,含 PromQL/LogQL/kubectl 命令与三条分级剧本,交叉引用系列 01–22。
【可观测性工程】Traces 栈与采样:Jaeger、Tempo、Zipkin、SkyWalking
拆解 Jaeger、Tempo、SkyWalking 架构差异与采样策略(头部/尾部/自适应),给出 W3C TraceContext 传播、OpenTelemetry tail_sampling 配置与选型框架。