土法炼钢兴趣小组的算法知识备份

【可观测性工程】Traces 栈与采样:Jaeger、Tempo、Zipkin、SkyWalking

文章导航

分类入口
architectureobservability
标签入口
#tracing#jaeger#tempo#skywalking#opentelemetry#sampling#tracecontext#distributed-tracing

目录

Traces 栈与采样:Jaeger、Tempo、Zipkin、SkyWalking

你在 Jaeger UI 里搜 duration > 500ms,点了 Search,然后去倒了杯咖啡。回来的时候,查询还在转圈。这不是 Jaeger 实现不好——是你让 Jaeger(背后是 Elasticsearch)在几十亿个 Span 上执行了一个全索引范围扫描。你对 Jaeger 说”给我所有慢于 500ms 的 Span”,Jaeger 去问 ES,ES 去扫所有分片——然后你等了 2 分钟。

这就是分布式追踪系统的核心矛盾:Trace 数据的写入量是 Metrics 的几十到几百倍,但查询需求不像 Logs 那样必须支持全文搜索——却也不是简单的 key-value 查找能完全满足的。不同追踪系统对这个矛盾的不同解法,定义了它们的架构灵魂。

一、分布式追踪的演化简史

Google 在 2010 年发表了 Dapper 论文(Sigelman et al.),描述了一个大规模分布式追踪基础设施的架构。Dapper 的核心概念至今仍在沿用:Trace 是 Span 的有向无环图、采样在请求入口决策、Span 携带 application-level annotation。但 Dapper 没有开源——它启发了一整个时代的克隆。

2012 年 Twitter 开源了 Zipkin,第一个广泛使用的开源分布式追踪系统。2015 年 Uber 开源了 Jaeger,基于 Go 重写,内置了自适应采样和 Elasticsearch/Cassandra 存储后端。2019 年 OpenTracing 和 OpenCensus 合并为 OpenTelemetry——从此分布式追踪的 API 和 SDK 被统一,竞争从”用谁的 SDK”转向”用谁的后端”。2021 年 Grafana 发布 Tempo——一个完全不同的设计哲学(不索引 Span attribute),引发了一场关于”Trace 存储到底该不该建索引”的持续讨论。同年,Apache SkyWalking 从中国社区走向全球,成为 Apache 顶级项目。

二、Span 与 Trace:数据结构与传播

分布式追踪的数据模型是建立在两个抽象上的:Span(一个操作单元)和 Trace(一组有向 Span 的树或 DAG)。SpanContext 是跨服务传播的载体——它包含四个字段:

W3C TraceContext 标准(2021 年 W3C Recommendation)定义了 HTTP header 格式:traceparent: 00-{trace_id}-{span_id}-{trace_flags}。这个标准是所有传播实现的基础——任何不遵循这个格式的传播都是兼容性问题。

三、Jaeger:全索引路线的旗舰

Jaeger 的架构由 5 个核心组件构成:

Jaeger 对 Span 的核心处理是”把每个 Span 打散写入 Elasticsearch 的索引”——trace_idoperation_namestart_timedurationtags(attributes)、process(resource attributes)——全部映射为 ES 的字段并建倒排索引。这让你可以秒级搜索”所有 service=checkouthttp.status_code=500 的 Span”。

代价也很明确:ES 的存储放大(倒排索引通常是原始数据的 1.5–3 倍)加上 Jaeger 对每个 Span 的多维度索引——在 5000 QPS、每个请求 10 个 Span 的场景下,日增 ES 数据可达数百 GB。Jaeger + ES 的月存储成本通常是同等 Tempo 方案的 5–10 倍。

四、Tempo:无索引路线

Tempo 的设计哲学是”Trace 数据最大的特征是按 trace_id 查找——所以只索引 trace_id 就够了”。它把 Span 按 trace_id 分组 → 在 Ingester 内存中暂存 → WAL → 满即 flush 为 Parquet Block → 上传 S3/GCS/Azure。

查询 trace_id = X:通过 consistent hash ring 定位 → 如果 trace 还在 WAL 从 Ingester 返回,如果在 Block 从对象存储读取 Parquet row group → 解压返回。全程不需要查询任何索引——因为 trace_id 本身就是定位 key。

Tempo 的 attribute 搜索靠 TraceQL:{duration > 500ms && status = error}。TraceQL 的实现是并发扫描对象存储中的 Block,对每个 Block 做过滤——依赖 parquet 的列存特性(只读需要的列)和 predicate pushdown。在单日数亿 Span 的规模下,搜索延迟通常在 3–15 秒——不是 ES 的毫秒级,但通常够用。

Tempo 的存储成本优势是压倒性的(大约是 Jaeger + ES 的 1/10 到 1/20),但代价是:别期望毫秒级 attribute 搜索;别把 Span attribute 当 SQL WHERE clause 用;如果你的团队每天都在问”上周四 user_id=xxx 的请求有没有报过错”,Tempo 可能不适合(Jaeger + ES 或 Datadog 的全文搜索更适合)。

