Jaeger 链路追踪接入

393 阅读2分钟

之前利用 Istio 自带的链路追踪功能将微服务间的网络调用上报到 jaeger 平台,但无法做到函数级别的上报,需要在业务代码里自行接入客户端进行上报。

我们服务语言主要2个:golang, nodejs

所以接下来主要介绍它们如何接入。

Jaeger 介绍

Jaeger 是 Uber 开源的一个链路追踪服务组件,可以接受 opentelemetry trace 数据,并展示完整链路。之前官方提供了单独的 jaeger client sdk,不过目前官方已经不推荐,官方欢迎接入 opentelemetry 标准,所以目前使用 opentelemetry 提供的 sdk 即可接入。

Jaeger 提供了 grpc, http 2种数据上报方式,端口分别为 4317 和 4318,这里我们选了 grpc 方式。

Golang 服务接入

  1. 安装 opentelemetry 依赖包
go get go.opentelemetry.io/otel@v1.29.0
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@v1.29.0
go get go.opentelemetry.io/otel/sdk@v1.29.0
go get go.opentelemetry.io/otel/trace@v1.29.0
  1. 初始化 Provider ,并返回全局 tracer

    • 先初始化 grpc exporter, jaeger 服务的地址是 192.168.1.10:4317
    • 设置全局 Provider;provider 是链路追踪的核心,用于管理 tracer,span context上下文,集成exporter
    • 获取全局的 tracer;tracer 用于生成 span,每个 span 代表一段独立的时间片段

package tracing

import (
	"context"
	"log"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
	"go.opentelemetry.io/otel/sdk/resource"
	"go.opentelemetry.io/otel/sdk/trace"
	semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
	oTrace "go.opentelemetry.io/otel/trace"
)

const TRACER_NAME = "go-service"

func NewGrpcProviderTracer() (oTrace.Tracer, func()) {
	ctx := context.Background()
	exp, err := otlptracegrpc.New(ctx,
		otlptracegrpc.WithEndpoint("192.168.1.10:4317"),
		otlptracegrpc.WithInsecure())
	if err != nil {
		panic(err)
	}

	tp := trace.NewTracerProvider(trace.WithBatcher(exp), trace.WithResource(resource.NewSchemaless(semconv.ServiceName(TRACER_NAME))))
	otel.SetTracerProvider(tp)

	tr := tp.Tracer(TRACER_NAME)

	return tr, func() {
		if err := tp.Shutdown(ctx); err != nil {
			log.Printf("failed to shutdown TracerProvider: %v", err)
		}
	}
}
  1. 函数级别上报使用
func HelloBiz(ctx context.Context) {
	ctx, span := tr.Start(ctx, "HelloBiz") // tr 是上面👆获取到的全局 tracer
	defer span.End()
        
	// .... 
}

Nodejs 服务接入

  1. 修改 package.json 的 dependencies 添加 opentelemetry 依赖,然后执行 pnpm i 进行安装更新
    "dependencies": {
        "@opentelemetry/exporter-trace-otlp-grpc": "^0.56.0",
        "@opentelemetry/instrumentation-grpc": "^0.56.0",
        "@opentelemetry/sdk-node": "^0.56.0",
        "@opentelemetry/sdk-trace-base": "^1.29.0",
        "@opentelemetry/semantic-conventions": "^1.28.0",
    },
  1. 一样初始化好 grpc exporter,如果你的当前微服务也是用的 grpc 协议,那么使用 GrpcInstrumentation 可以注册采集请求的 trace 详情;如果你用的是 http 协议,请改用 HttpInstrumentation (需要下载对应的 npm 依赖包)
import { credentials } from '@grpc/grpc-js';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
import { GrpcInstrumentation } from '@opentelemetry/instrumentation-grpc';
import { NodeSDK } from '@opentelemetry/sdk-node';

const serviceName = 'nodejs-service';

async function initTracer() {
    const exporter = new OTLPTraceExporter({
        credentials: credentials.createInsecure(),
        url: 'http://192.168.1.10:4317',
    });

    const otelSDK = new NodeSDK({
        serviceName,
        spanProcessors: [new tracing.BatchSpanProcessor(exporter)],
        instrumentations: [new GrpcInstrumentation()],
    });

    await otelSDK.start();

    ['SIGINT', 'SIGTERM'].forEach((signal) => {
        process.on(signal, () => otelSDK.shutdown().catch(console.error).finally(() => process.exit(0)));
    });
}
  1. 在服务启动 bootstrap 前,需要先执行上面👆的 initTracer 方法
async function bootstrap() {
    await initTracer();
    
    // ...
    app.listen();
}

bootstrap()
  1. 函数级别上报 trace
import { api } from '@opentelemetry/sdk-node';

const serviceName = 'nodejs-service';

function SayHi() {
    const tr = api.trace.getTracer(serviceName);
    const span = tr.startSpan('SayHi');
    
    // Do something
    
    span.end();
}

效果如下:

图片.png


好了,上述便是我这期分析的内容,希望对你有帮助,我们有缘再见