概述

Jaeger 的核心后端组件都是以 Docker 镜像的形式发布在 DockerHub 和 Quay 上,具体的镜像地址如下表:

组件 Dockerhub Quay
jaeger-agent hub.docker.com/r/jaegertracing/jaeger-agent/ quay.io/repository/jaegertracing/jaeger-agent
jaeger-collector hub.docker.com/r/jaegertracing/jaeger-collector/ quay.io/repository/jaegertracing/jaeger-collector
jaeger-query hub.docker.com/r/jaegertracing/jaeger-query/ quay.io/repository/jaegertracing/jaeger-query
jaeger-ingester hub.docker.com/r/jaegertracing/jaeger-ingester/ quay.io/repository/jaegertracing/jaeger-ingester

此外,如下还有一些 Jaeger 部署的编排模板:

  • Kubernetes: github.com/jaegertracing/jaeger-kubernetes
  • OpenShift: github.com/jaegertracing/jaeger-openshift

配置项

Jaeger 二进制文件可以通过多种方式进行配置设置(优先级依次递减):

  • 命令行参数
  • 环境变量
  • JSON、TOML、YAML、HCL或Java Properties 格式的配置文件

要查看完整的选项列表,请使用 help 命令运行二进制文件或参阅 CLI 页面以获取更多信息。

只有在选择了存储类型时才会列出特定该存储后端的相关选项。
例如,要查看带有 Cassandra 存储的收集器中的所有可用选项时,需要如下进行查询:

  1. docker run --rm \
  2. -e SPAN_STORAGE_TYPE=cassandra \
  3. jaegertracing/jaeger-collector:1.25 \
  4. help

为了通过环境变量提供配置参数,找到相应的命令行选项并将其名称转换为大写且下划线分隔的形式即可,例如:

命令行参数 环境变量名称
—cassandra.connections-per-host CASSANDRA_CONNECTIONS_PER_HOST
—metrics-backend METRICS_BACKEND

Agent

Jaeger 客户端库期望 jaeger-agent 进程在每个主机上本地运行。 Agent 暴露以下端口:

端口 协议 功能
6831 UDP 用于大部分Jaeger Client发来的 compact 格式的Thrfit 请求。
6832 UDP 用于接收 Node.js Client 发来的 binary 格式的 Thrift 请求。
5778 HTTP 提供服务配置和采样策略查询功能。
5775 UDP 使用 compact thrift 协议接收 zipkin.thrift 协议发来的请求。
14271 HTTP 管理端口,可以调用 / 进行健康检查,/metrics查询监控指标

Agent可以直接在物理机部署,也可以通过Docker镜像来执行,示例如下:

  1. docker run \
  2. --rm \
  3. -p6831:6831/udp \
  4. -p6832:6832/udp \
  5. -p5778:5778/tcp \
  6. -p5775:5775/udp \
  7. jaegertracing/jaeger-agent:1.25

与服务发现系统集成

Agent可以点对点直接连接到一个Collector的实例地址上,也可以连接到一个后端是多个Collector实例的负载均衡器上,同样,还可以直接配置一组静态的Collector实例地址。
在Docker中,一个常用的部署命令如下:

  1. docker run \
  2. --rm \
  3. -p5775:5775/udp \
  4. -p6831:6831/udp \
  5. -p6832:6832/udp \
  6. -p5778:5778/tcp \
  7. jaegertracing/jaeger-agent:1.25 \
  8. --reporter.grpc.host-port=jaeger-collector.jaeger-infra.svc:14250

在使用gRPC协议时,有多个关于负载均衡和名称解析的一系列配置项:

  • 单个连接时无负载均衡。默认情况下,如果你指定的是单个host:port,则不会有负载均衡的效果。
  • 一组静态host名称时,默认使用round-robin负载均衡策略,即指定了一组由英文逗号分隔的服务地址,例如: reporter.grpc.host-port=jaeger-collector1:14250,jaeger-collector2:14250,jaeger-collector3:14250。
  • 动态DNS解析和round-robin负载均衡策略,此时,需要在地址前增加dns:///,gRPC将会尝试对地址进行解析并进行负载均衡发送,示例如下:—reporter.grpc.host-port=dns:///jaeger-collector.jaeger-infra.svc:14250。

