使用 OpenTelemetry 监测 Golang 应用

330 阅读7分钟

各位Gophers们,大家好,我是gogogo,欢迎大家关注我。

Go凭借其快速启动和最小编译功能,Go 正迅速成为面向未来的组织的 Web 编程语言。但要在分布式软件环境中充分发挥其优势,请集成 OpenTelemetry (OTel),这是一种全面的可观测性框架,可为遥测数据收集提供统一的方法。请继续阅读以了解如何在 Go 应用程序中完美地实现 OTel,并在 Golang 中实现 Otel 指标和在 Golang 中实现 Otel Tracer。

1 什么是 Golang 中的Telemetry?

Golang中的Telemetry是从 Go 应用程序收集和传输指标、日志、跟踪和相关元数据到可观测性后端,以便深入了解应用程序性能。

Telemetry数据包括请求延迟、错误率、资源利用率和吞吐量。 OpenTelemetry 提供对每个指标的遥测测量,以确保有效的资源利用率和应用程序性能优化,并最终改善用户体验。

2 在 Go 应用程序中实现 OpenTelemetry 的完整指南

首先,请检查以确保您的 Go 版本受 OTel 支持——1.16 或更高版本。

步骤 1:设置 Docker 环境

  1. 打开您的终端并为您的项目创建一个新目录。
mkdir otel-golang
cd otel-golang
  1. 创建docker-compose.yaml内容如下
version: '3'
   services:
     otel-collector:
       image: otel/opentelemetry-collector-dev:latest
       ports:
         - 4317:4317
         - 55680:55680
     app:
       build: .
       ports:
         - 8080:8080
  1. 创建Dockerfile
FROM golang:latest
WORKDIR /app
COPY . .
RUN go build -o main .
CMD ["./main"]
  1. 运行以下命令以启动收集器。
docker-compose up -d

步骤 2:安装 OpenTelemetry

通过运行以下命令,在您的 Go 环境中安装必要的 OpenTelemetry 库、导出器和跟踪包。

go get -u go.opentelemetry.io/otel
go get -u go.opentelemetry.io/otel/exporters/otlp
go get -u go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
go get -u go.opentelemetry.io/otel/trace

安装完成后,您可以使用 OpenTelemetry 开始检测您的 Go 应用程序。

步骤 3:检测应用程序

  1. 在根目录下,创建一个新文件 main.go,并添加以下 Otel Tracer 代码。
package main

import (
	"context"
	"fmt"
	"log"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/otlp"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
	"go.opentelemetry.io/otel/trace"
)

func main() {
	exporter, err := otlp.NewExporter(context.TODO(),
		otlp.WithInsecure(),
		otlp.WithEndpoint("http://otel-collector:4317"),
		otlp.WithHTTPClient(otlptracehttp.NewClient()))
	if err != nil {
		log.Fatalf("Failed to create exporter: %v", err)
	}
	defer exporter.Shutdown(context.Background())

	provider := otel.GetTracerProvider()
	tracer := provider.Tracer("example")

	trace.RegisterExporter(exporter)
	trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})

	ctx, span := tracer.Start(context.Background(), "sayHello")
	defer span.End()

	fmt.Println("Hello, OpenTelemetry!")
}
  1. 在终端中运行以下命令来构建应用程序。
docker-compose build
  1. 使用以下命令来启动应用程序容器。
docker-compose up
  1. 通过打开浏览器并导航到 http://localhost:8080 来访问应用程序。

您应该在控制台上看到消息;“Hello, OpenTelemetry!”。

导航到 http://localhost:55680 并打开 OpenTelemetry Collector 的 Web UI。从菜单中,单击“Services”,然后单击“app”服务以查看从您的应用程序收集的遥测数据。

3 如何使用 OpenTelemetry 监测 Go 应用程序

您可以使用基于库的检测方法,通过 OpenTelemetry 监测 Go 应用程序。让我们一起探索如何操作。

  1. 导入所需的包。
