OTel微服务链路追踪解决方案

介绍

OpenTelemetry 是 CNCF 的一个可观测性项目,旨在提供可观测性领域的标准化方案,解决观测数据的数据模型、采集、处理、导出等的标准化问题,提供与三方 vendor 无关的服务。

启动jaeger容器

sh 复制代码
docker run -d --name jaeger \
   -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
   -e COLLECTOR_OTLP_ENABLED=true      \
   -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
   
   
 # 参数说明
 #COLLECTOR_ZIPKIN_HOST_PORT=:9411  采集zipkin
 #COLLECTOR_OTLP_ENABLED=true   支持OTLP

容器暴露端口说明

容器暴露以下端口:

Port Protocol Component Function
5775 UDP agent accept zipkin.thrift over compact thrift protocol (deprecated, used by legacy clients only)
6831 UDP agent accept jaeger.thrift over compact thrift protocol
6832 UDP agent accept jaeger.thrift over binary thrift protocol
5778 HTTP agent serve configs
16686 HTTP query serve frontend
14268 HTTP collector accept jaeger.thrift directly from clients
14250 HTTP collector accept model.proto
9411 HTTP collector Zipkin compatible endpoint (optional)

查看容器

sh 复制代码
root@node1:~# docker ps
CONTAINER ID   IMAGE                      COMMAND                  CREATED         STATUS         PORTS                                                                                                                                                                                                                                                                                                                                                                                               NAMES
4f8233f7a370   jaegertracing/all-in-one   "/go/bin/all-in-one-..."   2 minutes ago   Up 2 minutes   0.0.0.0:4317-4318->4317-4318/tcp, :::4317-4318->4317-4318/tcp, 0.0.0.0:5778->5778/tcp, :::5778->5778/tcp, 0.0.0.0:9411->9411/tcp, :::9411->9411/tcp, 0.0.0.0:14250->14250/tcp, :::14250->14250/tcp, 0.0.0.0:14268-14269->14268-14269/tcp, :::14268-14269->14268-14269/tcp, 0.0.0.0:16686->16686/tcp, :::16686->16686/tcp, 5775/udp, 0.0.0.0:6831-6832->6831-6832/udp, :::6831-6832->6831-6832/udp   jaeger

访问Jaeger

访问链接 http://192.168.202.221:16686

创建go项目测试

下载依赖

sh 复制代码
go get go.opentelemetry.io/otel/sdk
go get go.opentelemetry.io/otel/exporters/jaeger

使用otel的sdk创建服务调用

go 复制代码
package main

import (
    "context"
    "errors"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/baggage"
    "go.opentelemetry.io/otel/codes"
    "go.opentelemetry.io/otel/exporters/jaeger"
    "go.opentelemetry.io/otel/sdk/resource"
    tracesdk "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
    "log"
)

// 初始化provider
func tracerProvider(url string) (*tracesdk.TracerProvider, error) {
    // 初始化导出器,导出到jaeger
    exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
    if err != nil {
       log.Fatal(err)
    }
    // 创建traceProvider
    tp :=
       tracesdk.NewTracerProvider(
          tracesdk.WithBatcher(exp),
          tracesdk.WithResource(
             // 用于打印服务的一些信息
             resource.NewWithAttributes(
                semconv.SchemaURL,
                semconv.ServiceName("jaeger-service"),  // 服务名称
                attribute.String("environment", "dev"), // 一些属性
             ),
          ),
       )
    return tp, nil
}

func main() {
    // jaeger的api地址
    url := "http://192.168.202.221:14268/api/traces"
    // 获取trace  provider
    tp, err := tracerProvider(url)
    if err != nil {
       log.Fatal(err)
    }

    // 创建一个上下文
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    // 对tp进行关闭
    defer func() {
       err := tp.Shutdown(ctx)
       if err != nil {
          log.Fatal(err)
       }
    }()

    otel.SetTracerProvider(tp)

    // 上下文中附加一些属性
    m0, _ := baggage.NewMember("data1", "value1")
    m1, _ := baggage.NewMember("data2", "value2")
    bg, err := baggage.New(m0, m1)
    if err != nil {
       return
    }

    ctx = baggage.ContextWithBaggage(ctx, bg)

    // 开启tracing
    tr := tp.Tracer("jaeger-main") // jaeger-main为lib name

    ctx, span := tr.Start(ctx, "foo")
    defer span.End() // End表示发数据给jaeger

    // 调用bar函数
    err = bar(ctx)

    if err != nil {
       span.RecordError(err)                    // 记录错误
       span.SetStatus(codes.Error, err.Error()) // 设置状态
    }
}

func bar(ctx context.Context) error {
    tr := otel.Tracer("jaeger-main")

    _, span := tr.Start(ctx, "bar")
    defer span.End()

    // 业务逻辑
    // 设置一些属性
    span.SetAttributes(attribute.Key("test").String("test"))
    err := errors.New("bar出现了错误")

    //span.RecordError()
    // 添加一个错误事件
    span.AddEvent(err.Error())
    // 设置状态
    span.SetStatus(codes.Error, err.Error())

    return err
}

