一、先搞懂两个工具的核心作用,避免踩基础坑
很多开发者在做链路追踪时,会碰到Jaeger和OpenTelemetry(后面简称OTel)要配合的情况,先得把这俩工具的核心作用掰明白,不然连配合的逻辑都搞不清。 简单说,Jaeger是专门存、查链路追踪数据的“数据库+可视化工具”,就像你家里的相册,专门用来放拍好的照片(链路数据),还能按时间、事件翻找。而OTel是个“万能采集工具”,它能从你的代码、服务器、数据库里抓各种监控数据,包括链路追踪、日志、指标,抓完之后可以发给不同的地方,比如Jaeger、Prometheus、ELK这些。 很多人一开始会搞反逻辑:以为是Jaeger要适配OTel,其实是OTel要把自己抓的数据,改成Jaeger能认的格式再发过去——因为Jaeger有自己的“数据格式规则”,就像相册只认JPG,你拿PNG传进去,要么显示不出来,要么乱码。
二、核心问题:数据模型的映射规则,得讲透
这是配合时最容易出问题的地方,说白了就是OTel抓的链路数据,怎么转成Jaeger能认的格式。先给大家说清楚两个工具的链路数据核心字段,再讲怎么转。
2.1 两个工具的链路核心字段对比
先给大家列个直白的对比,没有复杂术语:
- Jaeger的核心字段:必须有“Trace ID”(整个链路的唯一编号,比如你买东西从选品到付款的全流程编号)、“Span ID”(链路里每个步骤的编号,比如选品是Span1,付款是Span2)、“Parent Span ID”(当前步骤的上一步编号,比如付款的上一步是选品)、“操作名”(这个步骤叫什么,比如“查询商品库存”)、“开始时间”“结束时间”、“标签”(给步骤加的备注,比如“商品ID=123”)、“日志”(步骤里的详细记录,比如“库存不足”)。
- OTel的核心字段:其实和Jaeger差不多,但命名有区别,比如OTel的“Trace ID”和Jaeger的完全一样,“Span ID”也一样,只是“Parent Span ID”在OTel里可能会被省略(如果是整个链路的第一个步骤,就没有上一步),另外OTel的“标签”叫“Attributes”,“日志”叫“Events”。
2.2 具体的映射规则,附代码示例
这里给大家用Python写一个完整的示例,因为Python的OTel SDK用起来简单,适合演示。先明确这个示例的技术栈:Python 3.9+、OTel Python SDK、Jaeger。 首先,你得先装需要的包:
# 装OTel的核心包和Jaeger导出器
pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-jaeger
然后写一个完整的代码,演示OTel抓的链路数据怎么转成Jaeger能认的格式,每个步骤都有注释:
# 导入OTel的核心模块
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
# 导入Jaeger的导出器,这个是专门用来把OTel数据转成Jaeger格式的
from opentelemetry.exporter.jaeger import JaegerExporter
# 第一步:配置OTel的Tracer(链路追踪器)
# 给这个服务起个名字,Jaeger里会显示这个服务名,方便你找
trace.set_tracer_provider(TracerProvider(service_name="demo-shop-service"))
tracer = trace.get_tracer(__name__)
# 第二步:配置Jaeger导出器,核心就是这里做数据映射
# 这里的配置参数都是OTel帮我们处理的,不用自己手动改字段,只要配对就行
jaeger_exporter = JaegerExporter(
# Jaeger的地址,如果你是本地装的Jaeger,就是这个地址
agent_host_name="localhost",
# Jaeger的端口,默认是6831,这个是Jaeger的标准端口
agent_port=6831,
# 可选:如果要加一些全局的标签,比如环境是测试还是生产
tags={"environment": "test", "version": "1.0.0"}
)
# 第三步:把导出器加到OTel的处理器里,OTel会自动把数据转成Jaeger格式
span_processor = BatchSpanProcessor(jaeger_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)
# 第四步:模拟一个完整的链路,演示数据流转
# 整个链路的第一个步骤,没有父Span
with tracer.start_as_current_span("查询商品列表") as span:
# 给这个Span加OTel的Attributes(对应Jaeger的标签)
span.set_attribute("request_type", "GET")
span.set_attribute("page_size", 10)
# 加OTel的Events(对应Jaeger的日志)
span.add_event("开始查询商品数据库", {"database": "mysql"})
span.add_event("查询完成", {"item_count": 8})
# 第二个步骤,是第一个步骤的子步骤,有父Span
with tracer.start_as_current_span("查询商品库存") as sub_span:
sub_span.set_attribute("product_id", 123)
sub_span.add_event("检查库存", {"stock": 5})
sub_span.add_event("库存足够", {"stock": 5})
# 第三个步骤,也是第一个步骤的子步骤
with tracer.start_as_current_span("生成商品图片链接") as sub_span:
sub_span.set_attribute("image_size", "large")
sub_span.add_event("生成链接完成", {"url": "https://example.com/img/123.jpg"})
# 第五步:关闭导出器,确保所有数据都发出去
trace.get_tracer_provider().shutdown()
很多人会问:这个代码里,OTel是怎么把自己的字段转成Jaeger的?其实是Jaeger导出器(就是JaegerExporter)帮我们做了映射:
- OTel的
service_name直接转成Jaeger的服务名; - OTel的
Trace ID``Span ID``Parent Span ID完全原封不动转过去; - OTel的
Attributes转成Jaeger的Tags; - OTel的
Events转成Jaeger的Logs; - OTel的开始时间、结束时间转成Jaeger的时间格式(因为两个工具的时间精度要求一样,都是微秒级,所以直接转)。
2.3 容易踩的映射坑
这里说两个最常见的坑,很多人都碰到过:
第一个坑:时间格式的问题。有些旧版本的OTel SDK或者Jaeger,对时间的精度要求不一样,比如Jaeger要求时间是微秒(1秒=1000000微秒),如果OTel转的时候转成了毫秒(1秒=1000毫秒),那Jaeger里显示的时间就会乱,比如一个步骤明明用了1秒,结果显示成1000秒。这个坑现在新版本的工具已经解决了,但如果你用的是2020年之前的版本,一定要注意。
第二个坑:标签的类型问题。Jaeger的标签只支持字符串、数字、布尔值这三种类型,如果你OTel的Attributes里加了一个列表类型的标签,比如span.set_attribute("product_ids", [123,456,789]),那Jaeger导出器会直接把这个标签丢了,不会报错,你在Jaeger里根本找不到这个标签。这个坑很多人碰到,因为列表在OTel里是合法的,但Jaeger不支持。
三、兼容性问题:不同版本的工具怎么适配
兼容性问题说白了就是:你用的OTel版本和Jaeger版本能不能配合,版本不对的话,要么数据发不出去,要么显示乱码。
3.1 版本兼容的核心逻辑
Jaeger的版本从1.0开始,对数据格式的支持有三个阶段:
- 1.0到1.15版本:只支持自己的旧格式,不支持OTel的格式,这个阶段你要配合的话,必须用OTel的“Jaeger旧格式导出器”;
- 1.16到1.20版本:开始支持OTel的格式,但需要手动开启配置,比如要在Jaeger的配置文件里加
enable_otlp=true; - 1.21及以上版本:默认支持OTel的格式,不需要额外配置。 而OTel的版本,从0.14开始,就支持Jaeger的新格式导出器了,所以只要你用的是2021年之后的OTel版本,基本都能适配。
3.2 版本适配的具体示例
比如你现在用的是Jaeger 1.18版本,OTel 1.10版本,那配置的时候就要注意:
首先,Jaeger的配置文件(jaeger-config.yml)里要加OTLP的配置:
# Jaeger 1.18版本的配置,开启OTLP支持
otlp:
enabled: true
grpc:
enabled: true
port: 4317
http:
enabled: true
port: 4318
然后,OTel的导出器要改成OTLP格式的,因为Jaeger 1.18支持OTLP,用OTLP的导出器比旧的Jaeger导出器更稳定:
# 导入OTLP的导出器,专门用来发OTLP格式的数据
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
# 配置OTLP导出器,地址是Jaeger的OTLP端口
otlp_exporter = OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
# 加到处理器里
span_processor = BatchSpanProcessor(otlp_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)
如果你的Jaeger是1.15版本,那就要用旧的Jaeger导出器,配置和之前的第一个示例一样,但要注意OTel的版本不能太高,OTel 1.0以下的版本才支持旧的Jaeger导出器。
3.3 兼容性问题的排查方法
如果你碰到数据发不出去或者显示乱码,按这个步骤排查:
第一步:先看Jaeger的日志,比如Jaeger启动的时候,有没有报错,比如“unknown format”“invalid time”之类的;
第二步:看OTel的日志,OTel的导出器有没有报错,比如“connection refused”“timeout”之类的;
第三步:检查版本,用jaeger --version看Jaeger的版本,用pip show opentelemetry-exporter-jaeger看OTel导出器的版本,然后对应版本的适配规则;
第四步:检查配置,比如Jaeger的端口有没有开,OTel的地址有没有写对,比如Jaeger的OTLP端口是4317(gRPC)或者4318(HTTP),旧的Jaeger导出器用的是6831,不要搞混。
四、应用场景、优缺点和注意事项
4.1 应用场景
这个配合方式适合什么场景呢? 第一个场景:旧系统已经用了Jaeger,现在要做监控升级,不想换链路追踪工具,用OTel来采集数据,这样既保留了Jaeger的可视化能力,又能享受OTel的采集方便性; 第二个场景:新系统要做全链路监控,需要同时采集链路、日志、指标,用OTel采集后,把链路数据发给Jaeger,日志发给ELK,指标发给Prometheus,这样统一采集,分散存储; 第三个场景:微服务架构的链路追踪,每个服务用OTel采集自己的链路数据,统一发给Jaeger,Jaeger能把跨服务的链路连起来,方便排查问题。
4.2 技术优缺点
优点:
- 保留了Jaeger的可视化能力,Jaeger的链路图很直观,很多开发者已经习惯用Jaeger;
- OTel的采集能力强,支持多种语言、多种框架,不用自己写采集代码;
- 统一采集,分散存储,灵活度高,比如你可以随时把链路数据从Jaeger换成其他工具,只要改OTel的导出器就行;
- 成本低,Jaeger和OTel都是开源免费的,不用花钱买商业工具。
缺点:
- 版本适配麻烦,不同版本的工具配合需要改配置,容易踩坑;
- 数据映射可能有问题,比如标签类型不支持、时间格式不对,导致数据丢失或者乱码;
- 性能问题,如果采集的数据量很大,OTel的导出器可能会有延迟,需要调整导出器的配置,比如批量导出的大小、间隔时间;
- 调试麻烦,数据从OTel到Jaeger经过了一层映射,出问题的时候,很难快速定位是OTel的问题还是Jaeger的问题。
4.3 注意事项
这里给大家列几个一定要注意的点:
- 版本尽量用新的,只要你的系统能支持,尽量用Jaeger 1.21以上,OTel 1.0以上,这样能避免很多兼容性问题;
- 配置端口的时候一定要分清楚,旧的Jaeger导出器用6831,OTLP的gRPC用4317,HTTP用4318,不要搞混;
- 标签的类型一定要符合要求,Jaeger只支持字符串、数字、布尔值,不要加列表、字典类型的标签;
- 批量导出的配置要合理,比如OTel的BatchSpanProcessor,默认的批量大小是100,间隔是5秒,如果你的数据量很大,可以把批量大小改成1000,间隔改成1秒,这样能减少延迟,但要注意不要超过Jaeger的处理能力;
- 一定要测试,在上线之前,先在测试环境测试一遍,确保数据能正常发过去,Jaeger里能正常显示,标签、日志都对,时间也对。
五、总结
Jaeger和OTel配合的核心问题就是数据模型映射和兼容性,其实只要搞清楚两个工具的作用,掌握映射规则,注意版本适配,就能顺利配合。 很多人觉得这个配合复杂,其实是因为一开始搞反了逻辑,以为是Jaeger要适配OTel,其实是OTel要把数据转成Jaeger能认的格式,只要选对导出器,配对版本,大部分问题都能解决。 另外,碰到问题的时候,不要急着换工具,先按排查步骤来,先看日志,再查版本,再改配置,大部分问题都能解决。
评论
围绕“Jaeger后端对接OpenTelemetry时的数据模型映射与兼容性问题”参与讨论