可观测性 / SRE 2026-09-11

可观测性平台分层架构实践:从三支柱到亿级 span/天的工程路径

可观测性不是装个 Prometheus + Grafana 就完事。从数据接入、协议选型、存储分层、降采样策略到查询性能优化,每一个环节都有数倍成本的差异。本篇以亿级 span/天的生产平台为蓝本,给出从零搭建到大规模生产每一层的选型 trade-off 与落地路径。

可观测性 Prometheus OpenTelemetry ClickHouse VictoriaMetrics Loki 降采样 冷热分层

一、为什么可观测性不是装个 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 作为统一关联键,所有数据天然打通。

支柱回答典型存储保留期采样率
MetricsWhatVictoriaMetrics13个月100%
LogsWhyLoki / ES30天100%
TracesWhereClickHouse / Tempo7天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)。这些标签决定了能否快速定位问题。原则:采集时加,查询时减——加标签成本低,减字段成本高。

经验法则:核心服务 100% 采样 + 尾部采样,边缘服务按比例采样。永远先采 100% 进 Collector,再决定丢弃哪些——这样既不漏关键链路,又控制存储成本。

四、存储层:四个数据库的取舍

存储层是成本的大头。同一个指标,写到不同数据库成本差 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)中低简单
JaegerES/Cassandra高高复杂
ClickHouse本地/集群高中中等

最终选 ClickHouse——查询性能(毫秒级 P99)、压缩比(10:1)、成本都最优。配合 Kafka 做缓冲,应对突发流量。

核心原则:存储选型不是"用最好的",而是"用最合适的"。核心是成本可控 + 性能达标 + 运维可承受三个维度平衡。我们花了 18 个月做了 4 次存储迁移,每一次都是血泪教训。

五、查询层:索引设计与查询优化

存储决定了能不能查,查询层决定了查得快不快。同样 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% 的查询不需要全量。

经验数据:从 50 TB 降到 16 TB,硬件成本从月 5 万降到月 1.5 万。可观测性的成本战争,就是存储成本的战争。

七、告警体系:从规则到因果

告警是可观测性的"输出"——观测到异常必须能及时通知且不淹没真信号。业界从静态阈值告警演进到智能告警,我们经历了三代:阈值规则 → 异常检测 → 因果关联。

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% 到测试集群,验证查询性能再上线。这个流程救了我们好几次。

教训:亿级 span 不是"配置大一点就行",而是每个组件都要重新设计。我们花了 6 个月才把亿级 span 平台稳定下来,期间踩了 12 个坑。中小规模能用的方案,直接搬到大规模必然崩。

九、演进路径与未来形态

可观测性平台还在快速演进。我们看到的未来形态有三个方向: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 延迟
写在最后:可观测性的难处不是技术,而是成本与价值的平衡。装十套工具、不如把一套打透;告警一万条、不如根因一条。可观测性的终极目标不是"看到一切",而是"在故障发生前预警,在故障发生时秒级定位"。这才是「探索架构之美」在 SRE 领域的回响——让复杂度变得可控。