Jaeger 部署与配置:单机模式、生产部署、存储后端选型

说起 Jaeger 的部署,我一开始也觉得很简单——不就是 docker-compose up 一下嘛?后来踩过几次坑,才发现从「跑起来」到「能扛住线上流量」,中间隔着一个大西洋。今天这篇我把踩过的坑、生产环境的心得、以及存储后端怎么选,一次性讲清楚。

一、单机模式:10 分钟快速上手

说白了,单机模式就是让你看看 Jaeger 长什么样。对于本地开发、功能验证,完全够用。我的习惯是用 Docker Compose 搞定所有组件。

个人习惯:我会在 docker-compose.yml 里显式指定版本号,避免无意中拉新版本导致配置不兼容。我曾经因为用了 latest 标签,结果存储后端索引结构变了,第二天查数据查不到,排查了半天才发现是版本跳跃的问题。

最简单的配置如下:

version: '3.7'
services:
  jaeger-collector:
    image: jaegertracing/jaeger-collector:1.60
    ports:
      - "14269:14269"  # admin
      - "14268:14268"  # thrift
      - "4317:4317"    # OTLP gRPC
      - "4318:4318"    # OTLP HTTP
    environment:
      - SPAN_STORAGE_TYPE=badger  # 无外依赖,本地文件存储

  jaeger-query:
    image: jaegertracing/jaeger-query:1.60
    ports:
      - "16686:16686"  # UI
      - "16687:16687"  # admin
    environment:
      - SPAN_STORAGE_TYPE=badger
    depends_on:
      - jaeger-collector

  jaeger-agent:
    image: jaegertracing/jaeger-agent:1.60
    ports:
      - "6831:6831/udp"
      - "14271:14271"  # admin
    command: ["--reporter.grpc.host-port=jaeger-collector:14250"]
    depends_on:
      - jaeger-collector

启动后访问 http://localhost:16686,就能看到干净的 Jaeger UI。嗯,这里要注意:Badger 是内嵌的本地 KV 存储,重启后数据可能丢失,只适合临时测试。

警告:生产环境千万别用 Badger!它用不了多实例,数据膨胀后查询效率急速下降。我曾在预发环境用 Badger 撑了三天,结果查询响应时间从 50ms 涨到 10秒+,吓得赶紧换掉了。

二、生产部署:架构升级与高可用

生产环境的目标很明确:高可用、可扩展、数据不丢。这时需要把 Collector、Query 分开部署,再加上存储后端和消息队列。以下是我推荐的最小生产架构。

Jaeger 生产部署架构(含存储后端选型) 应用服务(OTLP) Agent(UDP) 现有 SDK(Jaeger) Jaeger Collector 集群(无状态,可水平扩) Kafka / 消息队列 存储适配层(kafka / 直写) Elasticsearch 适合日志/链路上层分析 Cassandra 高写入场景首选 Badger(仅测试) 单机,不可持久化 Jaeger Query 服务 Kafka 可做缓冲区 让 Collector 和存储解耦

三、存储后端选型:核心决策

选哪个存储后端,直接决定了你的运维成本和查询体验。我对比过三种主流方案,整理了一张表。

特性 Elasticsearch Cassandra Badger
写入性能 中等,有写放大 优秀,线性可扩展 极快(单机)
查询灵活性 丰富聚合、全文检索 有限(按 traceID / 时间) 很有限
运维复杂度 中等,需调优堆内存 较高,集群管理 零运维
持久化 支持,可配置 ILM 支持,需备份策略 重启数据丢失
建议场景 中小规模,需要快速集成 大规模,写入量 > 10W span/s 开发测试,不可用

我的生产选型经验:如果你的团队已经有 Elasticsearch 运维经验,选 ES 最省事。如果 span 量极大(例如百万级/秒),建议上 Cassandra,写入性能更稳定。我不建议在大规模场景用 ES,因为写入抖动会导致索引性能问题——我曾经遇到 ES 写入队列打满导致 Jaeger Collector OOM,后来换成 Cassandra + Kafka 缓冲才解决。

四、关键配置参数(生产必备)

配置 Jaeger 不复杂,但有几个参数容易忽略。

Collector 的 gRPC 与 HTTP 端口

  • 14250 – 内部 gRPC(Agent 到 Collector)
  • 4317 – OTLP gRPC(新 SDK 首选)
  • 14268 – Thrift HTTP(兼容旧 SDK)

Query 缓存与超时

--query.max-clock-skew-adjustment=5s   # 时钟偏差校准
--query.additional-headers=access-control-allow-origin:*   # CORS 跨域
--es.index-prefix=jaeger-              # ES 索引前缀
曾经踩过的坑:我忘了设置 --es.use-ilm=true,结果 ES 索引数量暴涨到几千个,查询时 ES 直接挂了。如果你用 ES,强烈建议开启 ILM 策略。

五、存储后端选型的补充决策树

当你拿不准选哪个时,可以用这个简化的决策逻辑:

  1. 单机/测试 → Badger(零依赖)
  2. 规模 < 10K span/s → Elasticsearch(如果有 ES 人力)
  3. 规模 10K–100K span/s → Elasticsearch + ILM,或 Cassandra(选你熟悉的)
  4. 规模 > 100K span/s → Cassandra + Kafka(强烈推荐)

另外提一句:如果你用了 Kafka,Collector 的 --kafka.producer.batch-size 可以调到 16384,减少网络 IO。

# 生产 Collector 启动命令示例(使用 ES + Kafka)
docker run -d --name=jaeger-collector \
  -e SPAN_STORAGE_TYPE=elasticsearch \
  -e ES_SERVER_URLS=http://es-cluster:9200 \
  -e ES_INDEX_PREFIX=jaeger- \
  -e ES_USE_ILM=true \
  -e ES_ILM_POLICY_NAME=jaeger-ilm-policy \
  -e KAFKA_PRODUCER_BROKERS=kafka-broker:9092 \
  -e KAFKA_PRODUCER_BATCH_SIZE=16384 \
  jaegertracing/jaeger-collector:1.60

我个人习惯把环境变量统一放在一个 .env 文件里,方便多环境切换。你想想看,如果你每次部署都要改十几个参数,迟早会漏掉一个导致线上故障。

六、小结

这一章我们把 Jaeger 从单机玩具到生产可用说了一遍。核心就是三点:

  • 单机用 Docker Compose + Badger 快速验证;
  • 生产务必引入 Kafka 做缓冲,Collector 无状态水平扩展;
  • 存储后端的选择决定了你的运维成本和查询上限,不要只看初期性能。