Agent级别标签

Jaeger 支持在 Agent 级别增加 Tag 标签。
具体来说,在Agent级别上增加Tag标签后,Agent会自动将该标签追加到通过Agent收集到的所有span的tag中。
该标签支持通过命令行的方式进行设置,例如:—agent.tags=key1=value1,key2=value2,…,keyn=valuen。同时,也支持通过envFlag的方式来设置,例如:—agent.tags=key=${envFlag:defaultValue}。如果没有设置时,默认得到的将会是 envFlag 作为 key,defaultValue为对应的值。

Collector

Collector 服务是无状态的,因此可以同时部署很多个 Jaeger-Collecor 实例。
Collector 的配置非常的简单,仅仅配置一下后端的存储地址即可。
例如对于 Cassandra 存储而言,仅需要配置 —cassandra.keyspace 和 —cassandra.servers 选项。
对于 ElasticSearch 存储而言,仅需要配置 —es.server-urls 选项。

可以通过如下命令查询完整的命令行参数:

  1. go run ./cmd/collector/main.go -h

或者直接使用二进制文件执行如下命令:

  1. docker run -it --rm jaegertracing/jaeger-collector:1.25 -h

在默认设置下,collector 提供了以下端口:

端口 协议 功能
14250 gRPC 接收 jaeger-agent 上报span数据
14268 HTTP 接收 client 直接以 binary thrift 协议上报给collector的数据
9411 HTTP 接收上报的Zipkin Span数据,默认是禁用的
14269 HTTP 管理端口,可以调用 / 进行健康检查,/metrics查询监控指标

存储后端

Collector 需要一个存储后端进行数据的持久化存储。常用的存储后端主要推荐 Cassandra 和 ElasticSearch。

存储后端类型的选择可以通过 SPAN_STORAGE_TYPE 环境变量来进行设置,可选值为 cassandra, elastisearch, kafka(仅作为缓冲), grpc-plugin, badger(仅用于all-in-one), memory(仅用于all-in-one)。

从 1.6.0 版本开始,通过向 SPAN_STORAGE_TYPE 环境变量提供逗号分隔的一组后端存储类型列表,可以同时使用多种存储类型。需要注意的是,所有列出的存储类型都用于写入,但只有列表中的第一个类型将用于读取和归档。

对于大规模生产部署,Jaeger 团队建议使用 Elasticsearch 后端而不是 Cassandra。

内存存储

内存存储不适用于生产场景。
它仅仅是一个快速入门的解决方案,一旦进程发生重启,其中的数据将会丢失。
默认情况下,存储在内存中的trace数据并没有限制,但是可以通过 —memory.max-traces 参数来传递一个整数值限制存储的最大trace数量。

Badger-本地存储

Jaeger 1.9 版本中引入支持。
Badger 是一种本地存储方式,仅支持 all-in-one 的部署方式。
默认情况下,它使用临时文件充当临时存储。可以通过使用 —badger.ephemeral=false 选项来覆盖。

  1. docker run \
  2. -e SPAN_STORAGE_TYPE=badger \
  3. -e BADGER_EPHEMERAL=false \
  4. -e BADGER_DIRECTORY_VALUE=/badger/data \
  5. -e BADGER_DIRECTORY_KEY=/badger/key \
  6. -v <storage_dir_on_host>:/badger \
  7. -p 16686:16686 \
  8. jaegertracing/all-in-one:1.25

在 Jaeger 1.24.0 的版本中,将依赖的 Badger 版本从 v1.6.2 版本升级到了 v3 版本,这一升级改变了底层的数据格式。如果要进行相关升级,需要进行相关的数据处理操作。

Cassandra

Jaeger 支持 Cassandra 3.4 以上的版本。
Cassandra 的部署不在本文中讲解,可以参考 Cassandra 部署文档

配置 collector 使用 Cassandra 存储的最简命令如下:

  1. docker run \
  2. -e SPAN_STORAGE_TYPE=cassandra \
  3. -e CASSANDRA_SERVERS=<...> \
  4. jaegertracing/jaeger-collector:1.25

