Jaeger —— 基于 OpenTelemetry Collector 的分布式链路追踪平台
分类:05 APM与链路追踪
适用:微服务 / 云原生架构的端到端链路追踪、慢调用与根因定位;从本机调试到大规模生产集群
一句话:CNCF 毕业项目,v2 起以 OpenTelemetry Collector 为内核,OTLP 收 span、可插拔存储、UI 查 Trace
官方:https://www.jaegertracing.io/ | https://github.com/jaegertracing/jaeger
授权:Apache License 2.0(开源免费、可商用)
版本说明(重要):Jaeger v2 于 2024-11-12 发布,是基于 OpenTelemetry Collector 框架重构的新架构;Jaeger v1 已于 2025-12-31 EOL,不再更新(v1 的 jaeger-collector / jaeger-query / jaeger-agent / jaeger-ingester 镜像均已标记 Deprecated,最后版本 1.76)。v2 用单二进制 jaeger 通过不同角色运行。本文一律写「以官方最新稳定版为准」,不锁定 tag 与版本号。
1. 它解决什么问题
| 痛点 |
做法 |
| 一次请求跨十几个服务,慢在哪一步说不清 |
Trace/Span 瀑布图 + 时间线,按耗时、错误、标签检索 |
| 各语言自研埋点、格式互不兼容 |
统一 OpenTelemetry SDK / OTLP,vendor-neutral,兼容 Zipkin、Jaeger Thrift 旧格式 |
| 全量采集压力与成本太高 |
头部采样(概率/限速/远程)、尾部采样(按错误/延迟/标签)、自适应采样 |
| 采集与查询要分开扩缩容 / 存储选型受限 / 链路与指标割裂 |
v2 单二进制多角色 collector/query/ingester/all-in-one;Memory/Badger/Cassandra/ES/OpenSearch/Kafka 缓冲/Remote gRPC/ClickHouse(实验);SPM 把 Trace 转 RED 指标配 Prometheus/Grafana |
不适合:需要开箱即用的 Metrics/Log/告警一体化平台(用 SkyWalking 或 Prometheus 全家桶);只做主机/容器指标(用 Prometheus 系);需要商业支持与托管服务。Jaeger 自身不存储指标、不做告警,见第 6 章。
2. 核心概念与架构
| 术语 |
含义 |
| Trace / Span / SpanContext |
一次请求 = 一条 Trace(TraceID),由多个 Span 组成(operationName、起止时间、tags/logs、父子引用);Context 随跨进程调用透传 TraceID/SpanID/采样标记/baggage,标准为 W3C traceparent,兼容 B3、Jaeger uber-trace-id |
| OTLP / Collector pipeline |
v2 主推上报协议:gRPC :4317、HTTP :4318(/v1/traces,Protobuf/JSON),只收 tracing;配置风格 receivers → processors → exporters,由 service.pipelines.traces 串起,另有 connectors/extensions |
| Jaeger 组件 / 角色 |
扩展 jaeger_storage(注册后端)、jaeger_query(UI/查询 API)、remote_sampling;exporter jaeger_storage_exporter;processor adaptive_sampling。角色:all-in-one/collector/query/ingester/agent(agent 官方建议直接用标准 OTel Collector) |
| 采样 / Storage |
头部 probabilistic(概率)、ratelimiting(限速)、远程静态策略/自适应;尾部 tail_sampling(Trace 收齐后按错误/延迟判定)。主存储短 TTL + archive 长保留;Remote Storage API v2 接自定义 gRPC 后端 |
应用(OTel SDK) OTLP ┌── Jaeger v2(OTel Collector 内核)──┐ 存储
┌─────────────┐ 4317 gRPC / │ receivers: otlp / jaeger / zipkin │ ┌──────────────┐
│ Java/Go/ │───4318 HTTP───►│ processors: batch/tail_sampling │ │ memory/badger│
│ Python/Node │ /v1/traces │ exporters: jaeger_storage_exporter│───►│ Cassandra │
└──────┬──────┘ │ extensions: jaeger_storage/query │ │ ES/OpenSearch│
│ 采样拉取 5778/sampling │ remote_sampling │ │ Kafka(缓冲) │
▼ └───────────────┬──────────────────┘ └──────┬───────┘
┌─────────────┐ ▼ 读(:16685 gRPC/:16686 HTTP) │
│ 静态/自适应 │ ┌──────────────────────┐ ┌──────────────────────┐
│ 采样策略 │◄───────────│ Jaeger UI :16686 │ │ SPM → Prometheus → │
└─────────────┘ │ /api/*、/api/v3/* │ │ Grafana/Alertmanager│
└──────────────────────┘ └──────────────────────┘
Context 传播要点:SDK 在入口从 traceparent 提取父 SpanContext,出口注入 header;任一跳未透传(网关、MQ、线程池切换)就会断链。OTLP 是 v2 的原生入口,旧 Jaeger Thrift / Zipkin 接收器仅为向后兼容(标记 Deprecated)。jaeger_storage 之所以是扩展而非 exporter,是因为 query 端也要读同一份后端配置。
Collector pipeline 与采样
| 环节 |
可选组件 |
说明 |
| receivers |
otlp(4317/4318)、jaeger(14250/14268/6831/6832)、zipkin(9411)、kafka |
多协议并存,生产建议统一收敛到 otlp |
| processors |
batch、probabilistic_sampler、tail_sampling、adaptive_sampling、attributes/resource |
batch 必配;尾部采样需同组 Collector 保证同一 Trace 落同一实例 |
| exporters / extensions |
jaeger_storage_exporter/kafka/otlp;jaeger_storage/jaeger_query/remote_sampling/health_check |
写存储 / Kafka 削峰 / 转发;扩展经 service.extensions 启用 |
| 采样策略 |
客户端 OTEL_TRACES_SAMPLER、服务端静态 sampling-strategies.json、自适应、尾部 |
客户端与服务端采样率不一致会产生「半条链路」,见第 11 章 |
后端存储(官方 Storage 文档)
| 后端 |
适用 |
注意 |
| Memory / Badger |
开发测试 / 单机中小规模 |
Memory 重启即丢(max_traces 限条数)⚠️ 禁生产;Badger 本地嵌入式不能水平扩展 |
| Cassandra / Elasticsearch / OpenSearch |
分布式生产 |
官方对大规模生产推荐 OpenSearch;ES/OpenSearch 索引模板与版本需配套 |
| Kafka |
中间缓冲(非最终存储) |
collector 用 kafka exporter、ingester 用 kafka receiver,防存储抖动丢数据 |
| ClickHouse / Remote Storage gRPC API v2 |
实验 / 自定义后端 |
ClickHouse 需 feature gate;自定义后端实现 TraceReader/DependencyReader/TraceService |
3. 安装与部署
3.1 环境准备
| 项 |
要求 |
| 运行时 / 依赖 |
v2 为 Go 单二进制,无需 JVM;all-in-one + memory 零外部依赖;生产需外部存储(ES/OpenSearch/Cassandra/Badger 卷) |
| 写端口 |
:4317 OTLP gRPC、:4318 OTLP HTTP /v1/traces、:9411 Zipkin、:14250 legacy gRPC、:14268 legacy Thrift、:6831/6832 UDP Thrift |
| 读 / 运维端口 |
:16686 UI + /api/* + /api/v3/*、:16685 gRPC QueryService、:5778/5779 远程采样、:13133 health_check、:8888 自监控(后两者以实际启用扩展/配置为准) |
| 内核 / 权限 / 时钟 |
Linux 常规权限、无需 privileged;UDP 6831/6832 需 host 网络或显式映射;容器挂只读配置与 Badger 数据卷;全节点 NTP,max_clock_skew_adjust 默认 30s |
3.2 多种安装方式
# 版本、镜像 tag 一律以官方最新稳定版为准
export JAEGER_VER=<以官方最新稳定版为准>
# A. 二进制(GitHub Releases 提供 Linux/macOS/Windows 资产,见第 14 章)
curl -fsSL -o jaeger.tar.gz <官方 Releases 资产 URL>
tar -xzf jaeger.tar.gz && ./jaeger --help
# B. Docker all-in-one(官方 Getting Started,内存存储,仅调试)
docker run -d --name jaeger -p 16686:16686 -p 4317:4317 -p 4318:4318 -p 5778:5778 -p 9411:9411 \
cr.jaegertracing.io/jaegertracing/jaeger:${JAEGER_VER}
# C. Docker + 自定义配置
docker run -d --name jaeger -p 16686:16686 -p 4317:4317 -p 4318:4318 \
-v /path/to/config.yaml:/jaeger/config.yaml \
cr.jaegertracing.io/jaegertracing/jaeger:${JAEGER_VER} --config /jaeger/config.yaml
# D. 二进制 + systemd(生产托管)
cat >/etc/systemd/system/jaeger.service <<'EOF'
[Service]
Type=simple
User=jaeger
ExecStart=/opt/jaeger/jaeger --config /etc/jaeger/config.yaml
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload && systemctl enable --now jaeger
# E. HotROD 演示应用
docker run -d --name hotrod -p 8080:8080 -e OTEL_EXPORTER_OTLP_ENDPOINT=http://<JAEGER_HOST>:4318 \
cr.jaegertracing.io/jaegertracing/example-hotrod:${JAEGER_VER}
Docker Compose(all-in-one + OpenSearch 简化示例)
services:
jaeger:
image: cr.jaegertracing.io/jaegertracing/jaeger:${JAEGER_VER}
command: ["--config", "/jaeger/config.yaml"]
volumes: ["./config.yaml:/jaeger/config.yaml:ro"]
ports: ["16686:16686", "4317:4317", "4318:4318", "5778:5778"]
depends_on: [opensearch]
opensearch:
image: opensearchproject/opensearch:<以官方兼容版本为准>
environment: { discovery.type: single-node, OPENSEARCH_JAVA_OPTS: "-Xms1g -Xmx1g" }
ports: ["9200:9200"]
⚠️ K8s 场景:v2 是单二进制,直接用 Deployment + ConfigMap 挂载 config.yaml 即可(collector / query 拆成两个 Deployment)。Jaeger Operator 历史上管理 v1 CRD,v1 已 EOL,Operator 对 v2 的支持情况以官方文档为准;不要照搬旧博客里的 jaeger-operator + v1 Jaeger CR 清单。镜像仓库注意已从 Docker Hub 迁到 cr.jaegertracing.io/jaegertracing/。
3.3 首次启动与验证
curl -sS -o /dev/null -w 'UI HTTP %{http_code}\n' http://localhost:16686/ # 预期 200
curl -sS http://localhost:16686/api/services # 预期 {"data":[],"total":0,...,"errors":null}
curl -sS -X POST http://localhost:4318/v1/traces -H 'Content-Type: application/json' -d @trace.json # 预期 200
curl -sS http://localhost:13133/ # health_check(若启用)预期 200;docker logs jaeger 看启动日志
# 打开 http://localhost:16686 → Search → 选 Service → Find Traces,看到刚上报的 span
4. 配置文件详解
4.1 配置路径与覆盖机制
| 项 |
说明 |
| 指定配置 / 内置配置 |
jaeger --config config.yaml(v2 只认配置文件,不识别 v1 的 SPAN_STORAGE_TYPE 等变量);all-in-one 用二进制内置 all-in-one.yaml,官方 cmd/jaeger/ 下有 config-{storage}.yaml、config-query.yaml 示例 |
| 环境变量展开 / 命令行覆盖 |
YAML 内用 ${env:VAR:-default}(如 endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:4317"),OTel Go SDK 变量自动识别;--set=receivers.otlp.protocols.grpc.endpoint=0.0.0.0:4317 覆盖内置配置 |
| 自定义发行版 |
用 OpenTelemetry Collector Builder(ocb)增删组件后自行编译 |
4.2 关键项说明表
| 配置键 |
默认值 / 示例 |
说明 |
receivers.otlp.protocols.grpc/http.endpoint |
localhost:4317 / localhost:4318 |
OTLP gRPC / HTTP /v1/traces;容器需 0.0.0.0 |
processors.batch.timeout / send_batch_size |
5s / 512 |
批量发送,降低写放大 |
exporters.jaeger_storage_exporter.trace_storage / queue.num_consumers / queue_size |
<backend 名> / 10 / 100 |
指向 jaeger_storage.backends 注册名;发送并发与队列 |
extensions.jaeger_storage.backends.<name>.<type> |
memory.max_traces: 100000 |
类型:memory/badger/cassandra/elasticsearch/opensearch/grpc;字段以官方 config-{storage}.yaml 为准 |
extensions.jaeger_query.storage.traces / http.endpoint / grpc.endpoint |
<backend 名> / 0.0.0.0:16686 / 0.0.0.0:16685 |
query 读同一后端名;UI/REST 与 gRPC QueryService |
extensions.jaeger_query.base_path / max_clock_skew_adjust / remote_sampling.file.path / adaptive.sampling_store |
/ / 30s / 静态策略文件 / 后端名 |
反代前缀、时钟偏差校正(0s 关闭)、远程采样(http/grpc 默认 5778/5779) |
service.extensions / service.pipelines.traces / service.telemetry |
[jaeger_storage, jaeger_query] / [otlp]→[batch]→[jaeger_storage_exporter] / :8888、info |
必须显式列出,否则扩展不生效;Collector 自身指标与日志 |
4.3 完整可运行最小示例:config.yaml(all-in-one + memory)
# config.yaml —— OTLP 接收 + 内存存储 + UI + 远程采样
receivers:
otlp:
protocols:
grpc: { endpoint: 0.0.0.0:4317 }
http: { endpoint: 0.0.0.0:4318 }
processors:
batch: { timeout: 5s, send_batch_size: 512 }
exporters:
jaeger_storage_exporter:
trace_storage: some_trace_storage
queue: { num_consumers: 10, queue_size: 100 }
extensions:
jaeger_storage:
backends: { some_trace_storage: { memory: { max_traces: 100000 } } }
jaeger_query:
storage: { traces: some_trace_storage }
http: { endpoint: 0.0.0.0:16686 }
grpc: { endpoint: 0.0.0.0:16685 }
remote_sampling:
file: { path: /etc/jaeger/sampling-strategies.json }
http: {}
grpc: {}
service:
extensions: [jaeger_storage, jaeger_query, remote_sampling]
pipelines:
traces: { receivers: [otlp], processors: [batch], exporters: [jaeger_storage_exporter] }
telemetry:
logs: { level: info }
生产把 memory 换成 opensearch/elasticsearch/cassandra(字段以官方 config-{storage}.yaml 为准),collector 与 query 拆两个 Deployment 共用同一后端配置。示例见 https://github.com/jaegertracing/jaeger/tree/main/cmd/jaeger 。
4.4 采样配置与存储后端示例
# (a) 尾部采样:保留错误/慢调用,其余 10% 概率(processors 片段,合并进主配置)
processors:
tail_sampling:
decision_wait: 10s
num_traces: 100
policies:
- { name: errors, type: status_code, status_code: { status_codes: [ERROR] } }
- { name: slow, type: latency, latency: { threshold_ms: 500 } }
- { name: baseline, type: probabilistic, probabilistic: { sampling_percentage: 10 } }
batch: { timeout: 5s }
# service.pipelines.traces.processors: [tail_sampling, batch]
# (b) 自适应采样(extensions 片段)
extensions:
remote_sampling:
adaptive: { sampling_store: some_trace_storage, initial_sampling_probability: 0.1 }
# (c) 归档存储(extensions 片段):主存储短 TTL,archive 手动保存长保留
extensions:
jaeger_storage:
backends: { primary_store: { memory: { max_traces: 100000 } }, archive_store: { memory: { max_traces: 100000 } } }
jaeger_query:
storage: { traces: primary_store, traces_archive: archive_store }
// (d) 静态远程采样策略 sampling-strategies.json
{ "default_strategy": { "type": "probabilistic", "param": 0.1 }, "service_strategies": [ { "service": "checkout", "type": "ratelimiting", "param": 100 } ] }
probabilistic/ratelimiting 字段以官方 Sampling 文档为准;客户端 OTEL_TRACES_SAMPLER 与服务端策略二选一,避免双重采样。
5. 核心使用方法
5.1 启动与查询命令集(编号 120 + 5.2 UI 2123,共 ≥23 条)
# --- 服务管理 ---
# 1) 二进制前台启动 2) 查看帮助
jaeger --config /etc/jaeger/config.yaml
jaeger --help
# 3) 覆盖内置 all-in-one 的监听地址
jaeger --set=receivers.otlp.protocols.grpc.endpoint=0.0.0.0:4317
# 4) Docker 挂载自定义配置(all-in-one 见 3.2 B)
docker run -d -v $PWD/config.yaml:/jaeger/config.yaml \
cr.jaegertracing.io/jaegertracing/jaeger:${JAEGER_VER} --config /jaeger/config.yaml
# --- 查询 REST API(:16686,/api/* 为 internal;程序化推荐 :16685 gRPC api_v3)---
# 5) 服务列表 6) 操作名
curl -sS http://<JAEGER>:16686/api/services
curl -sS 'http://<JAEGER>:16686/api/operations?service=checkout'
# 7) 检索 Trace(微秒级 start/end、limit、minDuration、tags)
curl -sS 'http://<JAEGER>:16686/api/traces?service=checkout&start=1730000000000000&end=1730003600000000&limit=20&minDuration=500ms&tags={"http.status_code":"500"}'
# 8) 取单条 Trace 9) 服务依赖图(endTs/lookback 毫秒)
curl -sS http://<JAEGER>:16686/api/traces/<trace-id-hex-string>
curl -sS 'http://<JAEGER>:16686/api/dependencies?endTs=1730003600000&lookback=3600000'
# 10) 远程采样策略 11) 稳定 JSON API(路径以官方 OpenAPI 为准)
curl -sS 'http://<JAEGER>:5778/sampling?service=checkout'
curl -sS 'http://<JAEGER>:16686/api/v3/traces?query.service_name=checkout'
# 12) UI / 健康检查
curl -sS -o /dev/null -w '%{http_code}\n' http://<JAEGER>:16686/ && curl -sS http://<JAEGER>:13133/
# 13) gRPC 反射列出查询服务
grpc_cli ls <JAEGER>:16685
# --- OTLP 直投(调试用)---
# 14) HTTP JSON 上报一条 span(traceId 32 位 hex、spanId 16 位 hex、时间 Unix 纳秒字符串)
curl -sS -X POST http://<JAEGER>:4318/v1/traces -H 'Content-Type: application/json' \
-d '{"resourceSpans":[{"scopeSpans":[{"spans":[{"traceId":"5B8EFFF798038103D269B633813FC60C","spanId":"EEE19B7EC3C1B174","name":"GET /checkout","kind":2,"startTimeUnixNano":"1730000000000000000","endTimeUnixNano":"1730000000120000000","status":{"code":2}}]}]}]}'
# --- 客户端 OTel SDK 接入(无需 Jaeger 专用 client)---
# 15) 通用环境变量(SDK 自动配置)
export OTEL_SERVICE_NAME=checkout OTEL_EXPORTER_OTLP_ENDPOINT=http://<JAEGER>:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf OTEL_PROPAGATORS=tracecontext,baggage
export OTEL_TRACES_SAMPLER=parentbased_traceidratio OTEL_TRACES_SAMPLER_ARG=0.1
# 16) Java:OTel Java Agent(版本以 OpenTelemetry 官方最新稳定版为准)
java -javaagent:/opt/opentelemetry-javaagent.jar -Dotel.service.name=checkout \
-Dotel.exporter.otlp.endpoint=http://<JAEGER>:4318 -jar checkout.jar
# 17) Python / 18) Node.js 自动插桩
pip install opentelemetry-distro opentelemetry-exporter-otlp && opentelemetry-bootstrap -a install
opentelemetry-instrument python app.py
node --require @opentelemetry/auto-instrumentations-node/register app.js
# 19) Go:标准 OTel SDK 读 15) 环境变量(代码见第 7 章)
# 20) 采样策略校验 / 自监控 / 索引清理 / HotROD 演示
cat /etc/jaeger/sampling-strategies.json | jq .
curl -sS http://<JAEGER>:8888/metrics | grep otelcol_
docker run --rm cr.jaegertracing.io/jaegertracing/jaeger-es-index-cleaner:${JAEGER_VER} <ES_URL>
docker run --rm -p 8080:8080 cr.jaegertracing.io/jaegertracing/example-hotrod:${JAEGER_VER}
5.2 Web UI 路径(21~24)与关键 API
| # |
目标 |
路径或端点 |
| 21 |
检索链路 |
Search → Service/Operation/Tags/Min-Max Duration/Lookback → Find Traces |
| 22 |
看瀑布图 / 对比 Trace |
点 Trace → 时间线、Span tags/logs;Trace Comparison 并排视图 |
| 23 |
依赖图 / SPM |
Dependencies(DAG)、Monitor 页(需配 SPM + Prometheus) |
| 端点 |
端口 |
说明 |
POST /v1/traces |
4318 |
OTLP HTTP(Protobuf/JSON),主推写入口 |
GET /api/traces?service=&minDuration=&tags= / /api/traces/{traceID} |
16686 |
internal JSON,UI 自用,不保证兼容 |
GET /api/services / /api/operations / /api/dependencies |
16686 |
元数据与依赖图 |
GET /api/v3/*(稳定)/ gRPC jaeger.api_v3.QueryService |
16686 / 16685 |
程序化查询推荐 |
GET /sampling?service= |
5778 |
远程采样策略 |
6. 告警与通知
Jaeger 自身没有告警引擎。生产做法:Collector service.telemetry.metrics(或 SPM 的 spanmetrics connector)→ Prometheus 抓取 → Alertmanager 通知;RED 指标走 SPM + Grafana。以下规则基于 OTel Collector 内部指标(真实存在:otelcol_receiver_accepted_spans、otelcol_exporter_send_failed_spans、otelcol_processor_dropped_spans),SPM 指标名以实际 /metrics 暴露为准。
# prometheus/rules.yml —— 管线健康告警(OTel Collector 内部指标,真实存在)
groups:
- name: jaeger-pipeline
rules:
- { alert: JaegerReceiverNoSpans, expr: 'sum(rate(otelcol_receiver_accepted_spans{receiver="otlp"}[5m])) == 0', for: 10m, labels: {severity: warning} }
- { alert: JaegerExportFailures, expr: 'sum(rate(otelcol_exporter_send_failed_spans[5m])) > 0', for: 5m, labels: {severity: critical} }
- { alert: JaegerSpanDropped, expr: 'sum(rate(otelcol_processor_dropped_spans[5m])) > 0', for: 5m, labels: {severity: warning} }
# alertmanager.yml —— 多通道通知 + 噪声抑制
route:
group_by: [alertname, service]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: webhook-default
routes:
- { matchers: ['severity="critical"'], receiver: wechat-p0 }
receivers:
- name: webhook-default
webhook_configs:
- url: http://<告警网关_HOST>:<PORT>/jaeger/alarm # 网关转发钉钉/飞书/邮件
- name: wechat-p0
webhook_configs:
- url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=<企业微信机器人key>
- name: email-p2
email_configs:
- { to: <值班邮箱>, from: <发件邮箱>, smarthost: '<SMTP_HOST>:587', auth_username: <账号>, auth_password: <密码> }
inhibit_rules: # P0 触发时压掉同源 P2
- { source_matchers: ['severity="critical"'], target_matchers: ['severity="warning"'], equal: [alertname, service] }
# 钉钉由告警网关按 Alertmanager JSON 渲染后转发
curl -sS -X POST 'https://<钉钉机器人Webhook URL>' -H 'Content-Type: application/json' -d '{"msgtype":"text","text":{"content":"Jaeger 告警:<alerts>"}}'
降噪:for 持续时长、group_by 聚合、repeat_interval 重发间隔、inhibit_rules 抑制、按 service/severity 分级路由;SPM 的 RED 告警(错误率、P99)另配 recording rule,指标名以 spanmetrics connector 的 namespace 为准。
7. 实战场景
7.1 接入一个 Java 服务(OTel Agent)
- 目标/步骤:不改业务代码让
checkout 出 Trace → 起 all-in-one → 挂 OTel Java Agent → 造流量。
docker run -d --name jaeger -p 16686:16686 -p 4317:4317 -p 4318:4318 cr.jaegertracing.io/jaegertracing/jaeger:${JAEGER_VER}
export OTEL_SERVICE_NAME=checkout OTEL_EXPORTER_OTLP_ENDPOINT=http://<JAEGER_HOST>:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf OTEL_PROPAGATORS=tracecontext,baggage
java -javaagent:/opt/opentelemetry-javaagent.jar -jar /app/checkout.jar
curl -sS http://localhost:8080/api/checkout/1
- 验证:
/api/services 出现 checkout;UI Search 有瀑布图。回滚:去掉 -javaagent 与 OTEL_* 重启应用;⚠️ 容器级全局环境变量回滚时一并清理。
7.2 定位一次慢调用
- 目标/步骤:下单偶发 >800ms → 按耗时检索 → 看瀑布图 → 比对正常/异常 Trace → 加 SPM 告警。
curl -sS 'http://<JAEGER>:16686/api/traces?service=checkout&limit=50&minDuration=800ms&lookback=1h'
curl -sS http://<JAEGER>:16686/api/traces/<trace-id> | jq '.data[0].spans[] | {operationName,duration,tags}'
curl -sS 'http://<JAEGER>:16686/api/dependencies?endTs=<毫秒时间戳>&lookback=3600000'
# UI:Search → Min Duration=800ms → 点 Trace 找最长 Span(DB/RPC);补 SPM P99 告警(第 6 章)
- 验证:单个 Span 占总耗时 >70% 且 DB/RPC tag 明确、依赖图边吻合。回滚:删除
rules.yml 对应 group 并热重载 Prometheus;⚠️ 勿改 Jaeger 采集配置。
7.3 接入一个 Go 服务(OTel SDK)
- 目标/步骤:用标准 OTel SDK 上报 OTLP,不依赖 Jaeger 私有客户端;引入 otel + otlptracehttp,环境变量初始化,出口注入 traceparent。
// main.go 片段,依赖版本以 OpenTelemetry 官方最新稳定版为准
package main
import (
"context"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/trace"
)
func main() {
exp, _ := otlptracehttp.New(context.Background()) // 读 OTEL_EXPORTER_OTLP_ENDPOINT 等环境变量
tp := trace.NewTracerProvider(trace.WithBatcher(exp))
otel.SetTracerProvider(tp)
defer tp.Shutdown(context.Background())
_, span := otel.Tracer("order-go").Start(context.Background(), "POST /orders")
defer span.End() // 出口用 otelhttp/httptrace 注入 traceparent,保证跨服务不断链
}
export OTEL_SERVICE_NAME=order-go OTEL_EXPORTER_OTLP_ENDPOINT=http://<JAEGER_HOST>:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf OTEL_PROPAGATORS=tracecontext,baggage
go mod tidy && go run .
- 验证:
/api/services 出现 order-go,依赖图出现与 checkout 的调用边。回滚:去掉 OTel 初始化或清空 OTEL_* 重启;⚠️ 同集群其他 SDK 依赖这些变量时注意隔离。
7.4 Tail Sampling 保留错误与慢链路
- 目标/步骤:流量大只留有价值 Trace →
config.yaml 加 tail_sampling(4.4)→ 客户端 OTEL_TRACES_SAMPLER=always_on → 重启 Collector。
- 验证:
:8888/metrics 的 otelcol_processor_dropped_spans 上升,仍能查到 ERROR 与 >500ms Trace。回滚:processors 改回 [batch] 重启;⚠️ 多副本尾部采样须按 TraceID 一致性路由,否则决策不准。
8. 日常运维与故障排查
systemctl status jaeger && journalctl -u jaeger -f # 服务管理 / 二进制日志
docker logs -f jaeger 2>&1 | grep -iE 'error|dropped|ready' # 容器日志
curl -sS http://<JAEGER>:13133/ # health_check 扩展
curl -sS http://<JAEGER>:8888/metrics | grep otelcol_ # 自监控指标
curl -sS http://<JAEGER>:16686/api/services # 查询面可用性
| 现象 |
原因 |
处置 |
| 应用上报后 UI 查不到 |
OTLP endpoint 写成 localhost / 端口未映射 / 服务名过滤错 |
容器内用 0.0.0.0 监听、应用指向可达地址;确认 4317/4318 已 -p 映射;curl 直投验证 |
connection refused :4317 |
collector 未起或只监听 localhost |
检查 receivers.otlp.protocols.*.endpoint;用 --set 覆盖为 0.0.0.0 |
| 配置了扩展但不生效 |
service.extensions 未列出 / pipeline 未引用 exporter |
补 extensions: [jaeger_storage, jaeger_query];确认 service.pipelines.traces.exporters |
查询报 storage not found |
jaeger_query.storage.traces 与 jaeger_storage.backends 名字不一致 |
两处用同一 backend 名 |
| 重启后 Trace 全丢 / 高压下丢 span |
memory 临时存储 / 队列满、存储写慢 |
换持久化后端;调 queue_size、num_consumers、batch;接 Kafka 缓冲;⚠️ memory 禁生产 |
| 链路断裂只剩半条 / 时间线错乱 |
双重采样、Context 未透传 / 时钟漂移 |
采样二选一、透传 traceparent;全节点 NTP、调 max_clock_skew_adjust |
| ES 索引暴涨 |
未配 TTL / 索引清理器 |
用 jaeger-es-index-cleaner 或 ILM 定期清旧索引 |
9. 性能、容量与高可用
| 维度 |
建议 |
| 探针开销 / 采样率 / 容量估算 |
OTel SDK 开销随 Span/属性数增长,控制 attributes 并压测校准;低流量全采、高流量头部 1%~10% + 尾部保错误/慢调用。日容量 ≈ 日均 Span 数 × 单 Span 压缩后字节 × 副本数 × 保留天数 ÷ 采样率,先用 7 天试运行实测日增磁盘再外推 |
| Collector 集群 / 读写分离 / 缓冲 |
collector 无状态可水平扩 + 前置 LB;拆 collector 与 query 两个 Deployment;queue 平滑短时峰值,持续峰值用 Kafka 中间缓冲(collector→kafka exporter、ingester→kafka receiver);尾部采样多副本需按 TraceID 一致性路由 |
| 自监控 / 存储 HA |
service.telemetry.metrics → :8888/metrics 抓 otelcol_receiver_accepted_spans/_send_failed_spans/_dropped_spans 与队列长度(第 6 章告警);大规模官方推荐 OpenSearch 多节点 + 副本,定期索引清理 |
10. 安全加固、备份与升级
| 项 |
做法 |
| 认证鉴权 / TLS / 暴露面 |
UI/API 默认无认证,置于 Ingress/反代后加 SSO/Basic Auth/ACL,或按 OTel Collector 配 TLS/auth 扩展(字段以所用版本为准);对外 HTTPS、内网 OTLP 可 mTLS。只对外暴露 UI :16686,其余端口与 :9200 仅内网可达(NetworkPolicy/安全组),⚠️ 勿把 /api/* 暴露公网 |
| 最小权限 / 敏感数据 |
非 root 运行、配置只读挂载、ES 账号只授所需索引;Span tag 可能含用户信息,用 attributes/redaction 处理器脱敏,控制 baggage 传播内容 |
| 数据保留与备份 / 版本兼容 |
主存储短 TTL、archive 手动归档,ES/OpenSearch 用 ILM/快照并定期演练恢复,配置与规则入 Git;v1 → v2 是架构级迁移,v2 不识别 SPAN_STORAGE_TYPE/COLLECTOR_*,须改写为 OTel Collector YAML,客户端统一用 OTel SDK/OTLP |
| 升级与回滚 |
备份 → 预发验证 YAML 与 schema → 滚动升 collector → 升 query → 验证查询;保留上一版二进制/镜像 tag。⚠️ 存储 schema(尤其 ES 索引)通常不可自动降级,回滚前先做快照 |
11. 常见坑
| # |
现象 |
原因 |
解决 |
| 1 |
⚠️ 照旧博客用 jaegertracing/all-in-one / jaeger-collector 镜像拉不到或无更新 |
v1 已 2025-12-31 EOL,镜像迁到 cr.jaegertracing.io/ |
改用 cr.jaegertracing.io/jaegertracing/jaeger:<版本> 单二进制 |
| 2 |
⚠️ 用 SPAN_STORAGE_TYPE=elasticsearch 等变量配 v2 完全无效 |
v2 只认 --config 的 OTel Collector YAML |
改 YAML;需要变量用 ${env:VAR:-default} 展开 |
| 3 |
应用上报 200 但 UI 无数据 / 启动报 extension not found |
只监听 localhost、端口未映射 / service.extensions 漏列 |
endpoint 改 0.0.0.0、检查 -p;显式列出扩展并让 pipeline 引用同名 backend |
| 4 |
⚠️ 生产用 memory,重启 Trace 全丢 / ES 磁盘打满 |
memory 临时存储 / 无 TTL 与索引清理 |
换 OpenSearch/ES/Cassandra/Badger;配 ILM 与 jaeger-es-index-cleaner |
| 5 |
链路只剩半条 / 时间线错乱 |
双重采样、Context 未透传 / 时钟漂移 |
采样二选一、透传 traceparent;全节点 NTP、调 max_clock_skew_adjust |
| 6 |
程序化调用 /api/traces 升级后字段变了 |
/api/* 是 internal、非稳定契约 |
改用 :16685 gRPC api_v3 或 /api/v3/* |
| 7 |
⚠️ 把 :16686 或 :9200 暴露公网 / 配了 tail_sampling 仍丢错误 Trace |
默认无认证可被拖库 / decision_wait 过短或策略写错 |
内网隔离 + 代理鉴权;增大 decision_wait、num_traces,核对 policies |
| 8 |
v1 Operator 的 Jaeger CR 用不了 |
Operator 面向 v1 架构 |
v2 用 Deployment + ConfigMap;Operator 支持情况以官方文档为准 |
12. 速查表
| 项目 |
值 / 命令 |
| 分类 / 授权 |
05 APM与链路追踪 / Apache License 2.0 |
| 官网 / 仓库 |
https://www.jaegertracing.io/ | https://github.com/jaegertracing/jaeger |
| 镜像仓库 |
cr.jaegertracing.io/jaegertracing/jaeger:<版本>(v1 镜像已废弃) |
| 启动 |
jaeger --config config.yaml / jaeger --set=<key>=<value> |
| OTLP 写入 |
:4317 gRPC、:4318 HTTP POST /v1/traces |
| UI / 查询 |
:16686、/api/services、/api/traces、/api/dependencies |
| 稳定查询 |
:16685 gRPC api_v3 / :16686/api/v3/* |
| 远程采样 |
:5778 GET /sampling?service=、:5779 gRPC |
| 兼容写端口 |
:9411 Zipkin、:14250 legacy gRPC、:14268 legacy Thrift、:6831/6832 UDP |
| 健康检查 / 自监控 |
:13133(health_check)、:8888/metrics(otelcol_*) |
| 主配置 / 关键组件 |
--config config.yaml(示例 cmd/jaeger/config-{storage}.yaml);jaeger_storage/jaeger_query/remote_sampling/jaeger_storage_exporter/batch/tail_sampling |
| 存储后端 |
memory / badger / cassandra / elasticsearch / opensearch / kafka / grpc(remote) / clickhouse(实验) |
| 客户端接入 |
OTEL_EXPORTER_OTLP_ENDPOINT/OTEL_SERVICE_NAME/OTEL_TRACES_SAMPLER/OTEL_PROPAGATORS |
| 采样策略文件 |
/etc/jaeger/sampling-strategies.json(default_strategy + service_strategies) |
| 日志 / 服务管理 |
journalctl -u jaeger -f、docker logs jaeger、systemctl status jaeger |
| 演示应用 / 要点 |
example-hotrod:<版本> UI http://localhost:8080;v1 EOL(2025-12-31)、v2 基于 OTel Collector、无内置告警 |
13. 选型建议与同类对比
| 维度 |
SkyWalking |
Jaeger |
Zipkin |
Pinpoint |
| 定位 |
一体化 APM(Trace+Metrics+Log+Profile+Alarm) |
专注链路追踪(v2 基于 OTel Collector) |
轻量链路追踪 |
一体化 APM(偏 Java) |
| 埋点 / 数据模型 / 存储 |
无侵入 Agent + eBPF + OTLP / 自有 + Zipkin、OTLP / BanyanDB/ES/OpenSearch/MySQL/PG |
标准 OTel SDK/OTLP / OTLP 原生 + 兼容 Thrift、Zipkin / Memory/Badger/Cassandra/ES/OpenSearch/Kafka/Remote |
引 Brave 库 / Zipkin v2、B3 / ES/MySQL/Cassandra/内存 |
Java 字节码增强 / 自有 / HBase 为主 |
| 指标告警 / 运维 / 治理 |
内置 MAL/MQE + 告警引擎 / 中高(OAP 集群)/ ASF 顶级、国产主导 |
无内置告警,需 Prometheus+SPM / 中(单二进制、无 JVM)/ CNCF 毕业、OTel 最贴合 |
无 / 低 / 老牌轻量 |
内置有限 / 高(HBase)/ 韩国起源、Java 强 |
何时选 Jaeger:已标准化 OpenTelemetry、只要链路追踪与根因定位、希望后端可插拔且读写分离、不想引入 APM 全家桶;CNCF/OTel 生态优先。
何时不选:需要 Trace/Metrics/Log/告警一体(选 SkyWalking);需要无侵入多语言探针(选 SkyWalking/Pinpoint);小团队只要最快上手且数据量小(Zipkin 更轻);需要商业托管支持。
与 SkyWalking 组合:应用统一 OTel SDK 上报,OTLP 同时送 Jaeger(纯 Trace 归档/OTel 原生)与 SkyWalking(APM 与告警),避免双探针。
14. 参考链接