import (
    "context"
    "fmt"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
    "go.opentelemetry.io/otel/sdk/resource"
    "go.opentelemetry.io/otel/trace"
)
  1. 创建和配置exporter
exporter, err := otlp.NewExporter(context.TODO(),
    otlp.WithInsecure(),
    otlp.WithEndpoint("http://otel-collector:4317"),
    otlp.WithHTTPClient(otlptracehttp.NewClient()),
)
if err != nil {
    log.Fatalf("Failed to create exporter: %v", err)
}
defer exporter.Shutdown(context.Background())
  1. 初始化 exportertracer.
exporter, err := otlp.NewExporter(context.TODO(),
    otlp.WithInsecure(),
    otlp.WithEndpoint("http://otel-collector:4317"),
    otlp.WithHTTPClient(otlptracehttp.NewClient()))
if err != nil {
    log.Fatalf("Failed to create exporter: %v", err)
}
defer exporter.Shutdown(context.Background())

provider := otel.GetTracerProvider()
tracer := provider.Tracer("example")
  1. 设置全局 trace provider 并注册 exporter.
otel.SetTracerProvider(otel.NewTracerProvider(
    otel.TracerProviderOptions{
        Resource: resource.NewWithAttributes(
            resource.Attributes{
                "service.name": "my-service",
            },
        ),
        BatchExporter: exporter,
    },
))
  1. 启动一个span并执行检测操作。

ctx, span := otel.Tracer("my-component").Start(context.Background(), "my-operation")
defer span.End()

使用此方法,您可以收集各种遥测数据和元数据类型,包括指标(计数器、测量器和直方图)、日志、属性、跨度和跟踪,并将其导出为 YAML、JSON、CSV 或其他类型的文件。

4 Golang 中的分布式追踪

分布式追踪支持跨多个服务的请求跟踪。请按照以下步骤使用 OpenTelemetry 的上下文传播来实现 Go 应用程序中的分布式追踪和跨度关联。

  1. 导入分布式追踪所需的包
import (
    "context"
    "net/http"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
    "go.opentelemetry.io/otel/label"
    "go.opentelemetry.io/otel/propagation"
    "go.opentelemetry.io/otel/sdk/resource"
    "go.opentelemetry.io/otel/trace"
    "go.opentelemetry.io/otel/trace/tracerprovider"
    "go.opentelemetry.io/otel/otelhttp"
)
  1. 初始化trace providerexporter.
func initTracer() error {
       exporter, err := otlp.NewExporter(context.TODO(),
           otlp.WithInsecure(),
           otlp.WithEndpoint("http://otel-collector:4317"),
           otlp.WithHTTPClient(otlptracehttp.NewClient()),
       )
       if err != nil {
           return err
       }
      
       tp := tracerprovider.NewProvider(
           tracerprovider.WithBatcher(exporter),
           tracerprovider.WithResource(resource.NewWithAttributes(label.String("service.name", "my-service"))),
       )
      
       otel.SetTracerProvider(tp)
       otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
      
       return nil
   }
  1. 通过为每个操作创建 span 来检测应用程序。
func myHandler(w http.ResponseWriter, r *http.Request) {
       ctx := r.Context()
       tracer := otel.Tracer("my-component")
       ctx, span := tracer.Start(ctx, "my-operation")
       defer span.End()

       // 执行检测操作
       // ...

       span.AddEvent("my-event", trace.WithAttributes(label.String("key", "value")))
   }
  1. 使用 OpenTelemetry 自动检测来包装您的 HTTP 处理程序。
http.HandleFunc("/path", otelhttp.NewHandler(http.HandlerFunc(myHandler), "handler-name"))
  1. 设置 OpenTelemetry 中间件以捕获 HTTP 追踪并传播上下文。
func main() {
       // 初始化tracer provider and exporter
       err := initTracer()
       if err != nil {
           log.Fatalf("Failed to initialize tracer: %v", err)
       }

       // 设置 OpenTelemetry middleware 捕获 traces
       srv := &http.Server{
           Addr:    ":8080",
           Handler: otelhttp.NewHandler(http.DefaultServeMux, "http-server"),
       }

       // Start the server
       if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
           log.Fatalf("Failed to listen and serve: %v", err)
       }
   }