使用如下命令可以查询完整的相关命令:

  1. docker run \
  2. -e SPAN_STORAGE_TYPE=cassandra \
  3. jaegertracing/jaeger-collector:1.25 \
  4. --help

Jaeger 提供了一个脚本用户初始化 Cassandra 的 keyspace 和 schema 结构:

  1. MODE=test sh ./plugin/storage/cassandra/schema/create.sh | cqlsh

对于生产环境而言,需要将 MODE=prod DATACENTER={datacenter} 传递给执行脚本。
其中,{datacenter} 是 Cassandra 配置/网络拓扑中使用的名称。

该脚本还允许设置 TTL、键空间名称、复制因子等。不带参数时运行脚本可以查看该脚本支持的参数的完整列表。

ElasticSearch

Jaeger 的后端存储支持 ElasticSearch 的 5.X, 6.X 和 7.X 版本。
Elasticsearch 版本是从 root/ping url 中自动识别的。基于此版本 Jaeger 使用兼容的索引映射和 Elasticsearch REST API 来进行与ElasticSearch 交互。
此外,还可以通过 —es.version= 标志明确提供版本。

除了安装和运行 Elasticsearch 之外,Elasticsearch 不需要进行额外的初始化。
当ElasticSearch正常运行后,只要将正确的配置值传递给 Jaeger Collector 和 Query Service 即可。

一个最简的配置如下:

  1. docker run \
  2. -e SPAN_STORAGE_TYPE=elasticsearch \
  3. -e ES_SERVER_URLS=<...> \
  4. jaegertracing/jaeger-collector:1.25

查询全部支持的配置方式如下:

  1. docker run \
  2. -e SPAN_STORAGE_TYPE=elasticsearch \
  3. jaegertracing/jaeger-collector:1.25 \
  4. --help

ElasticSearch 索引的分片和副本

分片和副本是一些需要特别注意的配置值,它们是在创建索引时决定的。
该文章详细介绍了有关选择应选择多少个分片从而优化整个可用性和性能的内容。

Elasticsearch Rollover

Elasticsearch rollover 是一种索引管理策略,可以优化分配给索引的资源的使用。
例如,不包含任何数据的索引可能仍然分配了分片,相反,有时单个索引可能包含比其他索引多得多的数据。
Jaeger 默认将数据存储是按天粒度进行切割的,然后这种方式可能无法最大程度的利用我们的资源。
可以通过 —es.use-aliases=true 启用rollover策略。

rollover 可以允许根据以下一项或多项配置来决定是否开启一个新的索引:

  • max_age: 索引存在的最长时间,支持单位d, h, m。
  • max_docs: 索引中包含的最大文档数。
  • mac_size: 索引中主分片的最大大小,支持的单位包括tb, gb, mb。

rollover 索引管理策略比默认的每天进一次索引分隔要复杂的多,它需要一个初始化任务来进行存储初始化,同时来需要两个定时任务来管理索引。
想要了解有关 Jaeger 中 rollover 的更多细节,可以了解这篇文章
想要自动化的实现 rollover 功能,可以参考 ElasticSeach ILM 支持一文。

Initialize

以下命令通过创建索引别名、索引和索引模板来准备 Elasticsearch 以进行rollover 配置:

  1. docker run -it --rm --net=host jaegertracing/jaeger-es-rollover:latest init http://localhost:9200 # <1>

如果需要初始化存档存储,请添加 -e ARCHIVE=true。
初始化后 Jaeger 可以使用 —es.use-aliases=true 进行部署。

Rolling over to a new index

下一步是定期调用 rollover API,它根据设置的条件来判断是否需要切分到新索引。
该命令还会添加新索引来读取alias,以使新数据可用于搜索。

  1. docker run -it --rm --net=host -e CONDITIONS='{"max_age": "2d"}' jaegertracing/jaeger-es-rollover:latest rollover http://localhost:9200 # <1>

上述命令表示如果当前写入索引的存在的时间超过 2 天,该命令会将切分到新索引并将后续数据写入新的索引。关于更多的条件,可以参考 ElasticSearch 文档

