用 OpenTelemetry 开发可观测的软件系统(Observability)

Tags: 软件工程 

目录

说明

OpenTelemetry 项目在 Observability Primer 中介绍可观测的定义(Observability)。简单来说就是软件系统要向外暴露足够的信息。暴露的数据不仅反映出系统的运行状态,而且足以用来定位故障原因。有一系列工具可以被用来实现软件系统的可观测。关于 OpenTelemetry、OpenTracing、OpenCensus 的关系见:OpenTelemetry 介绍

OpenTelemetry 是什么?

OpenTelemetry 是一系列系统观测相关的 api、sdk 和工具。它定义采集测量数据的方法框架,并提供了 Golang/Java/JavaScript 等十多种语言的实现,以及支持对接几十种第三方系统。开发者用 OpenTelemetry 提供的 API 可以直接生成测量数据,并导入到选用的数据系统中。测量数据目前有 metrics、trace 和 log 三类。

OpenTelemetry 支持将测量数据以多种格式输出,可以按照 OpenTelemetry Protocol(简称 otlp) 格式输出。或者以第三方系统支持的格式输出,比如 Prometheus 使用的 metrics 数据格式,Jaeger 使用的 tracer 数据格式。通过切换 exporter 改变数据的输出格式。

OpenTelemetry 同时提供了一系列配套代码库,适配各种开发框架。在开发框架中引入对应的代码库中方法,就可以生成测量数据。

OpenTelemetry 架构

OpenTelemetry Client Architecture 介绍了整体架构。

  • 以 signal 为中心进行组织同类型的观测数据,比如 Tracing、Metricing、Loging 是三个不同的 signal;
  • signal 共用一套 context propagation 机制;
  • signal 由四部分组成: api、sdk、Semantic Conventions、Contrib Packages。

signal 的构成:

  • api: 最基础的接口定义
  • sdk: api 实现以及额外的接口
  • Semantic Conventions:常用的属性名称和属性数值命名约定
  • Contrib Packages: 未纳入 sdk 的可选代码

每种语言单独实现,比如 Go 语言实现包括下面的项目:otel go

Semantic Conventions

Semantic Conventions 是一套命名约定,目的为了将观测数据中的名称统一,定义了各种场景里 metrics 名称、通用的属性名称等。在项目 Semantic Conventions 中用 yaml 文件的方式维护,页面 OpenTelemetry Semantic Conventions 1.25.0 中进行了分类展示。

每种实现语言需要根据描述文档生成对应的常量或者枚举

Go 的实现中对于通用的属性提供了function,可以直接用来创举属性的键值对,比如下面的 semconv.ServerAddress()。metrics 名称等则是用常量定义的。

counter.Add(ctx, 1, metric.WithAttributes(
    semconv.ServerAddress(serverAddr),
    semconv.ClientAddress(clientAddr),
    semconv.RPCMethod(method),
))

instrumentation libraries

otel 还包括一系列的 instrumentation libraries。它们是针对特定的开发框架提供的 sdk,可以在 OpenTelemetry Registry 中查找相关项目。

基础使用

Tracing、Metricing 和 Logging 数据生成需要分别引用不同的代码包。它们是独立声明的 go package,虽然都位于一个 repo。

api 定义分别是下面的三个 package:

"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/log"

还有各自的 sdk 代码包:

sdktrace "go.opentelemetry.io/otel/sdk/trace"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
sdklog "go.opentelemetry.io/otel/sdk/log"

三者的 api 和 sdk 使用方式是类似的:

  • 生成一个 sdk 中定义的 exporter
  • 用上一步的 exporter 生成 api 中定义的 tracer/meter/logger
  • 用上一步的 tracer/meter/logger 的各自的方法生成观测数据

以 tracing 为例,InitTrace 中创建了 exporter,然后生成 tracerProvider 和 tracer。在 DemoDepth0 中用 tracer.Start() 生成 trace 数据。

import (
    "context"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
    "go.opentelemetry.io/otel/propagation"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    "go.opentelemetry.io/otel/trace"
)

func InitTrace(ctx context.Context, name string) (*sdktrace.TracerProvider, trace.Tracer, error) {
   exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
   if err != nil {
      return nil, nil, err
   }
   tracerProvider := sdktrace.NewTracerProvider(
      sdktrace.WithBatcher(exporter),
   )

   otel.SetTracerProvider(tracerProvider)
   otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))

   tracer :=  otel.GetTracerProvider().Tracer(name)

   return tracerProvider, tracer, nil
}