通过以下步骤,您可以追踪请求在不同应用程序组件中的流动,了解哪些服务正在运行以及它们是否以最佳状态运行。如果发现问题,Span 可以为修复提供足够的上下文。

5 可视化您的遥测数据

在收集您的 Go 遥测数据后,您必须在可观测性平台(在本例中为 Prometheus)中对其进行可视化。请按照以下步骤将数据从 OTel 导出到 Prometheus。

  1. 修改 docker-compose.yml 文件以添加 Prometheus 服务。
version: '3'

services:
  myapp:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - '8080:8080'

  jaeger:
    image: jaegertracing/all-in-one:latest
    ports:
      - '16686:16686'

  prometheus:
    image: prom/prometheus
    ports:
      - '9090:9090'
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
  1. 在你的项目目录中创建一个 prometheus.yml 文件并定义配置。
global:
  scrape_interval: 10s

scrape_configs:
  - job_name: 'myapp'
    static_configs:
      - targets: ['myapp:8080']
  1. 使用 OpenTelemetry Prometheus 导出器配置您的 Go 应用程序以公开指标,并导入必要的包。
import (
    "go.opentelemetry.io/otel/exporters/metric/prometheus"
    "go.opentelemetry.io/otel/metric"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/label"
    "go.opentelemetry.io/otel/sdk/metric/controller/push"
)
  1. 创建一个新的 Prometheus 导出器,并将其设置为 Otel Golang 指标的 OpenTelemetry 指标导出器。
promExporter, err := prometheus.NewExporter(prometheus.Options{})

// Set the Prometheus exporter as the metric exporter
if err == nil {
  pusher := push.New(
    promExporter,
    push.WithPeriod(1*time.Second),
  )
  controller := pusher.Controller()
  controller.Start()
  defer controller.Stop()

  metric.SetMeterProvider(pusher.Provider())
}
  1. 使用 OpenTelemetry API,用 Golang Otel 指标检测你的代码。
meter := otel.GetMeterProvider().Meter("myapp")

counter := metric.Must(meter).NewInt64Counter("requests_total")
counter.Add(ctx, 1, label.String("path", "/api/foo"))

通过访问 http://localhost:9090 导航到 Prometheus UI。在这里,您可以定义自定义查询并创建仪表板以可视化应用程序的指标。

6 在 Golang 中使用 OTel 的最佳实践

遵循以下最佳实践,以确保有效的检测和可观测性。

检测关键操作 识别软件中的关键操作,特别是那些可能减慢或阻碍最佳应用程序性能的操作。 重点关注捕获相关信息,例如延迟、错误率以及特定于您的应用程序的自定义属性。

上下文传播 OpenTelemetry 使用上下文传播将 span 与其父 span 相关联。 在执行同步和异步操作时,例如处理传入请求或调用外部服务,请在您的代码中包含上下文传播逻辑。

捕获相关属性并使用共享属性库 属性是键值对,可为跟踪、指标和日志提供上下文详细信息。 这些元数据能够更快地进行故障排除和问题修复。 使用共享属性库将有助于在各种服务之间实现遥测的一致性、标准化和可重用性。

实施采样 配置适当的采样策略(例如概率采样、速率限制或自定义采样)以控制生成的遥测数据量。 减少遥测数据生成有助于防止性能下降、成本升级和警报疲劳。

进行测试以确保检测 OpenTelemetry 的标准化库不会因过度资源消耗、延迟或其他问题而导致显着的开销或降低应用程序性能。 您还可以部署 CI/CD 流程来比较当前和以前的遥测数据,以跟踪应用程序服务的重大变化。

7 结论

本指南提供了在 Go 应用程序中检测 OpenTelemetry 以进行遥测收集和导出的分步指导。 随着应用程序变得越来越分布式,OpenTelemetry 提供了一个极好的机会来获得整个软件系统的可观测性,从而实现有效的故障排除和性能优化。