五、SkyWalking:中国开源的 APM 级方案

Apache SkyWalking 由吴晟于 2015 年创建。它不是”Trace backend”——而是”Application Performance Monitoring 平台”:自动的字节码增强(Java Agent 通过 ByteBuddy 注入埋点,无需改代码)、服务拓扑图、端点依赖、数据库慢查询监控、JVM 指标——这些在 Jaeger/Tempo 中都是缺失的能力。

SkyWalking 的存储支持 ES、H2、MySQL、TiDB、PostgreSQL、BanyanDB(自研)。它使用 ES 做 Trace 存储时同样面临索引膨胀问题,但 SkyWalking 的社区提供了更细粒度的索引控制——可以按 endpoint 和错误状态选择性索引。

SkyWalking 的优势在国内环境特别明显:中文文档、中文社区、Apache 顶级项目的信任背书、以及”开箱即用”的全功能 APM 体验。劣势是在全球范围内不如 Jaeger/Tempo 与 OTel 生态的集成深度——SkyWalking 有 OTel 兼容但它的原生协议不是 OTLP。

六、采样策略

Traces 必须采样——全量 Trace 的量级是 Metrics 的 50–100 倍。采样策略三种:

头部采样:在第一个 Span 创建时按 trace_id hash 决策。简单零开销但决策不可逆——如果一条错误落在 99% 被丢弃的区域,它的 Trace 永远回不来。这是为什么生产环境绝对不应该只用头部采样。

尾部采样:等所有 Span 到达 Collector 后基于完整信息(status_code、duration、attribute)决策。保证所有错误和慢请求不丢失——但需要 Span Buffer(内存开销)。标准生产配置:头部 1% baseline + 尾部全量保留所有 status=ERROR 和 duration > 阈值 的 Trace。

自适应采样:根据实时错误率和延迟分布自动调整采样率。Jaeger v2 实现了这个能力,但在反馈延迟(几十秒到几分钟)上仍有天然限制。

七、选型决策

条件 推荐 理由
团队 < 20 人,自建 LGTM 栈 Tempo 与 Grafana/Loki/Mimir 统一运维,存储成本极低
已有 ES 集群,需要全文搜索 Span Jaeger + ES 利用已有 ES 运维经验,毫秒级 attribute 搜索
国内团队,要 APM 全功能 SkyWalking 字节码增强、服务拓扑、中文社区
Trace 数据量极大(> 10B span/day) Tempo 只有 Tempo 能在这个量级下保持合理成本
需要灵活采样和多后端 fan-out OTel Collector → 任选后端 Collector 统一接收→处理→路由,后端可插拔

八、关键概念回顾

九、工程坑点

跨服务 traceparent 丢失:消息队列不自动传播 trace context → 在 producer 手动注入 traceparent 到 message header,consumer 手动恢复。

Span 时钟偏差:不同节点 NTP 不同步 → 子 Span 看起来晚于父 Span → Jaeger UI 报 “clock skew”。强制全集群 NTP 同步,允许 < 100ms 偏差。

ES 热索引分片爆炸:Jaeger 按天建 index 但分片数不合理 → ES 集群挂掉。根据日均 Span 数量合理设置 index 分片(每个分片 < 50GB)。

十、下一步

Traces 栈选好之后,我们进入可观测性最深的一层——内核追踪。下一篇 内核追踪:ftrace、kprobe、uprobe、tracepoint 生产实战


上一篇日志管道:Fluent Bit、Vector、Logstash、Cribl 的取舍

下一篇内核追踪:ftrace、kprobe、uprobe、tracepoint 生产实战

参考资料

  1. B. H. Sigelman et al., Dapper, a Large-Scale Distributed Systems Tracing Infrastructure, Google Technical Report, 2010
  2. W3C, Trace Context, W3C Recommendation, 2021, https://www.w3.org/TR/trace-context/
  3. Jaeger, Architecture, https://www.jaegertracing.io/docs/latest/architecture/
  4. Grafana Tempo, Architecture, https://grafana.com/docs/tempo/latest/architecture/
  5. Apache SkyWalking, Documentation, https://skywalking.apache.org/docs/

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-13 · architecture

【系统架构设计】分布式追踪:OpenTelemetry 与全链路可观测

分布式追踪的采样率设多少?100% 采样的成本和收益分别是什么?本文从 Google Dapper 论文的 Trace/Span 模型出发,拆解 W3C Trace Context 标准的传播机制,深入 OpenTelemetry SDK、Collector、Exporter 三层架构,对比 Jaeger 与 Tempo 的存储设计差异,讨论头部采样、尾部采样与自适应采样的工程取舍,结合 Uber 迁移 OpenTelemetry 的实战经验,给出追踪数据驱动的自动拓扑发现与关键路径分析方法。


By .