func DemoDepth0(ctx context.Context, tracer trace.Tracer) {
   ctx, span := tracer.Start(ctx, "DemoDepth1")
   defer span.End()
   DemoDepth1(ctx, tracer)
}

Exporter

OpenTelemetry 通过内置的 Exporter 将数据转换成各类第三方系统能够识别的格式。opentelemetry-go/example 给出了一些例子。 其中 example/otel-collector 使用了一个配套工具 opentelemetry-collector-contrib,该工具可以将 otlp 格式的数据转换成其它格式。

Exporter 的使用方式是相似的:

  • 调用对应的 New() 创建 expoter
  • 调用 sdk 中的方法创建使用 expoter 的 provider
  • 注册 provider
  • 随时可以用 otel.GetTracerProvider().Trace() 创建的 tracer 生成 trace 数据
func TestExporterStdout(t *testing.T) {
    ctx := context.Background()

    exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
    if err != nil {
        t.Fatalf("create exporter stdout fail: %s", err.Error())
    }
    tracerProvider := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
    )
    defer tracerProvider.Shutdown(ctx)

    otel.SetTracerProvider(tracerProvider)
    otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))

    tracer := otel.GetTracerProvider().Tracer("stdout demo")

    DemoDepth0(ctx, tracer)
}

使用 tracer 生成 trace 数据:

func DemoDepth0(ctx context.Context, tracer trace.Tracer) {
   ctx, span := otel.GetTracerProvider().Tracer("demo").Start(ctx, "DemoDepth1")
   defer span.End()
   DemoDepth1(ctx, tracer)
}

func DemoDepth1(ctx context.Context, tracer trace.Tracer) {
   ctx, span := otel.GetTracerProvider().Tracer("demo").Start(ctx, "DemoDepth2")
   defer span.End()
   DemoDepth2(ctx, tracer)
}

func DemoDepth2(ctx context.Context, trace trace.Tracer) {
}

在 grpc 中使用 OpenTelemetry

例子:instrumentation/google.golang.org/grpc/otelgrpc/example

expoter 的选择以及 provider 的注册需要自行编写,方法同 stdout exporter 的使用。

var (
    tracerProvider *sdktrace.TracerProvider
)

func InitTrace(ctx context.Context, name string) error {
    exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
    if err != nil {
        return err
    }
    tracerProvider = sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
    )

    otel.SetTracerProvider(tracerProvider)
    otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))

    return nil
}

func StopTrace(ctx context.Context) error {
    return tracerProvider.Shutdown(ctx)
}

client 端创建连接时添加 grpc.WithStatsHandler(otelgrpc.NewClientHandler()) 后,就会自动为所有 rpc 请求添加 trace 信息。

    conn, err := grpc.NewClient(ADDR,
        grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
    )

server 端创建 server 时添加 grpc.StatsHandler(otelgrpc.NewServerHandler()) 后,就会自动从请求头中解析出 trace 信息。如果请求头中没有 trace 信息,就初始生成。

    s := grpc.NewServer(
        grpc.StatsHandler(otelgrpc.NewServerHandler()),
    )

参考

  1. 李佶澳的博客
  2. Observability Primer
  3. What is OpenTelemetry?
  4. Jaeger: open source, distributed tracing platform
  5. Prometheus:From metrics to insight
  6. exporters
  7. example/otel-collector
  8. exporters/otlp
  9. opentelemetry-proto
  10. opentelemetry-collector-contrib
  11. exporters/stdout
  12. opentelemetry-go/example
  13. Using instrumentation libraries
  14. opentelemetry-go
  15. OpenTelemetry Registry
  16. instrumentation/google.golang.org/grpc/
  17. instrumentation/google.golang.org/grpc/otelgrpc/example
  18. example/otel-collector
  19. OpenTelemetry介绍
  20. OpenTelemetry Client Architecture
  21. otel go
  22. OpenTelemetry Semantic Conventions
  23. opentelemetry-go-contrib
  24. OpenTelemetry Semantic Conventions 1.25.0
  25. v1.26.0/semconv

推荐阅读

Copyright @2011-2019 All rights reserved. 转载请添加原文连接,合作请加微信lijiaocn或者发送邮件: [email protected],备注网站合作

友情链接:  系统软件  程序语言  运营经验  水库文集  网络课程  微信网文  发现知识星球