用 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 为中心进行组织,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 是一套用 yaml 文档描述的属性,每种实现语言需要根据描述文档生成对应的常量或者枚举。 Go 语言实现的 Semantic Conventions 位于 opentelemetry-go 的 semconv 目录。

此外,还有一系列的 instrumentation libraries。它们是针对特定的开发框架提供的 sdk。 在 OpenTelemetry Registry 可以查找已经支持的框架,比如适用于 grpc 的配套代码:instrumentation/google.golang.org/grpc/

基础使用

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

推荐阅读

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

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