Skip to content

Jaeger & OpenTelemetry 链路追踪

链路追踪概念

用户请求
    │ TraceID: abc123

API Gateway (Span 1: 50ms)

    ├── User Service (Span 2: 10ms)
    │       └── DB Query (Span 3: 5ms)

    └── Order Service (Span 4: 30ms)
            ├── Inventory Service (Span 5: 8ms)
            └── Payment Service (Span 6: 15ms)

Jaeger 安装

bash
# 使用 Jaeger Operator
kubectl create namespace observability
kubectl apply -f https://github.com/jaegertracing/jaeger-operator/releases/download/v1.54.0/jaeger-operator.yaml -n observability

# 创建 Jaeger 实例
cat <<EOF | kubectl apply -f -
apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
  name: jaeger
  namespace: observability
spec:
  strategy: production
  storage:
    type: elasticsearch
    options:
      es:
        server-urls: https://elasticsearch:9200
  ingress:
    enabled: true
    hosts:
    - jaeger.example.com
EOF

OpenTelemetry Collector

yaml
# OTel Collector 配置
apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: otel-collector
spec:
  config: |
    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318

    processors:
      batch:
        timeout: 1s
        send_batch_size: 1024
      memory_limiter:
        limit_mib: 400

    exporters:
      jaeger:
        endpoint: jaeger-collector:14250
        tls:
          insecure: true
      prometheus:
        endpoint: "0.0.0.0:8889"
      logging:
        loglevel: debug

    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: [memory_limiter, batch]
          exporters: [jaeger]
        metrics:
          receivers: [otlp]
          processors: [batch]
          exporters: [prometheus]

Go 应用集成

go
package main

import (
    "context"
    "net/http"

    "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/propagation"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
)

func initOTel(ctx context.Context) func() {
    exporter, _ := otlptracegrpc.New(ctx,
        otlptracegrpc.WithEndpoint("otel-collector:4317"),
        otlptracegrpc.WithInsecure(),
    )

    res, _ := resource.New(ctx,
        resource.WithAttributes(
            semconv.ServiceName("my-service"),
            semconv.ServiceVersion("v1.0.0"),
            semconv.DeploymentEnvironment("production"),
        ),
    )

    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
        sdktrace.WithResource(res),
        sdktrace.WithSampler(sdktrace.TraceIDRatioBased(0.1)), // 采样 10%
    )

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

    return func() { tp.Shutdown(ctx) }
}

func main() {
    ctx := context.Background()
    shutdown := initOTel(ctx)
    defer shutdown()

    tracer := otel.Tracer("my-service")

    // 自动为 HTTP 处理器添加追踪
    handler := otelhttp.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ctx := r.Context()

        // 创建子 Span
        ctx, span := tracer.Start(ctx, "处理业务逻辑")
        defer span.End()

        span.SetAttributes(
            attribute.String("user.id", r.Header.Get("X-User-ID")),
        )

        // 调用下游服务(自动传播 TraceContext)
        callDownstream(ctx)

        w.WriteHeader(http.StatusOK)
    }), "my-handler")

    http.ListenAndServe(":8080", handler)
}

本站内容由 褚成志 整理编写,仅供学习参考