下一步是从读取别名中删除旧索引,这意味着旧数据将无法用于搜索。
这模仿了默认每天索引部署中使用的 —es.max-span-age 标志的行为。
此步骤可以是可选的,并且可以在下一步中通过索引清理器简单地删除旧索引。

  1. docker run -it --rm --net=host -e UNIT=days -e UNIT_COUNT=7 jaegertracing/jaeger-es-rollover:latest lookback http://localhost:9200 # <1>

上述操作表示从读取别名中删除超过 7 天的索引。

Remove old data

可以使用 jaeger-es-index-cleaner 来删除历史数据,该工具也同样用于每天切分索引的场景。

  1. docker run -it --rm --net=host -e ROLLOVER=true jaegertracing/jaeger-es-index-cleaner:latest 14 http://localhost:9200 # <1>

上述命令表示删除超过14天的索引数据。

Elasticsearch ILM support

该功能是在 Jaeger 1.22.0 版本加入的,仅支持 ElasticSearch 7.X 版本。
Elasticsearch ILM 根据性能、弹性和保留要求来自动管理索引。
例如:

  • 按大小(字节或文档数)或时间切分到新索引并归档以前的索引。
  • 删除过早的索引数据等。

启用 ILM 的步骤如下:
Step1: 在 elasticsearch 中创建一个名为 jaeger-ilm-policy 的 ILM 策略。
例如,以下策略将在“active”索引存在时间达到 1m 时切分并删除存在时间超过 2m 的索引。

  1. curl -X PUT http://localhost:9200/_ilm/policy/jaeger-ilm-policy \
  2. -H 'Content-Type: application/json; charset=utf-8' \
  3. --data-binary @- << EOF
  4. {
  5. "policy": {
  6. "phases": {
  7. "hot": {
  8. "min_age": "0ms",
  9. "actions": {
  10. "rollover": {
  11. "max_age": "1m"
  12. },
  13. "set_priority": {
  14. "priority": 100
  15. }
  16. }
  17. },
  18. "delete": {
  19. "min_age": "2m",
  20. "actions": {
  21. "delete": {}
  22. }
  23. }
  24. }
  25. }
  26. }
  27. EOF

Step2: 使用 ES_USE_ILM=true 运行 elasticsearch initializer:

  1. docker run -it --rm --net=host -e ES_USE_ILM=true jaegertracing/jaeger-es-rollover:latest init http://localhost:9200 # <1>

如果需要初始化存档存储,请添加 -e ARCHIVE=true。
Step3: 初始化后,使用—es.use-ilm=true 和—es.use-aliases=true 部署Jaeger。

Kafka

存储插件

Ingester

jaeger-ingester 是一个单独的服务,它从 Kafka Topic 读取 span 数据并将其写入另一个存储后端(Elasticsearch 或 Cassandra)。

关于 Ingester 服务的完整命令可以参考如下方式查询:

  1. docker run \
  2. -e SPAN_STORAGE_TYPE=cassandra \
  3. jaegertracing/jaeger-ingester:1.25
  4. --help

QueryService && UI

jaeger-query 提供了一组 API Url 和 React/Javascript UI页面。
该服务是无状态的,通常在负载均衡器之后运行,例如 NGINX。

端口 协议 功能
16685 gRPC 提供基于 gRPC/protobuf 协议的查询服务
16686 HTTP /api* url 以及 / 的UI页面
16687 HTTP 管理端口,可以调用 / 进行健康检查,/metrics查询监控指标

最简部署示例(ES后端)

  1. docker run -d --rm \
  2. -p 16685:16685 \
  3. -p 16686:16686 \
  4. -p 16687:16687 \
  5. -e SPAN_STORAGE_TYPE=elasticsearch \
  6. -e ES_SERVER_URLS=http://<ES_SERVER_IP>:<ES_SERVER_PORT> \
  7. jaegertracing/jaeger-query:1.25

时钟偏移校准

