Jaeger + OpenTelemetry:云原生可观测性
Jaeger 与 OpenTelemetry:构建云原生可观测性的标准组合
在现代分布式系统中,理解请求的完整生命周期、快速定位性能瓶颈和故障根源已成为刚需。Jaeger 和 OpenTelemetry 正是这一领域的两大核心项目:前者是久经考验的分布式追踪后端与可视化工具,后者是 CNCF 托管的可观测性数据采集与传输标准。将二者结合,你能用一套统一、无厂商锁定的方式采集链路、指标和日志,并利用 Jaeger 强大的分析能力洞悉系统行为。本教程将手把手带你理解并实践这一组合。
1. 为什么我们需要 OpenTelemetry 和 Jaeger?
现代应用由数十甚至数百个微服务组成,一次用户点击可能触发数十次 RPC 调用。传统日志和监控很难回答“这次请求到底在哪里慢了”或“错误是从哪个服务开始发散的”。分布式追踪通过一次请求的全链路 trace 将分散的事件串联起来,而 OpenTelemetry (OTel) 解决了多语言、多协议数据采集的标准化问题,Jaeger 则针对这些数据提供了高性能的存储、检索和可视化能力。
核心价值:
- 端到端可见性:从边缘网关到数据库查询,完整的调用链清晰呈现。
- 统一采集标准:使用 OTel 的 API/SDK 生成追踪、指标、日志,数据格式与导出工具解耦。
- 灵活的后端切换:今天使用 Jaeger,明天可以无缝迁移到其他支持 OTLP 的后端,无需修改应用代码。
- 原生云原生:两者均为 CNCF 项目,与 Kubernetes、服务网格(如 Istio)深度集成。
2. 认识关键角色
2.1 OpenTelemetry:数据管道的通用接口
OpenTelemetry 是一组 API、SDK、收集器和工具,用于创建和管理遥测数据。它不负责存储和展示,而是专注于:
- 生成:应用通过 OTel SDK 创建 span、metric 等。
- 处理/采样:可在收集器层面对数据进行批处理、过滤、尾部采样。
- 导出:通过 OTLP 协议将数据发送到你选择的后端。
三大信号(Signals):
- Traces(追踪):描述一次请求在分布式系统中的行进路径。
- Metrics(指标):聚合的测量值,如请求速率、错误率、延迟分位数。
- Logs(日志):带时间戳的事件记录,可与 trace 关联。
2.2 Jaeger:分布式追踪的专家
Jaeger 最初由 Uber 开发,专为分布式上下文传播、性能分析和根因判断而设计。它的关键能力包括:
- 接收器:原生支持多种协议(Thrift, OTLP, Zipkin 等)。
- 存储后端:可对接 Elasticsearch、Cassandra、内存等,支持高基数查询。
- 全链路视图:甘特图式的 trace 时间轴,展示每个服务耗时的父子关系。
- 依赖分析:自动推导服务间调用拓扑。
- 自适应采样:根据策略决定保留哪些 trace,平衡成本与覆盖率。
3. 架构总览:如何协同工作
典型的部署架构如下:
┌─────────────┐ OTLP ┌─────────────────┐ OTLP/Thrift ┌─────────┐
│ 服务 A │ ──────────→ │ OpenTelemetry │ ─────────────────→ │ Jaeger │
│ (OTel SDK) │ │ Collector │ │ 后端 │
└─────────────┘ └─────────────────┘ └─────────┘
▲ ▲ │
│ 可选:处理/采样/富化 │
│ ↓
┌─────────────┐ 存储 + 查询 UI
│ 服务 B │
│ (OTel SDK) │
└─────────────┘
- 仪表化(Instrumentation):各服务使用 OTel SDK(或自动仪表化)生成 span,并将 span 上下文通过 HTTP/gRPC 等自动传播。
- 导出到收集器:每个服务可将遥测数据直接通过 OTLP 发送到 Jaeger,但推荐经过 OpenTelemetry Collector。收集器可部署为网关或 Sidecar,负责接收、批处理、过滤、执行尾部采样,然后统一转发到 Jaeger。
- Jaeger 存储与展示:Jaeger 接收并存储 span,提供简洁的 UI 进行查询和分析。
4. 动手实践:从零搭建完整示例
我们将用 Docker Compose 快速拉起一个包含 Jaeger、OpenTelemetry Collector 和示例应用的演示环境。
4.1 准备基础设施
创建 docker-compose.yaml:
version: "3.9"
services:
# Jaeger 一体式镜像,包含 agent、collector、query、UI
jaeger:
image: jaegertracing/all-in-one:1.58
container_name: jaeger
ports:
- "16686:16686" # UI
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
environment:
- COLLECTOR_OTLP_ENABLED=true
restart: unless-stopped
# OpenTelemetry Collector 示例
otel-collector:
image: otel/opentelemetry-collector-contrib:0.102.0
container_name: otel-collector
command: ["--config=/etc/otel-collector-config.yaml"]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- "1888:1888" # 可选的 pprof 扩展
depends_on:
- jaeger
然后在同目录下创建 otel-collector-config.yaml:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
exporters:
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp/jaeger]
启动环境:
docker compose up -d
此时 Jaeger UI 可通过 http://localhost:16686 访问。
4.2 为应用添加 OpenTelemetry 追踪
以 Python 为例,使用自动仪表化最快上手。假设你有一个简单的 Flask 服务。
安装依赖:
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install
配置导出器(环境变量):
export OTEL_SERVICE_NAME=order-service
export OTEL_TRACES_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
启动应用并自动注入仪表化:
opentelemetry-instrument python app.py
对于 Java、Go、.NET 等也都有类似的自动仪表化方案。现在所有请求产生的 span 都会通过 OTLP 发送到 OpenTelemetry Collector,进而转发至 Jaeger。
4.3 在 Jaeger UI 中分析追踪
访问 http://localhost:16686,你将看到服务列表中出现了 order-service。点击 “Find Traces” 即可看到最近的请求 trace。
- Trace 概览:显示总耗时、涉及的服务数量、span 数量。
- 时间瀑布图:每个 span 的父子关系以甘特图呈现,鼠标悬停可查看详细属性(标签、日志事件)。
- 关键分析:点击某个慢 span 可查看其内的自定义属性,例如数据库查询语句、RPC 方法名。
5. 关键配置与最佳实践
5.1 上下文传播
确保所有服务都使用 W3C TraceContext 作为传播格式,这是 OTel 的默认方式,可实现跨语言、跨协议的兼容。如果你有旧系统使用 Zipkin 或 Jaeger 原生传播头,可在 SDK 或收集器层面配置兼容。
5.2 采样策略
并非所有 trace 都需要保留,合理采样可大幅降低存储成本。
- 头部采样(Head Sampling):在 span 创建时决定,例如只保留5%的请求,随机采样或基于规则(如错误请求全保留)。可在 OTel SDK 中配置。
- 尾部采样(Tail Sampling):在 trace 完整后再决策,保留包含错误、长延迟的完整链路。必须在 OpenTelemetry Collector 中配置,因为只有收集器能看到完整链路。Jaeger 自己也支持远程采样策略,可将策略集中管理。
Tail Sampling 配置片段(在 collector 中):
processors:
tail_sampling:
decision_wait: 10s
policies:
- name: error-policy
type: status_code
status_code: { status_codes: [ERROR] }
- name: latency-policy
type: latency
latency: { threshold_ms: 500 }
5.3 丰富 span 信息
利用 OTel SDK 添加自定义属性,将业务上下文注入:
from opentelemetry import trace
current_span = trace.get_current_span()
current_span.set_attribute("user.id", "12345")
current_span.set_attribute("order.amount", 99.9)
这些属性会出现在 Jaeger UI 的 span 详情中,极大提升排查效率。
5.4 日志与 trace 关联
将日志关联到对应的 trace 和 span,实现从日志跳转到 trace 的能力。在结构化日志中注入 trace_id 和 span_id,例如在Java中使用MDC:
import io.opentelemetry.api.trace.Span;
Span.current().getSpanContext().getTraceId();
Jaeger UI 支持在 span 的 “Logs” 面板中查看关联的事件。
5.5 监控 Jaeger 自身
关注 Jaeger 组件的健康度,使用 Prometheus 采集 Jaeger 的 metrics(由 SPAN_STORAGE_TYPE 等配置暴露),配合 Grafana 仪表盘。OpenTelemetry Collector 也应导出自身遥测数据到监控系统。
6. 常见问题与故障排除
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| UI 中看不到服务 | OTLP 端点不通或服务名称未设置 | 检查 OTEL_EXPORTER_OTLP_ENDPOINT 是否正确,确认 Jaeger 是否启用了 OTLP receiver (--collector.otlp.enabled=true) |
| Span 不完整或缺失 | 采样丢弃、传播头丢失 | 检查采样率;确认服务间请求头传递 W3C traceparent |
| Jaeger 内存飙升 | 内存存储模式下 trace 过多 | 设置 SPAN_STORAGE_TYPE=badger 并限制保留时长,或切换至 Elasticsearch |
| 收集器报错 “connection refused” | Jaeger 尚未就绪或地址错误 | 确保收集器配置中的 Jaeger endpoint 容器名正确(如 jaeger:4317) |
7. 进阶方向
- Kubernetes 原生集成:使用 OpenTelemetry Operator 自动注入 sidecar 或自动仪表化,搭配 Jaeger Operator 在集群内全托管。
- 服务网格配合:Istio 的分布式追踪默认使用 Zipkin 协议,可配置 MeshConfig 将 traces 直接发送至 Jaeger,或通过 OpenTelemetry Collector 作为统一入口。
- 指标与告警:将 OTel 生成的指标导出至 Prometheus,使用 Jaeger 的 latency 数据配置 Prometheus 告警规则。
- 多租户与身份认证:为 Jaeger Query 添加 OAuth 代理或 JWT 认证,按 team 隔离 trace 访问。
总结
Jaeger 与 OpenTelemetry 的融合并非简单的“发送-接收”关系,而是一个精心设计的可观测性管道:OpenTelemetry 负责统一生成与路由遥测信号,Jaeger 作为专业的追踪后端提供深度分析与可视化。这种组合已成为云原生观测的事实标准,既能帮助你轻松起步,又能在系统规模增长时提供足够的灵活性与韧性。立即开始为你的服务添加一行 opentelemetry-instrument,让复杂的调用链从此透明。