601_Jaeger链路追踪
一句话概述:Jaeger 是 Uber 开源的分布式链路追踪系统(CNCF 毕业项目),用于监控微服务架构中请求的完整调用链路——一个请求经过了哪些服务、每个服务耗时多少、在哪个环节出了问题,是微服务性能排查的"X 光机"。
核心知识点表
| 概念 | 白话解释 |
|---|
| Trace | 链路,一个请求的完整调用过程(从入口到返回) |
| Span | 跨度,链路中的一个操作(如一次 HTTP 调用、一次数据库查询) |
| OpenTelemetry | OTel,链路追踪的统一标准(Jaeger v2 原生支持) |
| OTLP | OpenTelemetry Protocol,数据传输协议 |
| Collector | 收集器,接收、处理和存储链路数据 |
| Sampling | 采样,不是每个请求都记录,按比例采样以降低开销 |
| Context Propagation | 上下文传播,在服务间传递 TraceID |
安装配置
All-in-One 部署(开发测试)
# Docker 一键启动(内存存储)
docker run -d --name jaeger \
-p 4317:4317 \ # OTLP gRPC(推荐)
-p 4318:4318 \ # OTLP HTTP
-p 16686:16686 \ # Web UI
-p 9411:9411 \ # Zipkin 兼容端口
jaegertracing/jaeger:latest # Jaeger v2 最新版
# 访问 http://localhost:16686 查看 Web UI
# Jaeger v2 基于 OpenTelemetry Collector 构建
# 默认接收 OTLP 格式数据(gRPC:4317, HTTP:4318)
生产部署(Elasticsearch 后端)
# docker-compose.yml — Jaeger + Elasticsearch
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.0
container_name: elasticsearch
environment:
- discovery.type=single-node # 单节点模式
- xpack.security.enabled=false # 禁用安全(测试用)
- "ES_JAVA_OPTS=-Xms1g -Xmx1g" # JVM 内存
ports:
- "9200:9200" # ES API
volumes:
- es-data:/usr/share/elasticsearch/data
jaeger:
image: jaegertracing/jaeger:latest # Jaeger v2
container_name: jaeger
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
- "16686:16686" # Web UI
environment:
- JAEGER_DISABLED=false
volumes:
- ./jaeger-config.yaml:/etc/jaeger/config.yaml
command: ["--config", "/etc/jaeger/config.yaml"]
depends_on:
- elasticsearch
volumes:
es-data:
# jaeger-config.yaml — Jaeger v2 配置
# Jaeger v2 使用 OpenTelemetry Collector 配置格式
receivers:
otlp: # 接收 OTLP 数据
protocols:
grpc:
endpoint: "0.0.0.0:4317" # gRPC 端口
http:
endpoint: "0.0.0.0:4318" # HTTP 端口
exporters:
elasticsearch: # 导出到 Elasticsearch
endpoints:
- "http://elasticsearch:9200" # ES 地址
indices:
spans:
name: "jaeger-spans" # Span 索引名
services:
name: "jaeger-services" # 服务索引名
extensions:
jaeger_query: # Jaeger 查询 UI
storage:
traces:
elasticsearch:
endpoints:
- "http://elasticsearch:9200"
service:
extensions: [jaeger_query]
pipelines:
traces:
receivers: [otlp]
exporters: [elasticsearch]
Kubernetes 部署
# 使用 Helm 安装 Jaeger Operator
helm repo add jaegertracing https://jaegertracing.github.io/helm-charts
helm install jaeger-operator jaegertracing/jaeger-operator \
--namespace observability \
--create-namespace
# 创建 Jaeger 实例
kubectl apply -f - <<EOF
apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
name: production
namespace: observability
spec:
strategy: production # 生产策略(组件分离)
storage:
type: elasticsearch # 使用 ES 存储
options:
es:
server-urls: http://elasticsearch:9200
collector:
replicas: 2 # 2 个 Collector 副本
query:
replicas: 2 # 2 个 Query 副本
EOF
基本使用
应用接入(Python 示例)
# pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp
# pip install opentelemetry-instrumentation-flask opentelemetry-instrumentation-requests
from flask import Flask # Web 框架
from opentelemetry import trace # 追踪 API
from opentelemetry.sdk.trace import TracerProvider # 追踪提供者
from opentelemetry.sdk.trace.export import BatchSpanProcessor # 批量导出
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter # OTLP 导出器
from opentelemetry.instrumentation.flask import FlaskInstrumentor # Flask 自动埋点
from opentelemetry.instrumentation.requests import RequestsInstrumentor # requests 自动埋点
from opentelemetry.sdk.resources import Resource # 资源信息
# 配置 TracerProvider
resource = Resource.create({
"service.name": "my-web-app", # 服务名(在 Jaeger UI 中显示)
"service.version": "1.0.0", # 服务版本
"deployment.environment": "production" # 部署环境
})
provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(
OTLPSpanExporter(endpoint="http://localhost:4317") # Jaeger OTLP 地址
)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
# 创建 Flask 应用
app = Flask(__name__)
FlaskInstrumentor().instrument_app(app) # 自动追踪所有 HTTP 请求
RequestsInstrumentor().instrument() # 自动追踪所有 HTTP 调用
# 手动创建自定义 Span
tracer = trace.get_tracer("my-web-app") # 获取 Tracer
@app.route("/api/users")
def get_users():
with tracer.start_as_current_span("fetch-users") as span: # 创建 Span
span.set_attribute("user.count", 42) # 设置属性
span.add_event("querying database") # 添加事件
# 业务逻辑...
return {"users": [...]}
if __name__ == "__main__":
app.run(port=8080)
应用接入(Node.js 示例)
// npm install @opentelemetry/api @opentelemetry/sdk-node
// npm install @opentelemetry/auto-instrumentations-node
// npm install @opentelemetry/exporter-trace-otlp-grpc
// tracing.js — 在应用入口前加载
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const sdk = new NodeSDK({
serviceName: 'my-node-app', // 服务名
traceExporter: new OTLPTraceExporter({
url: 'http://localhost:4317', // Jaeger OTLP gRPC 地址
}),
instrumentations: [
getNodeAutoInstrumentations(), // 自动埋点(HTTP、Express、MySQL 等)
],
});
sdk.start(); // 启动 SDK
// 启动应用:node -r ./tracing.js app.js
Web UI 使用
# 访问 http://localhost:16686
# === 搜索链路 ===
# 1. 选择 Service(服务名)
# 2. 选择 Operation(操作名,如 GET /api/users)
# 3. 设置时间范围
# 4. 可选:设置标签过滤(如 http.status_code=500)
# 5. 点击 Find Traces
# === 查看链路详情 ===
# 点击具体 Trace → 看到完整调用链路
# 瀑布图显示:
# - 每个 Span 的名称和服务
# - 每个 Span 的耗时(柱状图)
# - Span 之间的父子关系
# - 每个 Span 的标签和日志
# === 对比链路 ===
# 选择两条 Trace → Compare → 对比正常和异常请求的差异
高级用法
采样策略
# 不是所有请求都需要追踪,采样可以降低开销
# 方法1:头部采样(在 SDK 中配置)
# 概率采样:10% 的请求被追踪
from opentelemetry.sdk.trace.sampling import TraceIdRatioBased
provider = TracerProvider(
sampler=TraceIdRatioBased(0.1), # 10% 采样率
resource=resource
)
# 方法2:尾部采样(在 Collector 中配置)
# 只保留有错误或慢请求的链路
processors:
tail_sampling:
decision_wait: 10s # 等待 10 秒再决策
policies:
- name: errors # 保留所有错误链路
type: status_code
status_code: {status_codes: [ERROR]}
- name: slow-requests # 保留慢请求
type: latency
latency: {threshold_ms: 1000} # 超过 1 秒的请求
- name: percentage # 其余按 10% 采样
type: probabilistic
probabilistic: {sampling_percentage: 10}
与 Prometheus + Grafana 集成
# Jaeger 提供 Prometheus 指标:
# - jaeger_collector_spans_received_total → 收到的 Span 总数
# - jaeger_collector_traces_saved_total → 保存的 Trace 总数
# - jaeger_query_requests_total → 查询请求总数
# 在 Grafana 中同时查看:
# - Metrics(Prometheus)→ 系统指标和告警
# - Traces(Jaeger)→ 链路追踪
# - Logs(Loki)→ 日志
# 三者可以通过 TraceID 关联跳转
SPM(服务性能监控)
# Jaeger v2 内置 SPM 功能
# 自动计算每个服务的:
# - 请求速率(Requests/sec)
# - 错误率(Error Rate)
# - 延迟分布(P50/P95/P99)
# 类似于 Datadog APM 的 RED 指标
# 在 Jaeger UI → Monitor 标签页查看
常见报错
| 报错信息 | 原因 | 解决方案 |
|---|
connection refused :4317 | Jaeger 未启动或端口不通 | 检查 Jaeger 状态和端口映射 |
traces not appearing | 数据未发送到 Jaeger | 检查 OTLP exporter 配置和网络 |
storage capacity exceeded | 存储空间不足 | 清理旧数据或扩容存储 |
too many spans | Span 数据量过大 | 配置采样策略降低数据量 |
context propagation failed | 上下文未正确传播 | 检查 SDK 的自动埋点配置 |
速查表
# === 关键端口(Jaeger v2) ===
# 4317 → OTLP gRPC(推荐)
# 4318 → OTLP HTTP
# 16686 → Web UI
# 9411 → Zipkin 兼容
# === Docker 快速启动 ===
docker run -d -p 4317:4317 -p 16686:16686 jaegertracing/jaeger:latest
# === 常用 SDK 包 ===
# Python: opentelemetry-sdk, opentelemetry-exporter-otlp
# Node.js: @opentelemetry/sdk-node, @opentelemetry/exporter-trace-otlp-grpc
# Java: opentelemetry-sdk, opentelemetry-exporter-otlp
# Go: go.opentelemetry.io/otel, go.opentelemetry.io/otel/exporters/otlp
# === 环境变量配置 ===
OTEL_SERVICE_NAME=my-service # 服务名
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 # OTLP 地址
OTEL_TRACES_SAMPLER=traceidratio # 采样器
OTEL_TRACES_SAMPLER_ARG=0.1 # 采样率(10%)
# === 可观测性三支柱 ===
# Metrics → Prometheus(指标监控)
# Logs → Loki(日志聚合)
# Traces → Jaeger(链路追踪)
# 三者通过 TraceID 关联
同类对比
| 特性 | Jaeger | Zipkin | Tempo | SigNoz |
|---|
| CNCF | 毕业项目 | 非 | 非(Grafana) | 非 |
| 协议 | OTLP 原生 | Zipkin/OTLP | OTLP | OTLP |
| 存储 | ES/Cassandra/内存 | ES/MySQL/内存 | 对象存储 | ClickHouse |
| Web UI | 内置 | 内置 | Grafana | 内置 |
| 存储成本 | 中 | 中 | 低 | 中 |
| 与 Grafana | 插件 | 插件 | 原生 | 独立 |
| 学习曲线 | 中等 | 低 | 低 | 低 |
选型建议:企业级链路追踪首选 Jaeger(CNCF 毕业、功能最全、v2 原生 OTLP);已用 Grafana 生态选 Tempo(存储成本最低、Grafana 原生集成);想要 All-in-One 可观测平台选 SigNoz(Metrics+Logs+Traces 一体)。