Opentelmetry(2): 【内部分享】 从入门到精通

建议点击 查看原文 查看最新内容。

原文链接: https://typonotes.com/posts/2023/08/29/opentelmetry-introduce-techsharing/

01. 我们为什么需要做链路追踪

服务逻辑复杂调用链条过长 , 甚至夸多个部门协作时。 一个请求 从被接受到应答 中间过程就是个 黑盒, 如果出现不稳定的情况, 例如 响应慢, 相应错误 的时候, 排查起来效率低下, 甚至无法排查。

如果想要解决这个问题, 就需要完成 请求链路染色。 这时候我们就需要一个 TraceID 来实现。 TraceID 每次请求都不同, 且全局唯一的

当完成链路染色之后, 又带入了新的问题: 并不知道 服务之间的调用关系。 于是又引入了 SpanIDParentSpanID, 从名字可以看出 这是一对父子关系。 从而有逻辑的将服务进行了层级串联。

02. W3C - Trace Context 标准

为了规范厂商, 实现平台通用。 于是 w3c 出台了 Trace Context 标准。

标准规定:

  1. 使用 traceparent HTTP Header 作为请求传递的标准媒介。
  2. traceparent 遵守格式 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
    1. Version-TraceID-SpanID-Flag
    2. Version: 保留字段, 当前值只有 00
    3. TraceID: 每次请求 全局唯一, 串联所有方法或服务。
    4. SpanID: 每次方法调用 唯一, 串联相邻的两个方法或服务。
    5. Flag: 标示位。
  3. tracestate 表示状态, 携带一些 额外的 vendor 信息, 以便在不同的 tracing system 中使用/兼容。
    1. rojo=00f067aa0ba902b7,congo=t61rcWkgMzE

03. OpenTelemetry

OpenTelemetry 就是实现 W3C Trace Context 标准的一个 库(Library) 。 地位可以说是 链路追踪届 龙头、一哥、扛把子

  1. OpenTelemetry(otel) 是 CNCF 项目。 背景实力强, 基于云原生, 受欢迎
  2. otel 是一个实现 可观测性框架和工具库支持多种语言
  3. otel 是一个信息生产者, 可以对接 多种后端平台, 高兼容性。

Span 是 OpenTelemetry 中一个重要概念。

  1. Span 包含之前提到的 TraceID, SpanID
  2. 还包含了每次 埋点 的其他信息, 例如 事件、属性、状态、错误、日志 等。
  3. 每个 Span 数据都是独立的。 多个 Span 的关系通过 SpanIDParentSpanID 串联。

在 otel 使用过程中, 有以下几个核心 API, 我将其分为了三组

  1. 资源管理: 创建/调用 Tracer, Span
  2. 信息埋点: Attributes, Event, Status, Error/Exception
  3. 信息传递: Propagator 生成/管理 跨服务/平台 的 Header 内容。

04. Propagation 广播/传递

  1. 当服务(app1, app2) 接受请求的时候, 需要解析是否具有 traceparent header
    1. 有: 从 header 中提取相关 trace 信息。
    2. 无: 初始化 trace 信息。
  2. 当服务内部传递时, 通过上下文传递(例如 golang context.Context)
  3. 当服务之间传递时, 遵守 w3c 规则, 使用 traceparent 传递
  4. 应答用户的时候将 TraceID 返回给用户, 以便处理用户反馈。

05. 生产实战 - 使用 OTEL-Collector-Contrib 转发消息

在生产中, 为了方便转移, 并没有直接将 OpenTelemery 对接到 Jaeger/Prometheus 等平台。

而是使用了一个中间件 OTEL Collector Contrib 进行转发。 如此只需要修改 contrib 配置, 就可以在不修改任何服务的情况下, 对接任意后端平台。

  1. https://github.com/open-telemetry/opentelemetry-collector
  2. https://github.com/open-telemetry/opentelemetry-collector-contrib

06. Demo: Golang + gonic/gin 接入案例

这里我自己通过 Golang + gin 实现了 OpenTelemetry 的接入。

代码已经放在 Github 上, 有完整的实现思路

  • 1. 使用 Otel-Collect-Contrib 初始化 trace.Provider
  • 2. 使用 otelgin 接入 trace.Provider
  • 3. 完成单服务的 Trace 树状结构
  • 4. 应答客户端时, 在 Header 中默认添加 TraceID
  • 5. 获取前方传递的 traceparent 信息
    1. 使用 otelgin.WithPropagators(pptc)
  • 6. 向后传递 Header: traceparent
    1. 这个应该从 context 中获取添加到 HTTP Client 请求的 Header 中。
    2. 而非 添加到 Response Header 中。
  • 7. 在 Trace 中添加 Error Log, Status, Attr
    1. span.RecordError 提交错误日志
    2. span.SetStatus 设置 trace span 状态。 氛围 error 和 ok
    3. span.SetAttributes 设置属性,可以通过属性搜索。 (所有属性被 索引)。
  • 8. 修改 Trace 中的 Kind 类型。 已知 Otelngin 提供的值为 Sever, 默认的值为 internal
    1. (*) Kind 是标准字段, 是枚举类型。 其中包含 internal, server, client, producer, consumer 可以在代码中看到。
    2. 可以通过 trace.WithSpanKind, 在 trace.Start 时作为 opt 传入。 之后不能通过 span 设置。
  • 9. 添加自定义属性字段
    1. (*) 自定义字段(Attribute)(类似 host).
    2. 每个 span 都是独立的。 因此 public attributes 需要在公共函数中注入 utils/span.go
    3. 因此使用 Context 进行传递, 在不同的 方法/函数 内进行公共 attr 共享。

07. 结果展示

根据后端平台不同, 展示效果略有差异