aeger 后端会收到来自不同主机上运行的应用程序的trace数据。
然而主机上的硬件时钟经常会出现相对漂移,称为时钟偏移效应。
时钟偏差会使trace分析变得困难,例如,当服务器span可能比客户端span更早开始时,这应该是不可能的。
Query Service使用了有关span之间关联关系的知识,通过时钟偏差调整算法来校正时钟漂移量。
所有调整的span数据都会在 UI 中显示警告,并提供可应用于其时间戳的准确时钟偏差的校准值。

有时,这些调整本身会使trace的数据难以理解。
例如,当在其父 span 的边界内重新定位服务器 span 时,Jaeger 不知道请求和响应延迟之间的确切关系,因此它只能将子 span 放置在父 span 的中间。

QueryService 支持配置 —query.max-clock-skew-adjustment 来控制应该允许多少时钟偏差调整。将此参数设置为零 (0s) 将完全禁用时钟偏移调整。此设置适用于从给定QueryService查询的所有Trace数据。

UI页面根路径

所有 jaeger-query HTTP 路由的基本路径都可以设置为除/之外的值,例如 /jaeger 将导致所有 UI URL 以 /jaeger 开头。这对于将 jaeger-query 部署在一个反向代理服务之后的场景中非常有用。

可以通过 —query.base-path 命令行参数或 QUERY_BASE_PATH 环境变量配置基本路径。

UI定制与嵌入

请参阅专用的前端/UI 页面

用于计算服务依赖的聚合任务

生产环境部署下,需要一个外部进程来聚合数据并在服务之间创建相互依赖关系。
项目 spark-dependencies 是一个 Spark 作业,它用于计算服务依赖链并将它们直接存储到后端存储中。

Ps: 对于 all-in-one 的部署方式而言,所有的trace数据都存储在内存中,因此,在 Query UI 页面查询依赖关系图时,可以直接快速的得到的对应的依赖关系。而对于生产环境的部署而言,所有的trace数据其实是存储在后端存储服务ElasticSearch或者Cassandra中的,因此,直接从后端存储查询trace并实时构建Dependency关系图就显得不合时宜了,因此,需要一个独立的Job来定时执行计算依赖关系,Query UI 仅负责数据显示即可。

jaeger 提供了一个 spark-dependencies 镜像可以直接使用,首先,我们需要拉取对应的镜像:

  1. docker pull jaegertracing/spark-dependencies

其中,其实就是包含了一个 jar 包,jaeger-spark-dependencies-0.0.1-SNAPSHOT.jar ,这也是该任务的唯一依赖包。
Ps: 该 Jar 包依赖 OpenJDK-8 的 Java 版本来执行。

使用 Docker 镜像触发spark依赖任务分析的最简单命令如下:

  1. docker run --env STORAGE=elasticsearch --env ES_NODES=http://127.0.0.1:9200 jaegertracing/spark-dependencies

可以看到,我们仅仅需要指定对应的后端存储类型以及后端的服务地址即可。
同理,直接使用 Jar 包执行也是可以的。

  1. STORAGE=elasticsearch ES_NODES=http://127.0.0.1:9200 java -jar jaeger-spark-dependencies.jar

当然,有时我们在执行过程中,可能也会遇到一些问题,下面,我们来罗列一些我们曾经遇到过的问题以及解决方法:
问题一:java.lang.Exception: Yarn Local dirs can’t be empty
该问题其实是由于没有设置 LOCAL_DIRS 环境变量引起的,直接设置对应的环境变量即可:

  1. export LOCAL_DIRS=`pwd`

问题二:java.lang.IllegalArgumentException: System memory 464519168 must be at least 471859200. Please increase heap size using the —driver-memory option or spark.driver.memory in Spark configuration.
该问题其实是由于JVM的 Xms和Xmx 过小,不满足运行条件导致的,可以直接手动设置对应的相关配置。

  1. docker run --env STORAGE=elasticsearch --env JAVA_OPTS="-Xms30g -Xmx30g" --env ES_NODES=http://127.0.0.1:9200 jaegertracing/spark-dependencies

问题三:Exception in thread “main” java.lang.IllegalArgumentException
这种问题往往是 Java 版本不匹配导致的,切换成 OpenJDK-8 的Java版本即可。

构建完成的 DAG 图如下所示:
image.png