使用 OpenTelemetry 零代码修改接收 SkyWalking 追踪数据
2022-08-12经过半年的努力,我们向 OpenTelemetry 社区贡献了完整的 SkyWalking Receiver。从现在开始,使用 SkyWalking 探针的所有用户能够在不修改任何代码的情况下,丝滑的使用 OpenTelemetry 兼容的所有可观测性后端平台。
0x0: 什么是分布式追踪
2010年,Google 的一篇 Dapper 论文 1 开启了分布式追踪的序章。CNCF 的 OpenTracing 作为分布式追踪的标准协议,定义了一套厂商无关、语言无关的规范,也有 Jaeger 、Zipkin 等项目的实现和支持。随后 Google 和微软提出了 OpenCensus 项目,在定义分布式追踪协议的基础上,也规范了应用性能指标,并实现了一套标准的 API ,为可观测性能力统一奠定了基础。经过对已有的标准协议不停的打磨和演变,CNCF 提出了 OpenTelemetry,它结合了 OpenTracing 与 OpenCensus 两个项目,成为了一个厂商无关、平台无关的支撑可观测性三大支柱的标准协议和开源实现。另一方面,基于 Dapper 论文的思想,国内也有 SkyWalking 开源项目实现了分布式追踪,由于探针的无侵入性,SkyWalking 获得了大量的用户,并且有越来越多的贡献者推动着它的高速迭代。
以 Dapper 的定义作为基准,一个标准的分布式 Trace 示例如下图所示。一个 Trace
是由 Span
构成的有向无环图(DAG),Span 是一个最小粒度的调用
,既可以指代一个程序块执行,也可以指代一次 HTTP 等应用协议的远程调用。
trace example
0x1: 协议对比
目前在做分布式追踪的开源项目很多,接下来挑选几个社区活跃度高,且生产环境使用多的项目对比一下传输协议的差异,大致如下:
传输协议 | |
---|---|
OpenTelemetry | 使用 traceparent Header 传递 Trace,同时进入 w3c 协议 |
SkyWalking | 使用 sw8 Header 传递 Trace |
Jaeger | 使用 Uber-Trace-Id Header 传递 Trace |
Zipkin | 使用 x-b3-xxx Header 传递 Trace |
0x2: 代码解读
为了能兼容不同的分布式追踪实现,OpenTelemetry 提供了组件植入的方式让不同的厂商能够经由 OpenTelemetry 标准化数据处理后输出到不同的后端。Jaeger 与 Zipkin 在社区中实现了 JaegerReceiver、ZipkinReceiver。我们也为社区贡献了 SkyWalkingReceiver,并进行了持续的打磨,现在已经具备了在生产环境中使用的条件,而且无需修改任何一行业务代码。
OpenTelemetry 与 SkyWalking 有一些共同点:都是使用 Trace
来定义一次追踪,并使用 Span
来标记追踪里的最小粒度。但是在一些细节和实现上还是会有差别:
SkyWalking | OpenTelemetry | |
---|---|---|
数据结构 | Span -> Segment -> Trace |
Span -> Trace |
属性信息 | Tags |
Attributes |
应用事件 | Logs |
Events |
引用关系 | References |
Links |
明确了这些差异后,就可以开始实现将 SkyWalking Trace 2 转换为 OpenTelemetry Trace 3。代码实现见 GitHub 4 5 。主要工作包括:
- 如何构造 OpenTelemetry 的 TraceId 和 SpanId
- 如何构造 OpenTelemetry 的 ParentSpanId
- 如何在 OpenTelemetry Span 中保留 SkyWalking 的原始 TraceId、SegmentId、SpanId
首先我们来看如何构造 OpenTelemetry 的 TraceId 和 SpanId。SkyWalking 和 OpenTelemetry 都是通过 TraceId
串联起各个分布式服务调用,并通过 SpanId
来标记每一个 Span ,但是实现规格有较大差异:
数据 | OpenTelemetry | SkyWalking |
---|---|---|
TraceId | 32位的 Guid | 不定长字符串,不同语言的实现有差异 |
SegmentId | 没有此概念 |
不定长字符串,不同语言的实现有差异 |
SpanId | 16位 Guid | 在每个 Segment 中从 0 开始编码的数字 |
ParentSpanId | 16位 Guid | Segment 内部和 Segment 之间有差异 |
具体来讲,SkyWalking TraceId 和 SegmentId 所有可能的格式如下 6 :
SkyWalking TracId 示例
其中,在 OpenTelemetry 协议里,Span 在所有 Trace 中都是唯一的,而在 SkyWalking 中,Span 仅在每个 Segment 里是唯一的,这说明要通过 SegmentId 与 SpanId 结合才能在 SkyWalking 中对 Span 做唯一标识,并转换为 OpenTelemetry 的 SpanId。代码实现见 GitHub 7 。
接下来,我们来看如何构造 OpenTelemetry 的 ParentSpanId。在一个 Segment 内部,SkyWalking 的 ParentSpanId 字段可直接用于构造 OpenTelemetry 的 ParentSpanId 字段。但当一个 Trace 跨多个 Segment 时,SkyWalking 是通过 Reference 中的 ParentTraceSegmentId 和 ParentSpanId 表示的关联信息,于是此时需要通过 Reference 中的信息构建 OpenTelemetry 的 ParentSpanId。代码实现见 GitHub。
最后,我们来看如何在 OpenTelemetry Span 中保留 SkyWalking 的原始 TraceId、SegmentId、SpanId。我们携带这些原始信息是为了能将分布式追踪后端展现的 OpenTelemetry TraceId、SpanId 与应用程序日志中的 SkyWalking TraceId、SegmentId、SpanId 进行关联,打通追踪和日志。我们选择将 SkyWalking 中原有的 TraceId
、SegmentId
、ParentSegmentId
携带到 OpenTelemetry Attributes 中,代码实现见 GitHub 8。
经过上述一系列转换后,我们将 SkyWalking Segment Object 完整的转换为了 OpenTelmetry Trace,总结如下:
Trace 转换逻辑
0x3 部署 Demo
下面我们以一个 Demo 来展示使用 OpenTelemetry 收集、展示 SkyWalking 追踪数据的完整过程。
首先,在部署 OpenTelemetry Agent 之后,开启如下配置,即可在 OpenTelemetry 中拥有兼容 SkyWalking 协议的能力:
1 | # otel-agent config |
接下来需要将业务应用对接的 SkyWalking OAP Service(如:oap:11800
)修改为 OpenTelemetry Agent Service(如:otel-agent:11800
),就可以开始使用 OpenTelemetry 接收 SkyWalking 探针的追踪数据了。
我们以 SkyWalking-showcase Demo 为例展示整个效果。它使用 SkyWalking Agent 做追踪,通过 OpenTelemetry 标准化处理后使用 Jaeger 来呈现最终效果:
Jaeger UI结果
通过 SkyWalking Showcase 的架构图,可知 SkyWalking 的数据经过 OpenTelemetry 标准化后,依然完整。在这个 Trace 里,请求从 app/homepage 发起,之后在 app 同时发起两个请求 /rcmd/
与 /songs/top
,分发到 recommandation
/ songs
两个服务中,并最终到达数据库进行查询,从而完成整个请求链路。
SkyWalking Showcase
另外,我们也可从 Jaeger 页面中查看到原始 SkyWalking Id 信息,便于与应用日志关联:
携带信息分析
0x4: 作者简介
谭建:DaoCloud 可观测性技术专家,GitHub:@JaredTan95。DaoCloud 道客是云原生领域的创新领导者,成立于 2014 年底,拥有自主知识产权的核心技术,致力于打造开放的云操作系统为企业数字化转型赋能。产品能力覆盖云原生应用的开发、交付、运维全生命周期,并提供公有云、私有云和混合云等多种交付方式。
林嘉炜:云杉网络 DeepFlow 工程师,GitHub:@taloric。云杉网络成立于 2011 年,公司秉承“技术创造价值”的使命,为运营商和大型企业提供领先的云网络和云监控解决方案,满足云时代 IT 系统不断增长的自动化和可观测性需求。DeepFlow 是一个开源的高度自动化的可观测性平台,GitHub:https://github.com/deepflowio/deepflow
0x5: 引用
- [1] Dapper 论文
- [2] SkyWalking 协议
- [3] OpenTelemetry 协议
- [4] PR#8107
- [5] PR#8549
- [7] PR#11562
- [8] PR#12651