可观测性平台分层架构实践:从三支柱到亿级 span/天的工程路径
可观测性不是装个 Prometheus + Grafana 就完事。从数据接入、协议选型、存储分层、降采样策略到查询性能优化,每一个环节都有数倍成本的差异。本篇以亿级 span/天的生产平台为蓝本,给出从零搭建到大规模生产每一层的选型 trade-off 与落地路径。
一、为什么可观测性不是装个 Prometheus
"可观测性"在 2017 年还是个新词,到 2026 年已经成了所有生产系统的标配。但80% 的团队把可观测性做成了"事后查日志"——出问题才知道,平时基本不用。真正的可观测性是在故障发生前就能感知异常,并且能快速定位根因。要做到这一点,必须有完整的数据通路、合理的存储分层、智能的查询能力。
从架构视角看,可观测性平台本质上是一个时序数据处理系统——海量、高写入、低查询延迟、长期归档。所有架构决策最终都收敛为:数据怎么采、怎么存、怎么查、怎么丢。这四个问题决定了平台的成本与能力上限。
graph LR
A[应用] -->|OTel SDK| B[采集层]
B --> C[存储层]
C --> D[查询层]
D --> E[告警/可视化]
B -.指标/日志/链路.-> C
二、三支柱架构:Metrics / Logs / Traces
业界普遍接受的"三支柱"——Metrics(指标)、Logs(日志)、Traces(链路)——是过去十年血泪教训的总结。它们各自回答不同问题,必须协同工作。三者各有最优的存储引擎,硬塞进一个数据库会牺牲所有维度的能力。
graph TB
subgraph 三支柱
M[Metrics
指标
聚合时序]
L[Logs
日志
离散事件]
T[Traces
链路
请求路径]
end
M --> Q1[VM/PromQL]
L --> Q2[Loki/LogQL]
T --> Q3[Tempo/Jaeger]
Q1 --> G[Grafana]
Q2 --> G
Q3 --> G
2.1 三支柱的职责边界
Metrics 回答"发生了什么"——CPU 90%、QPS 1 万、错误率 5%。成本低、聚合强、查询快,适合告警。
Logs 回答"为什么发生"——异常堆栈、用户行为、错误详情。成本中、检索灵活、保留期短。
Traces 回答"在哪发生"——慢在哪一跳、哪个服务、哪个调用。成本高、价值大、采样策略关键。
2.2 关联与统一
三支柱必须通过 trace_id 关联——从 Metrics 告警跳转到 Logs 看详情,从 Logs 跳转到 Traces 看链路。这是可观测性的灵魂。我们用 OpenTelemetry 的 W3C Trace Context 作为统一关联键,所有数据天然打通。
| 支柱 | 回答 | 典型存储 | 保留期 | 采样率 |
|---|---|---|---|---|
| Metrics | What | VictoriaMetrics | 13个月 | 100% |
| Logs | Why | Loki / ES | 30天 | 100% |
| Traces | Where | ClickHouse / Tempo | 7天 | 1-10% |
三、采集层:OpenTelemetry 与采样策略
采集层是入口,决定了数据质量与系统侵入性。OpenTelemetry(OTel)已经成为业界共识——统一了 Metrics/Logs/Traces 三种信号的采集协议,并提供多语言 SDK。但采什么、采多少、怎么采,是工程的关键。
3.1 OTel 协议选型
OTel 支持两种传输协议:OTLP(gRPC/HTTP) 和 Zipkin v2。OTLP 是官方主推,gRPC 高效但有连接管理成本,HTTP 简单但吞吐受限。我们最终用 OTLP/gRPC 做主、OTLP/HTTP 兜底——核心服务用 gRPC,边缘/批量上报用 HTTP。
graph LR
A[应用 OTel SDK] -->|OTLP/gRPC| B[OTel Collector]
A -->|OTLP/HTTP| B
B --> C[VM Remote Write]
B --> D[Tempo HTTP]
B --> E[Loki Push API]
3.2 采样策略
全量采集 = 破产。采样率必须根据业务分级:核心支付链路 100%、普通 API 10%、内部工具 1%。但固定采样率会错过偶发问题。我们用尾部采样(Tail-based Sampling)——先全量采集到 Collector,由 Collector 根据规则决定保留哪些 trace(错误、慢请求、特定标签)。代价是 Collector 压力大,需要专门的资源池。
3.3 数据丰富
原始数据价值有限,必须在采集时丰富——添加业务标签(用户等级、租户 ID、地域)、环境标签(版本、机房、Pod 名)、关联 ID(request_id、trace_id)。这些标签决定了能否快速定位问题。原则:采集时加,查询时减——加标签成本低,减字段成本高。
四、存储层:四个数据库的取舍
存储层是成本的大头。同一个指标,写到不同数据库成本差 5-10 倍。我们经历了四套方案的演进:Prometheus 本地 → Thanos → VictoriaMetrics → ClickHouse 集群。每个方案有明确的适用场景,没有银弹。
4.1 Metrics 存储选型
Metrics 主流方案三个:
- Prometheus:单机强,集群需配合 Thanos/Cortex;适合中小规模(< 千万级 series)
- VictoriaMetrics:单机性能 5-10 倍 Prometheus,集群方案简单;适合中等规模(< 1亿 series)
- Cortex / Mimir:云原生多租户方案;适合大规模(> 1亿 series)但运维复杂
我们最终选 VictoriaMetrics 集群——单机版处理 2000 万 series 毫无压力,集群版通过 vmstorage 分片水平扩展。运维成本比 Cortex 低一个数量级。
4.2 Logs 存储选型
Logs 三个流派——
- Elasticsearch:全文检索强,但存储开销大(原始 JSON + 倒排索引),运维重
- Loki:标签索引 + 压缩存储,成本低 5-10 倍,但查询能力弱(不做全文检索)
- ClickHouse:列式存储,查询灵活,运维中等
我们的方案:核心业务用 Loki(成本最优) + 检索需求强的用 ES(少量)。Loki 的"只索引标签 + 全文靠 grep"哲学极大降低成本——90% 的日志查询靠标签就够。
4.3 Traces 存储选型
Traces 数据量最大(每个请求几十个 span),存储最贵。我们对比了 Tempo、Jaeger、ClickHouse 三个方案——
| 方案 | 存储 | 查询性能 | 成本 | 运维 |
|---|---|---|---|---|
| Tempo | 对象存储(S3) | 中 | 低 | 简单 |
| Jaeger | ES/Cassandra | 高 | 高 | 复杂 |
| ClickHouse | 本地/集群 | 高 | 中 | 中等 |
最终选 ClickHouse——查询性能(毫秒级 P99)、压缩比(10:1)、成本都最优。配合 Kafka 做缓冲,应对突发流量。
五、查询层:索引设计与查询优化
存储决定了能不能查,查询层决定了查得快不快。同样 1 亿条数据,索引设计差 10 倍查询时间差 50 倍。我们做了三层优化:索引选择、查询下推、物化视图。
5.1 索引选择
每个数据库的索引模型不同,但都遵循一个原则:高频查询字段建索引,低频字段不建。我们梳理了 200 个查询场景,发现 80% 的查询只用到 5 个标签(service、env、level、status_code、region)。这 5 个字段必须建索引,其余字段查全表扫描。
5.2 查询下推
PromQL/LogQL 看似简单,实际暗藏性能陷阱。比如 sum(rate(http_requests_total[5m])) 全量扫描和带 {service="payment"} 选择器的时间差 10 倍。必须训练所有 SRE 写高效查询——慢查询不是用户问题,是平台能力问题。
graph TB
Q[查询] --> P[解析与重写]
P --> I[索引下推]
I --> S[分片并行]
S --> M[合并结果]
M --> C[缓存]
5.3 物化视图
Dashboard 的常用查询(QPS、错误率、P99 延迟)每次都算太浪费。我们用Recording Rules(Prometheus)和物化视图(ClickHouse)预先计算,写入新指标。下游查询读预计算结果,速度提升 100 倍。
六、降采样与冷热分层
数据是有时效价值的——1 小时前的明细价值高,1 周前的明细价值低,1 个月前的明细几乎只用来看趋势。降采样 + 冷热分层是控制成本的核心武器。
6.1 降采样策略
原始数据保留 7 天全量(ClickHouse 本地盘),7-30 天降采样到 1 分钟粒度(ClickHouse 冷盘),30-365 天降采样到 5 分钟粒度(对象存储归档)。同一查询在三个存储层自动路由——查询近 7 天走全量,查询近 30 天走 1 分钟粒度,查询近 1 年走 5 分钟粒度。
graph LR
A[原始数据 7天
ClickHouse 热] --> B[降采样 1分钟
30天 ClickHouse 冷]
B --> C[降采样 5分钟
365天 对象存储]
C --> D[删除/归档]
6.2 冷热分层
降采样的关键问题是查询降级——用户查最近 30 天的明细,但只能拿到 1 分钟粒度。Grafana 层面我们做了自动降级提示:查询时间范围超过 7 天时,UI 自动切换为趋势视图,附上"已降采样"提示。透明化降级比静默返回错误更友好。
6.3 存储成本对比
未做降采样:1 亿条 trace × 30天 × 全量 = 50 TB。做降采样:原始 10 TB + 1 分钟 5 TB + 5 分钟 1 TB = 16 TB,成本降 70%。代价是查询粒度变粗,但 90% 的查询不需要全量。
七、告警体系:从规则到因果
告警是可观测性的"输出"——观测到异常必须能及时通知且不淹没真信号。业界从静态阈值告警演进到智能告警,我们经历了三代:阈值规则 → 异常检测 → 因果关联。
7.1 阈值规则
第一代用静态阈值:error_rate > 1% 持续 5 分钟告警。简单有效,但有三大问题:阈值难调(业务变化需频繁调整)、告警风暴(一个根因引发 100 条告警)、无因果(不知道告警之间的关联)。
7.2 异常检测
第二代引入动态基线——根据历史数据自动学习正常范围,超出范围触发告警。我们用 3-sigma + 同环比算法,结合工作日历(区分工作日和节假日)。异常检测解决了阈值难调的问题,但告警风暴和无因果仍在。
7.3 因果关联
第三代是告警聚合与因果推断。我们做了两层:
- 规则聚合:同一服务/同一链路的告警合并为一条
- 拓扑推断:根据服务依赖图,自动推断告警根因——下游 100 条告警可能是上游 1 个故障引起的
graph TB
A[下游告警 x 100] --> B[聚合]
B --> C[拓扑推断]
C --> D[根因: 上游服务 X 故障]
D --> E[通知 oncall]
因果推断让告警量从"每天 500 条"降到"每天 30 条",oncall 工程师终于能睡个好觉。
八、生产经验:亿级 span/天的代价
当我们做到 亿级 span/天 时,遇到的问题和中小规模完全不同。本节总结三个最痛的教训。
8.1 Collector 是单点瓶颈
亿级 span 意味着 OTel Collector 处理每秒 10 万+ span。单 Collector 实例扛不住,必须Collector 集群 + 负载均衡。我们用 8 个 Collector 实例 + Nginx 负载均衡,但每个 Collector 还是需要 4 核 8G——OTel 的 Collector 资源消耗不低。
8.2 Kafka 缓冲是救命稻草
突发流量(秒杀、活动)会让 Collector 直接压垮后端存储。我们引入 Kafka 做缓冲层——Collector 把数据写入 Kafka,Consumer 按自己的节奏消费。代价:链路从"秒级"变成"十秒级",但换来零数据丢失 + 弹性。
graph LR
A[应用] -->|OTLP| B[Collector 集群]
B -->|写入| K[Kafka]
K -->|消费| C[VM]
K -->|消费| D[Tempo/CH]
K -->|消费| E[Loki]
8.3 索引重建是大工程
ClickHouse 索引建错一次,重建需要 2-3 天(10 TB 数据)。我们学会了建索引前先用小数据验证——把生产数据采样 1% 到测试集群,验证查询性能再上线。这个流程救了我们好几次。
九、演进路径与未来形态
可观测性平台还在快速演进。我们看到的未来形态有三个方向:eBPF 内核观测(无侵入采集)、AIOps 根因分析(自动推断)、Profile-Guided Observability(从 profiling 视角看系统)。每一个都不再是"未来"——eBPF 已经在生产中落地,AIOps 在头部公司大规模使用。
9.1 eBPF:零侵入采集
传统采集需要在应用代码里加 SDK——侵入性强、有遗漏。eBPF 通过内核探针在系统层面采集网络、IO、性能数据,无需修改应用。我们已经用 eBPF 替换了部分网络可观测性的应用层采集,覆盖度提升 30%,侵入性降到零。
9.2 AIOps:从告警到自愈
告警的下一步是自动根因分析 + 自愈。我们正在做的实验:用 LLM 分析告警 + 日志 + 链路,自动给出故障定位和修复建议。LLM 在"理解异常文本"上有天然优势——比传统基于规则的关联更灵活。
9.3 Profile 维度
Metrics/Logs/Traces 之外,Profiling(性能剖析)是第四个支柱。它回答"为什么慢"——CPU 热点、内存分配、锁竞争。我们正在集成 Pyroscope、Parca 等开源 profiling 系统,让可观测性覆盖到代码级。
📌 关键架构决策清单
| 决策点 | 推荐方案 | 关键 trade-off |
|---|---|---|
| 采集协议 | OpenTelemetry + OTLP/gRPC | 标准化 vs 厂商绑定 |
| 采样策略 | 尾部采样 + 核心全采 | 成本 vs 完整性 |
| Metrics 存储 | VictoriaMetrics | 性能 vs 运维 |
| Logs 存储 | Loki(主)+ ES(少量) | 成本 vs 全文检索 |
| Traces 存储 | ClickHouse + Kafka | 查询 vs 成本 |
| 降采样 | 7天全量 / 30天1分 / 365天5分 | 查询粒度 vs 成本 |
| 告警 | 异常检测 + 拓扑推断 | 准确率 vs 复杂度 |
| 缓冲层 | Kafka(大规模)/ 直连(小规模) | 弹性 vs 延迟 |