文章目录
- OTel+Jaeger+ES:分布式链路追踪实战部署与优化
-
- 一、核心组件解析:OTel、Jaeger、Elasticsearch
-
- [1.1 OpenTelemetry(OTel):统一的可观测性采集层](#1.1 OpenTelemetry(OTel):统一的可观测性采集层)
- [1.2 Jaeger:链路追踪可视化与分析](#1.2 Jaeger:链路追踪可视化与分析)
- [1.3 Elasticsearch:链路数据的后端存储](#1.3 Elasticsearch:链路数据的后端存储)
- 二、整体方案设计:数据流转与架构逻辑
- 三、全流程实操部署:从环境准备到功能验证
-
- [3.1 环境准备:镜像拉取](#3.1 环境准备:镜像拉取)
- [3.2 部署核心服务:Docker Compose 编排](#3.2 部署核心服务:Docker Compose 编排)
- [3.3 配置文件编写:Jaeger 与 OTel Collector](#3.3 配置文件编写:Jaeger 与 OTel Collector)
-
- [3.3.1 Jaeger 配置文件(conf/jaeger-config.yaml)](#3.3.1 Jaeger 配置文件(conf/jaeger-config.yaml))
- [3.3.2 OTel Collector 配置文件(conf/otelcol-config.yml)](#3.3.2 OTel Collector 配置文件(conf/otelcol-config.yml))
- [3.4 目录权限配置:ES 数据持久化](#3.4 目录权限配置:ES 数据持久化)
- [3.5 服务可用性验证:ES、Jaeger、OTel Collector](#3.5 服务可用性验证:ES、Jaeger、OTel Collector)
-
- [3.5.1 Elasticsearch 可用性验证](#3.5.1 Elasticsearch 可用性验证)
- [3.5.2 Jaeger UI 访问验证](#3.5.2 Jaeger UI 访问验证)
- [3.6 链路数据测试:手动上报与索引验证](#3.6 链路数据测试:手动上报与索引验证)
- [3.7 ES 生命周期管理:数据自动清理](#3.7 ES 生命周期管理:数据自动清理)
- [3.8 业务服务数据上报:接入 OTel Agent](#3.8 业务服务数据上报:接入 OTel Agent)
-
- [3.8.1 手动启动服务](#3.8.1 手动启动服务)
- [3.8.2 Supervisor 启动服务(生产环境常用)](#3.8.2 Supervisor 启动服务(生产环境常用))
- [四、生产环境增强:Jaeger UI 登陆认证](#四、生产环境增强:Jaeger UI 登陆认证)
-
- [4.1 环境准备](#4.1 环境准备)
-
- [4.1.1 Nginx 镜像拉取](#4.1.1 Nginx 镜像拉取)
- [4.2.2 生成密码文件](#4.2.2 生成密码文件)
- [4.2 调整 Docker Compose:新增 Nginx 服务](#4.2 调整 Docker Compose:新增 Nginx 服务)
- [4.3 验证 Nginx 认证](#4.3 验证 Nginx 认证)
- 五、方案总结与优化建议
-
- [5.1 方案总结](#5.1 方案总结)
- [5.2 优化建议](#5.2 优化建议)
OTel+Jaeger+ES:分布式链路追踪实战部署与优化
在微服务架构普及的今天,一次用户请求往往会跨越数十个服务节点,当系统出现延迟、报错等问题时,定位根因变得异常困难。分布式链路追踪作为可观测性三大支柱(日志、指标、链路)之一,能清晰还原请求的完整调用链路,是微服务排障、性能优化的核心工具。本文将从核心组件解析入手,结合全实操流程,讲实战如何基于 OpenTelemetry(简称 OTel)、Jaeger、Elasticsearch 搭建一套高可用的分布式链路追踪方案,并适配生产环境的安全需求。
一、核心组件解析:OTel、Jaeger、Elasticsearch
要搭建这套链路追踪方案,首先需要理解核心组件的定位和价值,这是方案设计的基础。
1.1 OpenTelemetry(OTel):统一的可观测性采集层
在 OTel 出现之前,各厂商的链路采集工具(如 Jaeger Agent、Zipkin Collector)互不兼容,业务接入不同追踪系统需要重复开发适配代码,运维人员也需要维护多套采集体系,成本极高。
OTel 是 CNCF 孵化的开源项目,核心价值是标准化 和统一化,它定义了一套通用的可观测性数据模型(覆盖链路、指标、日志),并提供全生命周期的采集、处理、导出能力,核心组件分为三层:
- 采集层(SDK/Agent):
- SDK:多语言原生开发包(Java/Python/Go 等),支持业务代码侵入式埋点,自定义链路标签、事件;
- Agent:无侵入式采集代理(如 OTel Java Agent),通过字节码增强技术自动采集服务调用、数据库访问、HTTP 请求等链路数据,无需修改业务代码。
- 处理层(Collector):核心转发 / 处理组件,包含 Receiver(接收数据)、Processor(处理数据,如批量、过滤、采样)、Exporter(导出数据)三大模块,支持多协议接入(OTLP/HTTP/gRPC、Jaeger、Zipkin)和多后端导出,是链路数据流转的核心枢纽。
- 协议层(OTLP):OpenTelemetry Protocol 的缩写,是 OTel 定义的标准化数据传输协议,统一了链路 / 指标 / 日志的数据格式,解决了不同组件间的数据兼容问题。
简单来说,OTel 解决了 "采集层碎片化" 的问题,让业务无需关心后端存储 / 分析系统,只需接入 OTel 就能完成链路数据的标准化采集、处理和转发,实现 "一次接入,多端适配"。

1.2 Jaeger:链路追踪可视化与分析
Jaeger 是 CNCF 毕业的开源链路追踪工具,由 Uber 开源并捐赠,核心定位是链路数据的接收、存储、查询和可视化,核心模块包括:
- Jaeger Agent:轻量级网络代理,部署在每台宿主机上,接收本地服务上报的链路数据,批量转发给 Collector(减少网络请求数);
- Jaeger Collector:接收 Agent/OTel Collector 转发的链路数据,完成数据验证、清洗、索引构建后写入存储后端;
- Jaeger Query:查询服务,接收前端 UI 的查询请求,从存储后端检索链路数据并返回;
- Jaeger UI:可视化界面,提供链路检索(按服务名、操作名、时间范围、Trace ID 等)、链路拓扑、耗时分析、异常定位等能力,是排查微服务调用问题的 "可视化窗口";
- Storage:存储层,支持 Cassandra、Elasticsearch、Kafka 等后端,可根据场景灵活选择。
本文选择 Elasticsearch 作为存储后端,兼顾查询性能、数据持久化和索引生命周期管理能力,适配生产环境的存储需求。
1.3 Elasticsearch:链路数据的后端存储
链路追踪数据具有 "写多读少、时序性强、需快速检索、数据量随时间线性增长" 的特点,而 Elasticsearch(ES)作为分布式全文搜索引擎,天生适配这类数据的存储和查询需求,核心优势体现在:
- 高效检索:基于倒排索引结构,支持按服务名、Trace ID、Span ID、时间范围、异常标签等维度快速检索,毫秒级返回结果;
- 时序数据优化:支持时序索引模板,可按时间分片(如按天 / 小时创建索引),降低单索引大小,提升查询效率;
- 索引生命周期管理(ILM):可配置自动策略(如数据保留 7 天、30 天),自动清理过期数据,避免磁盘资源耗尽;
- 分布式扩展:支持集群部署,可通过增加节点横向扩展存储和查询能力,适配业务流量增长;
- 兼容性:Jaeger 提供原生的 ES 存储适配插件,无需二次开发即可完成数据写入 / 读取。
ES 作为 Jaeger 的存储后端,能平衡 "存储成本、查询性能、运维复杂度",是中小规模到大规模微服务链路追踪的首选存储方案。
二、整体方案设计:数据流转与架构逻辑
本方案的核心逻辑是 "标准化采集→统一转发→存储→可视化分析",数据流转流程如下:
- 业务服务通过 OTel Java Agent(无侵入方式)采集链路数据,标准化为 OTLP 协议;
- OTel Collector 接收业务服务上报的 OTLP 数据,批量处理后转发给 Jaeger;
- Jaeger 将链路数据写入 Elasticsearch 进行持久化存储;
- 运维 / 开发人员通过 Jaeger UI 查询链路数据,分析调用问题;
- 生产环境通过 Nginx 代理 Jaeger UI,增加密码认证,保障数据安全。
整个架构基于 Docker Compose 编排,实现服务隔离和一键部署,兼顾易用性和可维护性。
bash
业务服务(OTel Agent) → OTel Collector → Jaeger Collector → Elasticsearch
↓
Jaeger UI ← Jaeger Query ← Elasticsearch
↑
Nginx(认证代理)→ 生产环境访问入口
三、全流程实操部署:从环境准备到功能验证
3.1 环境准备:镜像拉取
要运行 OTel、Jaeger、Elasticsearch 等组件,首先需要获取对应的 Docker 镜像 ------Docker 镜像作为组件的运行载体,能屏蔽不同服务器环境的差异,保证组件运行的一致性。本文选用经过验证的稳定版本:Elasticsearch 8.19.7、Jaeger 2.12.0、OTel Collector 0.142.0,镜像拉取操作如下:
bash
# 拉取es
docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.elastic.co/elasticsearch/elasticsearch:8.19.7
docker tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.elastic.co/elasticsearch/elasticsearch:8.19.7 docker.elastic.co/elasticsearch/elasticsearch:8.19.7
# 拉取jaeger
docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/jaegertracing/jaeger:2.12.0
docker tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/jaegertracing/jaeger:2.12.0 docker.io/jaegertracing/jaeger:2.12.0
# 拉取opentelemetry-collector
docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/otel/opentelemetry-collector-contrib:0.142.0
docker tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/otel/opentelemetry-collector-contrib:0.142.0 docker.io/otel/opentelemetry-collector-contrib:0.142.0
3.2 部署核心服务:Docker Compose 编排
多个组件之间存在依赖关系(如 Jaeger 需等待 Elasticsearch 启动完成),且需要统一的网络隔离和资源限制,Docker Compose 能通过一个配置文件完成多服务的编排,简化部署和维护成本。新建docker-compose.yaml文件,配置如下:
bash
version: '3.3'
# 自定义桥接网络:隔离链路追踪相关服务,避免与宿主机/其他容器网络冲突
networks:
otel-trace-network:
driver: bridge
ipam:
config:
- subnet: 172.29.0.0/16
services:
# Elasticsearch 8.19.7 单节点服务:链路数据存储核心
es-trace:
image: docker.elastic.co/elasticsearch/elasticsearch:8.19.7
container_name: es-trace
# 接入自定义隔离网络
networks:
- otel-trace-network
# 异常退出自动重启,提升服务可用性
restart: always
# 环境变量配置:核心参数,适配单节点部署和Jaeger存储需求
environment:
- cluster.name=otel-trace-cluster
- node.name=es-trace-01
# JVM内存配置:根据服务器资源调整,建议最小1G,最大不超过32G(ES默认堆内存上限)
- "ES_JAVA_OPTS=-Xms2g -Xmx4g"
# 8.x版本单节点必须配置,否则集群无法启动
- discovery.type=single-node
# 锁定内存,避免ES内存被系统SWAP,提升性能
- bootstrap.memory_lock=true
# 测试环境关闭安全层(生产环境建议开启并配置证书)
- xpack.security.enabled=false
- xpack.security.enrollment.enabled=false
# 允许Jaeger自动创建索引(否则链路数据无法写入)
- action.auto_create_index=true
# 自动获取挂载目录权限,避免数据目录权限不足
- TAKE_FILE_OWNERSHIP=true
# 端口映射:宿主机9201→容器9200(HTTP),9301→容器9300(TCP)
ports:
- "9201:9200"
- "9301:9300"
# 数据卷挂载:宿主机目录→容器数据目录,实现ES数据持久化
volumes:
- ./es-trace-data:/usr/share/elasticsearch/data
# 内存锁定配置:禁用SWAP,匹配bootstrap.memory_lock=true
ulimits:
memlock:
soft: -1
hard: -1
# 健康检查:确保ES启动完成且集群状态正常后,再启动依赖服务
healthcheck:
interval: 30s
retries: 20
timeout: 10s
test: >
bash -c 'curl -s http://localhost:9200/_cat/health?v | grep -q "green\|yellow"'
# 资源限制:避免ES占用过多服务器资源,影响其他组件
deploy:
resources:
limits:
cpus: '2'
memory: 4G
# 重点修改:jaeger 服务(改用环境变量配置,去掉无效 YAML 挂载)
jaeger:
image: jaegertracing/jaeger:2.12.0
container_name: jaeger
networks:
- otel-trace-network
ports:
- "16686:16686"
- "14317:4317"
environment:
# 1. 指定存储类型为 Elasticsearch
- SPAN_STORAGE_TYPE=elasticsearch
# 2. ES 容器内通信地址(必须是 es-trace:9200,docker-compose 网桥内互通)
- ES_SERVER_URLS=http://es-trace:9200
# 3. ES 索引前缀(和你的索引名匹配)
- ES_INDEX_PREFIX=jaeger-trace
# 4. 核心:字段映射,解决 traceID 报错(官方环境变量,直接指定 keyword 子字段)
- ES_MAPPINGS_TRACEID=traceID.keyword
- ES_MAPPINGS_SPANID=spanID.keyword
- ES_MAPPINGS_SERVICENAME=process.serviceName.keyword
# 5. 启用 OTLP 协议接收(适配你的 otel-collector 转发数据)
- COLLECTOR_OTLP_ENABLED=true
# 6. 可选:ES 操作超时时间
- ES_TIMEOUT=10s
depends_on:
es-trace:
condition: service_healthy
restart: always
deploy:
resources:
limits:
cpus: '1'
memory: 2G
# OpenTelemetry Collector 0.142.0 服务:统一链路数据采集网关
otel-collector:
image: otel/opentelemetry-collector-contrib:0.142.0
container_name: otel-collector
networks:
- otel-trace-network
# 启动命令:加载自定义配置文件
command:
- --config=/etc/otelcol-config.yml
# 配置文件挂载:宿主机自定义配置→容器配置文件
volumes:
- ./conf/otelcol-config.yml:/etc/otelcol-config.yml
# 端口映射:OTLP gRPC/HTTP接收端口,对接业务服务上报
ports:
- "4317:4317"
- "4318:4318"
# 依赖关系:等待Jaeger启动后启动,避免数据转发失败
depends_on:
jaeger:
condition: service_started
restart: always
# 健康检查:验证Collector是否就绪,可接收/转发数据
# 资源限制
deploy:
resources:
limits:
cpus: '1'
memory: 1G
3.3 配置文件编写:Jaeger 与 OTel Collector
组件的默认配置无法适配实际业务场景(如 Jaeger 需要指定 Elasticsearch 存储地址,OTel Collector 需要指定数据转发目标),通过自定义配置文件可以定制组件行为,让整个链路数据流转符合预期。
3.3.1 Jaeger 配置文件(conf/jaeger-config.yaml)
Jaeger 的核心配置是指定存储后端为 Elasticsearch,确保链路数据能正确写入 ES,配置如下:
yaml
# conf/jaeger-config.yaml - 完全对齐 Jaeger 2.12.0 官方规范
extensions:
# Jaeger Query 扩展(UI 查询+数据读取)
jaeger_query:
storage:
traces: my_es_storage # 关联下面定义的 ES 存储后端
metrics: my_es_storage
base_path: /jaeger/ui # UI 访问路径(默认)
# Jaeger 存储扩展(定义 ES 存储后端)
jaeger_storage:
# 跟踪数据存储后端
backends:
my_es_storage: &es_config # 复用 ES 配置(锚点语法)
elasticsearch:
server_urls:
- http://es-trace:9200 # ES 容器名+端口(关键适配)
indices:
index_prefix: "jaeger-trace" # 索引前缀
# 指标数据存储后端(复用 ES 配置)
metric_backends:
my_es_storage: *es_config # 引用上面的 ES 配置
receivers:
# 接收 OTLP 协议数据(和 Collector 端口不冲突)
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317 # Jaeger 监听的 OTLP gRPC 端口
http:
endpoint: 0.0.0.0:4318 # Jaeger 监听的 OTLP HTTP 端口
processors:
# 批量处理数据(提升效率)
batch:
exporters:
# 导出到 ES 存储(关联上面的存储后端)
jaeger_storage_exporter:
trace_storage: my_es_storage # 必须和 backends 中的 ID 一致
service:
# 启用扩展(存储+查询)
extensions: [jaeger_storage, jaeger_query]
# 数据处理管道(接收→处理→导出)
pipelines:
traces:
receivers: [otlp]
processors: [batch] # 加入批量处理器
exporters: [jaeger_storage_exporter] # 对应上面的导出器
3.3.2 OTel Collector 配置文件(conf/otelcol-config.yml)
OTel Collector 作为数据采集入口,需要配置数据接收协议和转发目标,确保业务上报的链路数据能转发到 Jaeger,配置如下:
yaml
# OpenTelemetry Collector 0.142.0 极简可运行配置
receivers:
# 接收 OTLP 协议数据(gRPC/HTTP 端口)
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
# 处理器:仅保留必选的 batch(提升数据发送效率)
processors:
batch:
timeout: 1s
send_batch_size: 1024
# 导出器:用 OTLP 替代已移除的 jaeger 导出器(Jaeger 支持 OTLP 协议)
exporters:
otlp/jaeger:
endpoint: jaeger:4317 # Jaeger 的 OTLP gRPC 端口
tls:
insecure: true # 开发环境关闭 TLS 验证
# 服务配置(核心:仅保留必要组件,避免报错)
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp/jaeger]
# 遥测配置:移除废弃的 address 字段,用默认配置
telemetry:
logs:
level: info
metrics:
level: basic # 仅保留基础指标
3.4 目录权限配置:ES 数据持久化
Elasticsearch 容器需要读写宿主机的es-trace-data目录来实现数据持久化,若权限不足会导致 ES 启动失败,因此需要提前创建目录并配置正确的权限:
bash
# 创建ES数据持久化目录:/data/otel/es-trace-data(可自定义路径)
# 作用:将ES数据存储在宿主机,容器重启后数据不丢失
mkdir -p /data/otel/es-trace-data
# 修改目录所属用户/组为1000:1000(匹配ES容器内用户)
# 原因:ES容器内用户无宿主机root权限,需赋予对应目录读写权限
chown -R 1000:1000 /data/otel/es-trace-data
# 修改目录权限为775:所有者可读可写可执行,同组可读可写可执行,其他可读可执行
# 作用:确保ES容器内用户能访问目录,同时限制其他用户的写入权限
chmod 775 /data/otel/es-trace-data
3.5 服务可用性验证:ES、Jaeger、OTel Collector
启动服务:
bash
# 启动服务
docker-compose up -d
docker-compose ps

服务部署完成后,需要验证核心组件是否正常运行,这是后续链路数据采集的基础。
3.5.1 Elasticsearch 可用性验证
ES 是链路数据的存储核心,首先验证其集群状态,并调整单节点的分片配置(避免因副本数导致的状态异常):
bash
# 检查ES集群健康状态
# 作用:验证ES是否启动成功,集群状态为green(健康)或yellow(单节点正常)
# 返回结果中"status"字段为green/yellow则正常,red则异常
curl -s http://127.0.0.1:9201/_cluster/health | jq .
# 设置所有索引副本数为0
# 原因:单节点ES无法创建副本(副本需要其他节点),若副本数>0,索引状态为red,无法写入数据
curl -X PUT http://127.0.0.1:9201/_all/_settings \
-H "Content-Type: application/json" \
-d '{
"index.number_of_replicas": 0
}'
# 创建全局索引模板:新增索引默认副本数为0
# 作用:后续Jaeger自动创建的链路索引,默认副本数为0,避免重复执行上述命令
curl -X PUT http://127.0.0.1:9201/_index_template/global-default-replicas-0 \
-H "Content-Type: application/json" \
-d '{
"index_patterns": ["*"],
"template": {
"settings": {
"index.number_of_replicas": 0,
"index.number_of_shards": 1
}
},
"priority": 1,
"version": 1,
"_meta": {
"description": "全局默认模板:所有新增索引副本数为 0,单节点专用"
}
}'
3.5.2 Jaeger UI 访问验证
Jaeger UI 是链路数据可视化的入口,访问http://服务器IP:16686,若能正常打开界面,说明 Jaeger 服务运行正常。

3.6 链路数据测试:手动上报与索引验证
核心服务正常后,需要验证链路数据能否正常采集、存储和查询。通过手动上报 OTLP 格式的测试数据,模拟业务链路数据,验证整个数据流转链路:
bash
# 1. 手动上报OTLP格式测试数据
# 作用:模拟业务服务向OTel Collector上报链路数据
# 参数说明:
# - service.name=test-manual:自定义服务名,便于检索
# - traceId/SpanId:链路/跨度唯一标识
# - startTimeUnixNano/endTimeUnixNano:跨度开始/结束时间(纳秒)
curl -X POST http://192.168.119.7:4318/v1/traces \
-H "Content-Type: application/json" \
-d '{
"resourceSpans": [
{
"resource": {
"attributes": [{"key": "service.name", "value": {"stringValue": "test-manual"}}]
},
"scopeSpans": [
{
"scope": {"name": "test-api"},
"spans": [
{
"traceId": "0123456789abcdef0123456789abcdef",
"spanId": "0123456789abcdef",
"name": "test-operation",
"kind": 2,
"startTimeUnixNano": "'$(($(date +%s)*1000000000))'",
"endTimeUnixNano": "'$(($(date +%s)*1000000000 + 1000000))'",
"status": {"code": 0}
}
]
}
]
}
]
}'
# 2. 手动刷新ES索引
# 作用:ES默认1秒刷新索引(近实时),手动刷新确保测试数据立即可见
curl -X POST http://192.168.119.7:9201/_refresh
# 3. 验证ES中是否存在测试数据
# 作用:验证链路数据是否成功写入ES
# 查询条件:process.serviceName=test-manual(匹配上报的服务名)
# 返回结果中"hits.total"大于0则说明数据写入成功
curl -s -X POST http://192.168.119.7:9201/jaeger-trace-jaeger-span-*/_search \
-H "Content-Type: application/json" \
-d '{
"query": {
"match": {
"process.serviceName": "test-manual"
}
}
}' | jq '.hits.total'
同时可在 Jaeger UI 中选择test-manual服务,点击 "Find Traces" 验证数据是否可见。
3.7 ES 生命周期管理:数据自动清理
链路数据会持续产生,若不做清理会导致 ES 磁盘占满,通过 Elasticsearch 的索引生命周期管理(ILM),可配置数据自动保留 7 天并删除,降低运维成本:
bash
# 1. 创建ILM策略:数据保留7天后删除
# 作用:定义链路数据的生命周期,避免手动清理过期数据
# 阶段说明:
# - hot阶段:数据写入后处于热阶段,优先级100(优先查询)
# - delete阶段:数据存储7天后自动删除
curl -X PUT http://192.168.119.7:9201/_ilm/policy/jaeger_trace_ilm_policy \
-H "Content-Type: application/json" \
-d '{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"set_priority": {
"priority": 100
}
}
},
"delete": {
"min_age": "7d",
"actions": {
"delete": {}
}
}
}
}
}'
# 2. 创建索引模板:关联ILM策略
# 作用:将ILM策略应用到所有Jaeger链路索引(jaeger-trace-*前缀)
# 配置说明:
# - index_patterns:匹配所有Jaeger链路索引
# - index.lifecycle.name:关联上述ILM策略
# - index.lifecycle.rollover_alias:索引滚动别名(便于索引管理)
curl -X PUT http://192.168.119.7:9201/_index_template/jaeger_trace_template \
-H "Content-Type: application/json" \
-d '{
"index_patterns": ["jaeger-trace-*"],
"template": {
"settings": {
"index.lifecycle.name": "jaeger_trace_ilm_policy",
"index.lifecycle.rollover_alias": "jaeger-trace-alias",
"index.number_of_shards": 1,
"index.number_of_replicas": 0
}
},
"priority": 100,
"version": 1
}'
# 3. 为已存在的索引应用ILM策略
# 作用:确保历史链路索引也遵循ILM策略,避免遗漏
curl -X PUT http://192.168.119.7:9201/jaeger-trace-*/_settings \
-H "Content-Type: application/json" \
-d '{
"index.lifecycle.name": "jaeger_trace_ilm_policy"
}'
3.8 业务服务数据上报:接入 OTel Agent
核心链路追踪系统搭建完成后,需要让实际业务服务接入 OTel,实现链路数据的自动采集。OTel 提供了无侵入的 Java Agent,只需在启动服务时添加相关参数即可:
3.8.1 手动启动服务
启动服务需要一个opentelemetry-javaagent.jar,下载opentelemetry-javaagent.jar:
bash
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v2.24.0/opentelemetry-javaagent.jar
启动微服务:
bash
# 手动启动Java服务并接入OTel Agent
# 参数说明:
# -javaagent:指定OTel Java Agent路径(无侵入采集链路数据)
# -Dotel.service.name=gateway:自定义服务名(Jaeger UI中显示的服务名)
# -Dotel.exporter.otlp.endpoint:OTel Collector的地址(接收链路数据)
# -Dfile.encoding=utf-8:避免日志乱码
# gateway.jar:业务服务JAR包
java -javaagent:/data/projects/opentelemetry-javaagent.jar -Dotel.service.name=gateway -Dotel.exporter.otlp.endpoint=http://192.168.119.7:4317 -Dfile.encoding=utf-8 -jar gateway.jar
3.8.2 Supervisor 启动服务(生产环境常用)
ini
[program:gateway]
# 服务工作目录
directory=/data/projects/
# 启动命令:接入OTel Agent并启动服务
# 参数说明:
# -Dotel.logs.exporter=none:关闭日志采集(避免重复采集)
# 其他参数同手动启动
command=/bin/bash -c "java -javaagent:/data/projects/opentelemetry-javaagent.jar -Dotel.service.name=gateway -Dotel.exporter.otlp.endpoint=http://192.168.119.7:4318 -Dotel.logs.exporter=none -Dfile.encoding=utf-8 -jar gateway.jar"
# 服务启动后自动启动
autostart=true
# 服务异常退出后自动重启
autorestart=true
# 启动后稳定运行5秒视为启动成功
startsecs=5
# 启动失败后重试3次
startretries=3
# 运行服务的用户
user=root
# 不重定向标准错误到标准输出
redirect_stderr=false
# 标准输出日志路径及大小限制
stdout_logfile=/data/logs/gateway.log
stdout_logfile_maxbytes=20MB
stdout_logfile_backups=3
# 标准错误日志路径及大小限制
stderr_logfile=/data/logs/gateway.err
stderr_logfile_maxbytes=20MB
stderr_logfile_backups=3
四、生产环境增强:Jaeger UI 登陆认证
测试环境中 Jaeger UI 可直接访问,但生产环境中链路数据包含业务调用细节,存在数据泄露风险,因此需要为 Jaeger UI 增加密码认证。本文通过 Nginx 反向代理 Jaeger UI,并配置 HTTP Basic 认证实现登陆校验。
4.1 环境准备
4.1.1 Nginx 镜像拉取
bash
# 1. 拉取Nginx Alpine镜像(轻量级,占用资源少)
docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/library/nginx:alpine
docker tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/library/nginx:alpine docker.io/library/nginx:alpine
# 2. 创建Nginx配置/密码/日志目录
# 作用:分离配置、密码、日志文件,便于管理
mkdir -p ./nginx/conf ./nginx/htpasswd ./nginx/logs
# 3. 赋予目录权限:确保Nginx容器能读取配置/写入日志
chmod 755 -R ./nginx ./nginx/logs
nginx配置文件:
bash
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
server {
listen 80;
server_name _;
# 指向本地生成的加密密码文件(容器内挂载路径)
auth_basic "Jaeger 链路追踪系统";
auth_basic_user_file /etc/nginx/htpasswd/jaeger_users;
# 安全响应头加固
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-XSS-Protection "1; mode=block";
# 反向代理到Jaeger容器(同一网络,容器名直接访问)
location / {
proxy_pass http://jaeger:16686;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_no_cache $http_upgrade;
expires -1;
}
client_max_body_size 10m;
}
}
4.2.2 生成密码文件
HTTP Basic 认证依赖密码文件,需使用 htpasswd 工具生成:
bash
# 生成密码文件:./nginx/htpasswd/jaeger_users
# 参数说明:
# -c:创建新文件
# admin:用户名
# 执行后输入密码(如123456),会自动加密存储
htpasswd -c ./nginx/htpasswd/jaeger_users admin
4.2 调整 Docker Compose:新增 Nginx 服务
修改docker-compose.yaml,新增 Nginx 代理服务,将 Jaeger UI 的访问入口改为 Nginx 端口,并关闭 Jaeger 直接访问的端口(可选):
bash
# 新增Nginx代理服务,实现Jaeger登陆认证
nginx-jaeger-proxy:
image: nginx:alpine
container_name: nginx-jaeger-proxy
networks:
- otel-trace-network
restart: always
ports:
- "16687:80" # 宿主机16687端口为Jaeger唯一访问入口
volumes:
- ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/htpasswd/jaeger_users:/etc/nginx/htpasswd/jaeger_users:ro
- ./nginx/logs:/var/log/nginx
depends_on:
jaeger:
condition: service_started
healthcheck:
interval: 10s
retries: 3
timeout: 5s
test: curl --fail http://localhost:80 || exit 1
deploy:
resources:
limits:
cpus: '1'
memory: 512M
至此,完整的docker-compose文件如下:
bash
version: '3.3'
# 自定义桥接网络:隔离链路追踪相关服务,避免与宿主机/其他容器网络冲突
networks:
otel-trace-network:
driver: bridge
ipam:
config:
- subnet: 172.29.0.0/16
services:
# Elasticsearch 8.19.7 单节点服务:链路数据存储核心
es-trace:
image: docker.elastic.co/elasticsearch/elasticsearch:8.19.7
container_name: es-trace
# 接入自定义隔离网络
networks:
- otel-trace-network
# 异常退出自动重启,提升服务可用性
restart: always
# 环境变量配置:核心参数,适配单节点部署和Jaeger存储需求
environment:
- cluster.name=otel-trace-cluster
- node.name=es-trace-01
# JVM内存配置:根据服务器资源调整,建议最小1G,最大不超过32G(ES默认堆内存上限)
- "ES_JAVA_OPTS=-Xms2g -Xmx4g"
# 8.x版本单节点必须配置,否则集群无法启动
- discovery.type=single-node
# 锁定内存,避免ES内存被系统SWAP,提升性能
- bootstrap.memory_lock=true
# 测试环境关闭安全层(生产环境建议开启并配置证书)
- xpack.security.enabled=false
- xpack.security.enrollment.enabled=false
# 允许Jaeger自动创建索引(否则链路数据无法写入)
- action.auto_create_index=true
# 自动获取挂载目录权限,避免数据目录权限不足
- TAKE_FILE_OWNERSHIP=true
# 端口映射:宿主机9201→容器9200(HTTP),9301→容器9300(TCP)
ports:
- "9201:9200"
- "9301:9300"
# 数据卷挂载:宿主机目录→容器数据目录,实现ES数据持久化
volumes:
- ./es-trace-data:/usr/share/elasticsearch/data
# 内存锁定配置:禁用SWAP,匹配bootstrap.memory_lock=true
ulimits:
memlock:
soft: -1
hard: -1
# 健康检查:确保ES启动完成且集群状态正常后,再启动依赖服务
healthcheck:
interval: 30s
retries: 20
timeout: 10s
test: >
bash -c 'curl -s http://localhost:9200/_cat/health?v | grep -q "green\|yellow"'
# 资源限制:避免ES占用过多服务器资源,影响其他组件
deploy:
resources:
limits:
cpus: '2'
memory: 4G
# 重点修改:jaeger 服务(改用环境变量配置,去掉无效 YAML 挂载)
jaeger:
image: jaegertracing/jaeger:2.12.0
container_name: jaeger
networks:
- otel-trace-network
ports:
- "16686:16686"
- "14317:4317"
environment:
# 1. 指定存储类型为 Elasticsearch
- SPAN_STORAGE_TYPE=elasticsearch
# 2. ES 容器内通信地址(必须是 es-trace:9200,docker-compose 网桥内互通)
- ES_SERVER_URLS=http://es-trace:9200
# 3. ES 索引前缀(和你的索引名匹配)
- ES_INDEX_PREFIX=jaeger-trace
# 4. 核心:字段映射,解决 traceID 报错(官方环境变量,直接指定 keyword 子字段)
- ES_MAPPINGS_TRACEID=traceID.keyword
- ES_MAPPINGS_SPANID=spanID.keyword
- ES_MAPPINGS_SERVICENAME=process.serviceName.keyword
# 5. 启用 OTLP 协议接收(适配你的 otel-collector 转发数据)
- COLLECTOR_OTLP_ENABLED=true
# 6. 可选:ES 操作超时时间
- ES_TIMEOUT=10s
depends_on:
es-trace:
condition: service_healthy
restart: always
deploy:
resources:
limits:
cpus: '1'
memory: 2G
# OpenTelemetry Collector 0.142.0 服务:统一链路数据采集网关
otel-collector:
image: otel/opentelemetry-collector-contrib:0.142.0
container_name: otel-collector
networks:
- otel-trace-network
# 启动命令:加载自定义配置文件
command:
- --config=/etc/otelcol-config.yml
# 配置文件挂载:宿主机自定义配置→容器配置文件
volumes:
- ./conf/otelcol-config.yml:/etc/otelcol-config.yml
# 端口映射:OTLP gRPC/HTTP接收端口,对接业务服务上报
ports:
- "4317:4317"
- "4318:4318"
# 依赖关系:等待Jaeger启动后启动,避免数据转发失败
depends_on:
jaeger:
condition: service_started
restart: always
# 健康检查:验证Collector是否就绪,可接收/转发数据
# 资源限制
deploy:
resources:
limits:
cpus: '1'
memory: 1G
# Nginx 代理服务:生产环境Jaeger UI认证
nginx-jaeger-proxy:
image: nginx:alpine
container_name: nginx-jaeger-proxy
networks:
- otel-trace-network
restart: always
# 端口映射:宿主机16687→容器80(Jaeger UI唯一访问入口)
ports:
- "16687:80"
# 配置/密码/日志挂载:实现反向代理和认证配置
volumes:
- ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/htpasswd/jaeger_users:/etc/nginx/htpasswd/jaeger_users:ro
- ./nginx/logs:/var/log/nginx
# 依赖关系:等待Jaeger启动后启动
depends_on:
jaeger:
condition: service_started
# 健康检查:验证Nginx是否正常运行
healthcheck:
interval: 10s
retries: 3
timeout: 5s
#test: curl --fail http://localhost:80 || exit 1
test: curl --fail -u 'admin:password' http://localhost:80 || exit 1
# 资源限制:Alpine版本轻量,按需配置
deploy:
resources:
limits:
cpus: '1'
memory: 512M
4.3 验证 Nginx 认证
在nginx/conf/nginx.conf中配置反向代理和密码认证,同时在nginx/htpasswd/jaeger_users中添加用户密码(可通过htpasswd工具生成),配置完成后重启 Docker Compose,访问http://服务器IP:16687即可看到登陆验证界面。
登陆验证:

五、方案总结与优化建议
5.1 方案总结
本文搭建的分布式链路追踪方案基于 OpenTelemetry、Jaeger、Elasticsearch 三大核心组件,具备以下核心优势:
- 标准化采集:基于 OTel 实现多语言、多协议的链路数据统一采集,避免厂商锁定,降低业务接入成本;
- 可视化分析:Jaeger UI 提供直观的链路检索、拓扑分析、耗时统计能力,将 "黑盒" 的微服务调用转为 "白盒",大幅降低排障效率;
- 高性能存储:ES 适配链路数据的时序性、检索性需求,结合 ILM 实现数据自动清理,兼顾性能和存储成本;
- 生产级安全:通过 Nginx 代理实现 Jaeger UI 密码认证,避免链路数据泄露(后续增强可选);
- 易部署维护:Docker Compose 编排所有组件,一键部署 / 重启,简化运维成本。
5.2 优化建议
针对生产环境的高可用、高性能、高安全需求,可进一步优化:
高可用优化:
- ES 改为集群模式(至少 3 节点),配置副本数 > 0,避免单点故障;
- Jaeger Collector/OTel Collector 部署多实例,通过负载均衡(如 Nginx/HAProxy)分发请求;
- 所有组件数据目录挂载到分布式存储(如 NFS/CEPH),避免宿主机磁盘故障导致数据丢失。
性能优化:
- 调整 OTel Collector 批量参数(如
send_batch_size=2048、timeout=2s),根据业务 QPS 适配; - ES 调整分片数(如按天创建索引,单索引分片数 = 节点数),提升查询性能;
- 开启 Jaeger 采样策略(如按比例采样 10%),减少非核心链路数据量。
监控告警:
- 为 ES/Jaeger/OTel Collector 接入 Prometheus + Grafana,监控核心指标(如 ES 磁盘使用率、Jaeger 数据写入延迟、Collector 接收 / 转发量);
- 配置告警规则(如 ES 磁盘使用率 > 80%、Collector 健康检查失败),通过钉钉 / 邮件 / 短信推送告警。
安全优化:
- 开启 OTel Collector 与 Jaeger、Jaeger 与 ES 之间的 TLS 加密,配置证书认证;
- ES 开启 xpack.security,配置用户名 / 密码认证,限制访问 IP;
- 业务服务与 OTel Collector 之间配置网络隔离(如防火墙),仅允许业务服务网段访问 Collector 端口。
- 易部署维护:Docker Compose 编排所有组件,一键部署 / 重启,简化运维成本。