Jaeger — 分布式追踪系统
是什么
Jaeger 是 Uber 2017 年开源的分布式追踪系统,灵感来自 Google 2010 年那篇 Dapper 论文。
日常类比:
- prometheus 看的是一段时间的指标——这一小时 CPU 多少、错误率多高
- Jaeger 看的是一次请求的完整路径——这一个用户点了下单,请求穿过 30 个服务,哪个服务慢、哪个抛错、哪个调了下游什么——一目了然
如果你的系统只有一个进程,print 日志就够了。但当一次请求要穿过订单服务、库存服务、风控服务、支付服务、消息队列、3 个数据库——日志散在 8 台机器上、时间戳还有偏差——只看日志根本拼不回来一次请求做了什么。
Jaeger 就是来拼这个的。
为什么重要
四个理由:
- 微服务排错刚需——单体应用 print 日志就行;微服务下”哪个服务慢”看不出来,必须要追踪
- OpenTracing / OpenTelemetry 标准支持——Jaeger 是这两个标准的早期实现者,跨语言(Go / Java / Python / Node / Rust)通吃
- CNCF 毕业项目——2019 年从 CNCF 孵化器毕业,等于拿到了”云原生事实标准”的徽章
- 大厂背书——Uber / Lyft / Red Hat / ByteDance / 阿里都在用;不是玩具
核心要点
理解 Jaeger 抓三个概念就够了。
Span(一次操作)
一个 Span 是一次操作的记录——比如”调用了下游订单服务”或”查了一次数据库”。一个 Span 包含:
- 开始时间 + 结束时间(差值就是耗时)
- 操作名(如
POST /orders) - Tags(键值对,
http.status=500、user.id=42) - Logs(这次操作中间发生的事件)
Trace(一组关联 span)
一个 Trace 是一次完整请求穿过整个系统的所有 span,通过同一个 trace-id 串联。span 之间有父子关系——一个 span 调了下游,下游就是子 span。
可视化是这样:
[Trace abc123]├── span: API gateway (100ms)│ └── span: order service (80ms)│ ├── span: db query (30ms)│ └── span: rpc call (40ms)│ └── span: ...Sampling + Storage
- Sampling(抽样):生产环境一秒动辄上万次请求,全存爆炸;通常采样 1%-10%。常见策略:probabilistic(概率)/ rate-limiting(限速)/ adaptive(自适应)
- Storage(存储后端):Jaeger 自己不存数据,靠后端。默认 elasticsearch,也支持 cassandra / Kafka / Badger
实践案例
案例 1:Docker 一键启
最快上手:
docker run -d \ -p 6831:6831/udp \ -p 16686:16686 \ jaegertracing/all-in-one:latest然后浏览器开 http://localhost:16686,Jaeger UI 就来了。注意 all-in-one 把 collector / query / storage 都塞一个镜像里——只能玩玩,不能上生产。
案例 2:Python 接入
用 OpenTelemetry SDK:
from opentelemetry import tracefrom opentelemetry.exporter.jaeger.thrift import JaegerExporterfrom opentelemetry.sdk.trace import TracerProviderfrom opentelemetry.sdk.trace.export import BatchSpanProcessor
trace.set_tracer_provider(TracerProvider())tracer = trace.get_tracer(__name__)
exporter = JaegerExporter(agent_host_name="localhost", agent_port=6831)trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(exporter))
with tracer.start_as_current_span("checkout"): with tracer.start_as_current_span("validate-cart"): # 你的业务代码 pass with tracer.start_as_current_span("charge-payment"): pass跑起来后,Jaeger UI 上就能看到 checkout 这个 trace,里面嵌套两个子 span。
案例 3:UI 看时间线
Jaeger UI 主视图是一条水平时间线:
- 横轴是时间(毫秒)
- 每一行是一个服务的一个 span
- 子 span 缩进显示
- 鼠标 hover 看 tags / logs
实战价值:一眼就能看出”这次 800ms 的请求里,500ms 都耗在风控服务的某个 redis 查询上”。这种洞察力,光看日志是拿不到的。
踩过的坑
- 采样率太低会错过偶发慢请求——1% 采样意味着 99% 请求被丢,那个偶尔出现的 30 秒慢请求多半不在你抽到的 1% 里。生产建议用 adaptive sampling,对慢请求 / 错误请求强制保留
- elasticsearch 后端写入压力大——trace 数据量是日志的几倍,要配 ILM(Index Lifecycle Management)自动删旧索引;也要给 ES 单独的集群,不要和业务日志混
- Trace 上下文传播配错链路就断——HTTP 服务靠
traceparentheader 传递,gRPC 靠 metadata;中间任何一层没传,下游 span 就成了孤儿。框架自动注入是常态,但跨技术栈(HTTP → 消息队列 → gRPC)容易漏 - All-in-one 不是生产用——生产要分开部署 collector(接收 span)/ query(查询 UI)/ storage(持久化),三层独立扩容;一键镜像只是 demo
适用 vs 不适用场景
适用:
- 微服务架构(≥ 5 个服务),跨服务请求耗时排查
- 突发慢请求 / 偶发错误的根因定位
- 跟 prometheus / Grafana 组合做”先指标后 trace” 的分层排错
不适用:
- 单体应用——一根 trace 全在一个进程里,APM 工具更直接
- 极致低延迟(每次插入都要传 context)的高频路径——trace 本身有 1-2% 开销
- 数据极敏感 + 跨境合规要求严的场景——trace 里常含 PII,要单独做脱敏
历史小故事(可跳过)
- 2017 年 4 月:Uber 工程团队开源 Jaeger(名字来源于德语”猎人”)
- 2017 年 9 月:捐给 CNCF,进入孵化器
- 2019 年 10 月:从 CNCF 毕业(继 Kubernetes / prometheus 之后又一个图形化云原生项目)
- 2021 年:与 OpenTelemetry 整合——OpenTelemetry 成为新事实标准,Jaeger 转向兼容
- 2024 年:Jaeger v2 发布,底层用 OpenTelemetry SDK 重写,正式成为 OTel 生态一员
学到什么
- 观测三件套是分工而非替代:metrics(prometheus)/ logs / traces(Jaeger)各管一个维度,缺一不可
- trace-id 是把分布式系统拼回一致体验的核心抽象——一根线把分散的事件串起来
- 采样不是省钱手段,是必需品——100% 存储等于让追踪系统拖垮主系统
- CNCF 毕业不等于永远主导——Jaeger 毕业 5 年后被 OpenTelemetry 反客为主,开源世界没有终局
关联
- prometheus —— 监控三件套之一,看时间序列指标
- elasticsearch —— Jaeger 默认存储后端
- cassandra —— Jaeger 另一个常用存储后端
- grafana —— 常和 Jaeger 配合,从指标跳到对应 trace 排错