在jaeger中查看

Promethes的接入

下载依赖

sh 复制代码
go get go.opentelemetry.io/otel/exporters/prometheus

go代码如下

go 复制代码
package main

import (
    "context"
    "github.com/prometheus/client_golang/prometheus/promhttp"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/exporters/prometheus"
    metric2 "go.opentelemetry.io/otel/metric"
    "go.opentelemetry.io/otel/sdk/metric"
    _ "go.opentelemetry.io/otel/sdk/metric"
    "log"
    "math/rand"
    "net/http"
    "os"
    "os/signal"
)

func main() {
    ctx := context.Background()

    // 初始化Prometheus的导出器
    exp, err := prometheus.New()

    if err != nil {
       log.Fatal(err)
    }

    // 创建Provider
    provider := metric.NewMeterProvider(metric.WithReader(exp))
    meter :=
       provider.Meter("prometheus")

    // 启动指标服务
    go serveMetrics()

    // 创建指标,将应用程序指标写入
    attrs := metric2.WithAttributes(

       attribute.Key("A").String("B"),
       attribute.Key("C").String("D"),
       attribute.Key("E").String("F"),
    )

    // 计数器指标
    counter, err := meter.Float64Counter("foo", metric2.WithDescription("foo counter描述"))
    if err != nil {
       log.Fatal(err)
    }

    // 加5,自服务启动后加5
    counter.Add(ctx, 5, attrs)

    // 实时指标
    gauge, err := meter.Float64ObservableGauge("bar", metric2.WithDescription("bar observer 描述"))
    if err != nil {
       log.Fatal(err)
    }

    // 直方图,根据范围对数据进行分类归类
    histogram, err := meter.Float64Histogram("baz", metric2.WithDescription("baz histogram 描述"))

    if err != nil {
       log.Fatal(err)
    }
    // 添加数据记录
    histogram.Record(ctx, 23, attrs)
    histogram.Record(ctx, 7, attrs)
    histogram.Record(ctx, 56, attrs)
    histogram.Record(ctx, 23, attrs)
    histogram.Record(ctx, 43, attrs)
    histogram.Record(ctx, 60, attrs)
    histogram.Record(ctx, 14, attrs)
    histogram.Record(ctx, 21, attrs)
    histogram.Record(ctx, 90, attrs)
    histogram.Record(ctx, 24, attrs)
    histogram.Record(ctx, 67, attrs)

    // 注册回调函数
    _, err = meter.RegisterCallback(func(ctx context.Context, observer metric2.Observer) error {
       n := rand.Float64() * (100.)
       observer.ObserveFloat64(gauge, n, attrs)
       return nil
    }, gauge)

    ctx, _ = signal.NotifyContext(ctx, os.Interrupt)
    <-ctx.Done()
}

func serveMetrics() {
    http.Handle("/metrics", promhttp.Handler())

    err := http.ListenAndServe(":2223", nil)

    if err != nil {
       log.Fatal(err)
    }
}

查看端口数据

访问 http://192.168.0.204:2223/metrics

在Prometheus中配置文件中添加该endpoint

yaml 复制代码
  - job_name: "tracing_status"
    metrics_path: /metrics
    static_configs:
      - targets: ["192.168.0.204:2223"]

重启Prometheus

sh 复制代码
systemctl restart prometheus

查看状态

在图形界面查询

在Grafana中添加图表

参考文档

相关推荐
踏浪无痕3 分钟前
从救火到防火:我在金融企业构建可观测性体系的实战之路
后端·面试·架构
陈葛杰5 分钟前
VMware 安装 Rocky Linux 9.6(Minimal 版)超详细图文教程|轻量 · 安全 · 生产级
linux·运维·服务器
时间之里21 分钟前
【linux】:目录解析
linux·运维·服务器
qq_3176203143 分钟前
001:Linux 命令速查手册
linux·运维·服务器
韩立学长1 小时前
基于Springboot建筑物保护可视化系统rk6tni53(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
superman超哥1 小时前
Rust Link-Time Optimization (LTO):跨边界的全局优化艺术
开发语言·后端·rust·lto·link-time·跨边界·优化艺术
superman超哥1 小时前
Rust 编译优化选项配置:释放性能潜力的精细调控
开发语言·后端·rust·rust编译优化·精细调控·编译优化选项
Coder_Boy_1 小时前
分布式系统设计经验总结:金融vs电商的核心差异与决策思路
java·运维·微服务·金融·电商
UrbanJazzerati1 小时前
深度解析Salesforce Apex的Governance Limit:SOQL 50,000条记录的事务级限制
后端·面试
biubiubiu07061 小时前
Ansible自动化
运维·自动化·ansible