从零搭建分布式链路追踪系统:OpenTelemetry实战与Jaeger集成

从零搭建分布式链路追踪系统:OpenTelemetry实战与Jaeger集成

引言

在微服务架构中,一个请求往往跨越多个服务,定位性能瓶颈和错误变得异常困难。分布式链路追踪系统应运而生,它能记录请求在各服务间的完整调用链路。本文将带你从零开始,使用OpenTelemetry标准采集追踪数据,并集成Jaeger进行可视化展示。无论你是运维还是开发者,都能通过本文快速搭建一套生产级的链路追踪体系。

Distributed Tracing Overview

1. 核心概念

  • Trace:代表一个请求的完整调用链,由多个Span组成。
  • Span:调用链中的单个操作单元,包含开始时间、结束时间、标签等信息。
  • Trace Context:用于在服务间传递Trace ID和Span ID,保证链路连续性。

OpenTelemetry是CNCF的观测性标准,提供统一的API和SDK,支持多种后端(如Jaeger、Zipkin)。

2. 环境准备

2.1 安装Jaeger

使用Docker快速启动Jaeger all-in-one实例:

docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 4317:4317 \
  -p 4318:4318 \
  -p 14250:14250 \
  -p 14268:14268 \
  -p 14269:14269 \
  -p 9411:9411 \
  jaegertracing/all-in-one:1.50

访问 http://localhost:16686 即可打开Jaeger UI。

2.2 开发语言与SDK

本文以Go语言为例,其他语言类似。确保Go 1.18+。

3. Go应用集成OpenTelemetry

3.1 添加依赖

go get go.opentelemetry.io/otel \
       go.opentelemetry.io/otel/exporters/jaeger \
       go.opentelemetry.io/otel/sdk/trace \
       go.opentelemetry.io/otel/sdk/resource

3.2 初始化TracerProvider

创建一个 tracer.go 文件:

package main

import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/exporters/jaeger"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
)

func initTracer() (*sdktrace.TracerProvider, error) {
    exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces")))
    if err != nil {
        return nil, err
    }

    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exp),
        sdktrace.WithResource(resource.NewWithAttributes(
            semconv.SchemaURL,
            semconv.ServiceNameKey.String("my-service"),
            attribute.String("environment", "development"),
        )),
    )
    otel.SetTracerProvider(tp)
    return tp, nil
}

3.3 创建Span

假设我们有一个HTTP处理函数:

func handleRequest(w http.ResponseWriter, r *http.Request) {
    tracer := otel.Tracer("my-app")
    ctx, span := tracer.Start(r.Context(), "handleRequest")
    defer span.End()

    // 模拟业务逻辑
    doWork(ctx)

    span.SetAttributes(attribute.String("http.method", r.Method))
    w.Write([]byte("Hello, OpenTelemetry!"))
}

func doWork(ctx context.Context) {
    _, span := otel.Tracer("my-app").Start(ctx, "doWork")
    defer span.End()
    // 模拟耗时操作
    span.AddEvent("work started")
    // ...
    span.AddEvent("work finished")
}

3.4 传播Context

在HTTP客户端调用时,需要将trace上下文通过HTTP头传递:

import (
    "net/http"
    "go.opentelemetry.io/otel/propagation"
)

func callAnotherService(ctx context.Context) error {
    req, _ := http.NewRequestWithContext(ctx, "GET", "http://other-service/api", nil)
    otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(req.Header))
    client := http.Client{}
    resp, err := client.Do(req)
    // ...
}

4. 配置与启动

main.go 中调用初始化:

func main() {
    tp, err := initTracer()
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        if err := tp.Shutdown(context.Background()); err != nil {
            log.Fatal(err)
        }
    }()

    http.HandleFunc("/", handleRequest)
    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

运行程序,然后访问 http://localhost:8080,再打开Jaeger UI即可看到trace。

5. 验证与排查

在Jaeger UI中可以搜索服务、查看Span详情、火焰图等。如果没看到数据,检查:

  • Jaeger collector地址是否正确?
  • 网络是否可达?
  • 采样率是否为1(默认是1/1000)?

6. 高级用法

6.1 采样策略

可以配置基于头部的采样或概率采样:

import "go.opentelemetry.io/otel/sdk/trace"

tp := sdktrace.NewTracerProvider(
    sdktrace.WithSampler(sdktrace.AlwaysSample()),
)

6.2 Baggage传递

通过Context传递自定义键值对:

import "go.opentelemetry.io/otel/baggage"

member, _ := baggage.NewMember("user", "alice")
bag, _ := baggage.New(member)
ctx := baggage.ContextWithBaggage(ctx, bag)

总结

通过本文,你已从零搭建了一套基于OpenTelemetry和Jaeger的分布式链路追踪系统。从概念到代码,再到验证,每一步都清晰的实践。这套方案兼容性强、社区活跃,适合生产环境。下一步可以探索多语言集成、自定义采样以及与其他后端(如Prometheus)的协同。

觉得内容不错?我要

评论 暂无评论
暂无评论,快来抢沙发吧~