logo
logo

使用 OpenTelemetry 零代码修改接收 SkyWalking 追踪数据

谭建,林嘉炜 2022-08-12

经过半年的努力,我们向 OpenTelemetry 社区贡献了完整的 SkyWalking Receiver。从现在开始,使用 SkyWalking 探针的所有用户能够在不修改任何代码的情况下,丝滑的使用 OpenTelemetry 兼容的所有可观测性后端平台。

0x0: 什么是分布式追踪

2010年,Google 的一篇 Dapper 论文 1 开启了分布式追踪的序章。CNCF 的 OpenTracing 作为分布式追踪的标准协议,定义了一套厂商无关、语言无关的规范,也有 JaegerZipkin 等项目的实现和支持。随后 Google 和微软提出了 OpenCensus 项目,在定义分布式追踪协议的基础上,也规范了应用性能指标,并实现了一套标准的 API ,为可观测性能力统一奠定了基础。经过对已有的标准协议不停的打磨和演变,CNCF 提出了 OpenTelemetry,它结合了 OpenTracing 与 OpenCensus 两个项目,成为了一个厂商无关、平台无关的支撑可观测性三大支柱的标准协议和开源实现。另一方面,基于 Dapper 论文的思想,国内也有 SkyWalking 开源项目实现了分布式追踪,由于探针的无侵入性,SkyWalking 获得了大量的用户,并且有越来越多的贡献者推动着它的高速迭代。

以 Dapper 的定义作为基准,一个标准的分布式 Trace 示例如下图所示。一个 Trace 是由 Span 构成的有向无环图(DAG),Span 是一个最小粒度的调用,既可以指代一个程序块执行,也可以指代一次 HTTP 等应用协议的远程调用。

trace exampletrace 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 在社区中实现了 JaegerReceiverZipkinReceiver。我们也为社区贡献了 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 。主要工作包括:

  1. 如何构造 OpenTelemetry 的 TraceId 和 SpanId
  2. 如何构造 OpenTelemetry 的 ParentSpanId
  3. 如何在 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 示例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 中原有的 TraceIdSegmentIdParentSegmentId 携带到 OpenTelemetry Attributes 中,代码实现见 GitHub 8

经过上述一系列转换后,我们将 SkyWalking Segment Object 完整的转换为了 OpenTelmetry Trace,总结如下:

Trace 转换逻辑Trace 转换逻辑

0x3 部署 Demo

下面我们以一个 Demo 来展示使用 OpenTelemetry 收集、展示 SkyWalking 追踪数据的完整过程。

首先,在部署 OpenTelemetry Agent 之后,开启如下配置,即可在 OpenTelemetry 中拥有兼容 SkyWalking 协议的能力:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# otel-agent config
receivers:
# add the following config
skywalking:
protocols:
grpc:
endpoint: 0.0.0.0:11800 # 接收 SkyWalking Agent 上报的 Trace 数据
http:
endpoint: 0.0.0.0:12800 # 接收从前端/ nginx 等 HTTP 协议上报的 Trace 数据
service:
pipelines:
traces:
# add receiver `skywalking`
receivers: [skywalking]

# otel-agent service yaml
spec:
ports:
- name: sw-http
port: 12800
protocol: TCP
targetPort: 12800
- name: sw-grpc
port: 11800
protocol: TCP
targetPort: 11800

接下来需要将业务应用对接的 SkyWalking OAP Service(如:oap:11800)修改为 OpenTelemetry Agent Service(如:otel-agent:11800),就可以开始使用 OpenTelemetry 接收 SkyWalking 探针的追踪数据了。

我们以 SkyWalking-showcase Demo 为例展示整个效果。它使用 SkyWalking Agent 做追踪,通过 OpenTelemetry 标准化处理后使用 Jaeger 来呈现最终效果:

Jaeger UI结果Jaeger UI结果

通过 SkyWalking Showcase 的架构图,可知 SkyWalking 的数据经过 OpenTelemetry 标准化后,依然完整。在这个 Trace 里,请求从 app/homepage 发起,之后在 app 同时发起两个请求 /rcmd//songs/top ,分发到 recommandation / songs 两个服务中,并最终到达数据库进行查询,从而完成整个请求链路。

SkyWalking ShowcaseSkyWalking Showcase

另外,我们也可从 Jaeger 页面中查看到原始 SkyWalking Id 信息,便于与应用日志关联:

携带信息分析携带信息分析

0x4: 作者简介

谭建:DaoCloud 可观测性技术专家,GitHub:@JaredTan95。DaoCloud 道客是云原生领域的创新领导者,成立于 2014 年底,拥有自主知识产权的核心技术,致力于打造开放的云操作系统为企业数字化转型赋能。产品能力覆盖云原生应用的开发、交付、运维全生命周期,并提供公有云、私有云和混合云等多种交付方式。

林嘉炜:云杉网络 DeepFlow 工程师,GitHub:@taloric。云杉网络成立于 2011 年,公司秉承“技术创造价值”的使命,为运营商和大型企业提供领先的云网络和云监控解决方案,满足云时代 IT 系统不断增长的自动化和可观测性需求。DeepFlow 是一个开源的高度自动化的可观测性平台,GitHub:https://github.com/deepflowio/deepflow

0x5: 引用