随着分布式应用越来越普遍,分布式应用需要依赖强大的可观测性设施来提供监控保障,强大的可观测性设施需要依赖高质量的遥测数据。虽然已经有许多开源或者商业供应商提供了遥测数据监测采集方案。但是在没有统一标准的情况下,采集的遥测数据兼容性差,维护监测客户端也给使用者带来沉重的负担。
Opentelemetry 可以为开发者们提供统一的,与第三方无关的遥测数据采集方案,以解决上述的各种问题。
源起
Opentelemetry 源于 OpenTracing 与 OpenCensus 两大开源社区的合并而来。OpenTracing 在 2016 年由 Ben Sigelman 发起,旨在解决开源 Tracing 实现重复开发监测客户端, 数据模型不统一, Tracing 后端兼容成本高的问题。OpenCensus 则是由 Google 内部实践而来,结合了 Tracing 和 Metrics 的监测客户端开源工具包。
由于两大开源社区各自的影响力都不小,而存在两个或多个 Tracing 的标准这个事情本身跟社区组建的主旨相违背。于是两大开源社区一拍即合,成立了 OpenTelemetry。
为什么需要
从使用者角度来看,接入 Tracing 监测客户端,对业务代码有一定入侵性。一旦接入了一个供应商的监测客户端,就很难切换到其他供应商提供的监测客户端。而从 Tracing 服务端供应商的角度来说,服务端除了要能够处理自己 Tracing 客户端的数据外,还需要兼容其他供应商 Tracing 客户端产生的数据,维护成本越来越高。尤其是在分布式应用逐渐普及的情况下,如文章开头所说, Opentelemetry 的价值更加明显。
Opentelemetry 项目组成
Opentelemetry 的项目主要分为四个部分内容
- 跨语言规范说明
- 收集、转换、转发遥测数据的工具 Collector
- 各语言监测客户端 API & SDK
- 自动监测客户端与第三方库 Instrumentation & Contrib
跨语言规范说明
规范说明包含话题内容比较广泛。其中有包含遥测客户端内部实现所需要的规范,也有包含遥测客户端与外部通信所需要实现的协议规范。
遥测客户端内部实现所需要的规范,如监测客户端基本架构、设计原则,遥测信号(Traces/ Metrics/ Logs)与辅助对象(Baggage/ Context/ Propagator)的概念与模型定义,实现遥测客户端需要实现的类与功能的设计等。这部分内容本文就不做详细介绍,可以在 specification/overview.md 以及相应对象文件夹下面的 datamodel.md/ api.md/ sdk.md 可以进行查阅。
遥测客户端与外部通信所需要实现的协议规范主要是指 OpenTelemetry Protocol (简称 OTLP)。OTLP 是 Opentelemetry 原生的遥测信号传递协议,虽然在 Opentelemetry 的项目中组件支持了 Zipkin v2 或 Jaeger Thrift 的协议格式的实现,但是都是以第三方贡献库的形式提供的。只有 OTLP 是 Opentelemetry 官方原生支持的格式。
OTLP 的数据模型定义是基于 ProtoBuf 完成的,如果你需要实现一套可以收集 OTLP 遥测数据的后端服务,那就需要了解里面的内容,对应可以参考代码仓库:
代码仓库:opentelemetry-proto
收集、转换、转发遥测数据的工具 Collector
在 Tracing 实践中有一个原则,遥测数据收集过程需要与业务逻辑处理正交。意思就是遥测数据收集并传递到遥测后端服务的过程不占用业务逻辑的信道/线程,尽量较少监测客户端对原有业务逻辑的影响。Collector 是基于这个原则实践的产物。
从架构层面来说,Collector 有两种模式。一种是把 Collector 部署在应用相同的主机内(如 K8S 的 DaemonSet),或者部署在应用相同的 Pod 里面 (如 K8S 中的 Sidecar),应用采集到的遥测数据,直接通过回环网络传递给 Collector。这种模式统称为 Agent 模式。
另一种模式是把 Collector 当作一个独立的中间件,应用把采集到的遥测数据往这个中间件里面传递。这种模式称之为 Gateway 模式。
两种模式既可以单独使用,也可以组合使用,只需要数据出口的数据协议格式跟数据入口的数据协议格式保持一致。
Opentelemetry Architecture
在 Collector 内部设计中,一套数据的流入、处理、流出的过程称为 pipeline。一个 pipeline 有三部分组件组合而成,它们分别是 receiver/ processor/ exporter。
- receiver
- 负责按照对应的协议格式监听接收遥测数据,并把数据转给一个或者多个 processor
- processor
- 负责做遥测数据加工处理,如丢弃数据,增加信息,转批处理等,并把数据传递给下一个 processor 或者传递给一个或多个 exporter
- exporter
- 负责把数据往下一个接收端发送(一般是遥测后端),exporter 可以定义同时从多个不同的 processor 中获取遥测数据
Collector Pipeline
从上面的设计可以看出,Collector 除了提供让遥测数据收集与业务逻辑处理正交的能力,还充当了遥测数据对接遥测后端的适配器。Collector 可以接收 otlp, zipkin, jaeger 等任意格式的数据,然后以 otlp, zipkin, jaeger 等任意格式的数据转发出去。这一切只取决于你需要输入或输出的格式是否有对应的 receiver 和 exporter 实现。 otlp 相关的实现都是在 opentelemetry-collector 仓库中。而 otlp 以外的协议实现,则是可以参考下面代码仓库。
各语言监测客户端 API & SDK
Opentelemetry 为每种语言提供了基础的监测客户端 API & SDK 包。这些包一般都是根据 opentelemetry-specification 里面的建议与定义,以及结合语言自身的特点,实现了在客户端采集遥测数据的基本能力。如元数据在服务间、进程间的传递,Trace 添加监测与数据导出,Metrics 指标的创建、使用及数据导出等。以下为各语言监测客户端 API & SDK 包对应的代码仓库表。
语言 |
仓库地址 |
---|---|
C++ |
opentelemetry-cpp |
.NET |
opentelemetry-dotnet |
Erlang |
opentelemetry-erlang-api & opentelemetry-erlang |
Golang |
opentelemetry-go |
Java |
opentelemetry-java |
javaScript |
opentelemetry-js-api & opentelemetry-js |
PHP |
opentelemetry-php |
Python |
opentelemetry-python |
Ruby |
opentelemetry-ruby |
Rust |
opentelemetry-rust |
Swift |
opentelemetry-swift |
按照 Opentelemetry 项目的规划,2021 年上半年大部分组件完成 Tracing 的支持。以目前的时间点(2021年12月)来看,C++/.NET/Golang/Java/Javascript/Python/Ruby 监测客户端对 Tracing 支持已经进入 Stable 状态。 Erlang/Rust/Swift 监测客户端对 Tracing 支持则是进入了 Beta 测试阶段。
而 Opentelemetry 项目规划对于 Mertics 的支持则晚一些。希望在 2021 年下半年大部分组件能够完成 Metrics 的支持。以目前情况来看,各语言客户端包对于 Metrics 支持还处在 Alpha 测试阶段。 而对于 Logs 的支持,则是计划在 2022 年开始。
Instrumentation & Contrib
如果单纯使用监测客户端 API & SDK 包,许多的操作是需要修改应用代码的。如添加 Tracing 监测点,记录字段信息,元数据在进程/服务间传递的装箱拆箱等。这种方式具有代码侵入性,不易解耦,而且操作成本高,增加用户使用门槛。这个时候就可以利用公共组件的设计模式或语言特性等来降低用户使用门槛。
利用公共组件的设计模式,例如在 Golang 的 Gin 组件,实现了 Middleware 责任链设计模式。我们可以引用 github.com/gin-gonic/gin 库,创建一个 otelgin.Middleware,手动添加到 Middleware 链中,实现 Gin 的快速监测。
利用语言特性的,例如 Java 使用 Java Agent 的能力与 bytebuddy 字节码织入技术,在 Java 应用启动之前找到对应类和方法,修改字节码注入监测,实现对指定类的自动监测。
理论上来说,快速监测依赖于客户端 API & SDK,自动监测则依赖于快速监测。但是实际操作却并没有按理论的来。如 Java 语言利用 Java Agent 与 bytebuddy 技术可以实现对指定开源组件实现全自动监测的,所以就没有单独提供快速监测(在 OpenTracing 里面是有分开的)。
语言 |
快速监测 |
自动监测 |
---|---|---|
Python |
opentelemetry-python-contrib |
opentelemetry-python-contrib |
javaScript |
opentelemetry-js-contrib |
opentelemetry-js-contrib |
Ruby |
opentelemetry-ruby |
opentelemetry-ruby |
Java |
(x) |
opentelemetry-java-instrumentation |
Golang |
opentelemetry-go-contrib |
(x) |
.NET |
opentelemetry-dotnet-contrib |
opentelemetry-dotnet-instrumentation |
Erlang |
opentelemetry-erlang-contrib |
|
Rust |
opentelemetry-rust |
|
Swift |
opentelemetry-swift |
总结
Opentelemetry 的使命是实现收集高质量、大范围、便携的遥测数据,让有效的可观测性设施成为可能。它本身并不提供完整的可观测性解决方案,而是提供了统一的遥测数据采集方案。而如果是需要搭建一套完整的可观测性设施,还需要搭配相应的监测后端做数据持久化与数据查询,如 Tracing 后端 zipkin/jaeger/tempo/,metrics 后端 prometheus,logs 后端 loki 等。