OpenTelemetry+Jaeger+ES:分布式链路追踪实战部署

文章目录

  • 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 的存储后端,能平衡 "存储成本、查询性能、运维复杂度",是中小规模到大规模微服务链路追踪的首选存储方案。

二、整体方案设计:数据流转与架构逻辑

本方案的核心逻辑是 "标准化采集→统一转发→存储→可视化分析",数据流转流程如下:

  1. 业务服务通过 OTel Java Agent(无侵入方式)采集链路数据,标准化为 OTLP 协议;
  2. OTel Collector 接收业务服务上报的 OTLP 数据,批量处理后转发给 Jaeger;
  3. Jaeger 将链路数据写入 Elasticsearch 进行持久化存储;
  4. 运维 / 开发人员通过 Jaeger UI 查询链路数据,分析调用问题;
  5. 生产环境通过 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 三大核心组件,具备以下核心优势:

  1. 标准化采集:基于 OTel 实现多语言、多协议的链路数据统一采集,避免厂商锁定,降低业务接入成本;
  2. 可视化分析:Jaeger UI 提供直观的链路检索、拓扑分析、耗时统计能力,将 "黑盒" 的微服务调用转为 "白盒",大幅降低排障效率;
  3. 高性能存储:ES 适配链路数据的时序性、检索性需求,结合 ILM 实现数据自动清理,兼顾性能和存储成本;
  4. 生产级安全:通过 Nginx 代理实现 Jaeger UI 密码认证,避免链路数据泄露(后续增强可选);
  5. 易部署维护:Docker Compose 编排所有组件,一键部署 / 重启,简化运维成本。

5.2 优化建议

针对生产环境的高可用、高性能、高安全需求,可进一步优化:

高可用优化

  • ES 改为集群模式(至少 3 节点),配置副本数 > 0,避免单点故障;
  • Jaeger Collector/OTel Collector 部署多实例,通过负载均衡(如 Nginx/HAProxy)分发请求;
  • 所有组件数据目录挂载到分布式存储(如 NFS/CEPH),避免宿主机磁盘故障导致数据丢失。

性能优化

  • 调整 OTel Collector 批量参数(如 send_batch_size=2048timeout=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 编排所有组件,一键部署 / 重启,简化运维成本。
相关推荐
八月瓜科技2 小时前
AI侵权频发:国内判例定边界,国际判决敲警钟
大数据·人工智能·科技·深度学习·机器人
无垠的广袤2 小时前
【VisionFive 2 Lite 单板计算机】边缘AI视觉应用部署:人脸检测
linux·人工智能·python·opencv·开发板
曹天骄2 小时前
Cloudflare CDN 预热全面实战指南(含全球 PoP 解析 + 预热覆盖模型)
运维·开发语言·缓存
福赖2 小时前
《微服务即使通讯中ES的作用》
大数据·elasticsearch
三不原则2 小时前
AIOps 数据采集:日志/指标/链路数据的整合与标准化
运维
Dola_Zou2 小时前
如何用一套加密狗方案打通 Windows、Linux 与 macOS等,零成本实现跨平台交付?
linux·安全·macos·自动化·软件工程·软件加密
hoiii1872 小时前
分布式电源选址定容的MATLAB算法实现
分布式·算法·matlab
Wpa.wk2 小时前
Docker - 搭建镜像仓库- 了解
运维·经验分享·测试工具·docker·容器
盖雅工场2 小时前
业务波动适配型排班,破解零售服务业人力失衡难题
大数据·人工智能