このトピックでは、Goランタイム環境でのトレース分析機能について説明します。
背景
Alibaba Cloud Tracing AnalysisはOpenTracing標準を採用しており、オープンソースコミュニティと互換性があります。 Tracing Analysisは、分散アプリケーションの開発者にさまざまな機能を提供します。 たとえば、開発者は分散トレースのクエリと診断、分散トポロジの動的な検出、アプリケーションのパフォーマンスのリアルタイムな要約を行うことができます。
Function ComputeはTracing Analysisと統合します。 Jaeger SDKまたはOpenTelemetryを使用してトレース情報をアップロードし、関数の呼び出しを追跡できます。 Tracing Analysisは、サーバーレスアーキテクチャのパフォーマンスボトルネックの分析と診断に役立ちます。 これにより、サーバーレスシナリオでの開発と診断の効率が向上します。
概要
Tracing Analysisは、Function Computeコンソールで設定できます。 詳細については、「トレース分析の有効化」をご参照ください。
サービスのTracing Analysisを有効にすると、Function Computeはシステムで消費された時間を自動的に記録します。 時間は、コールドスタート時間、イニシャライザ関数の実行時間、及び関数の実行時間を含む。 次の図のシステムスパンの詳細については、「スパン名の説明」をご参照ください。
ビジネス側の関数で消費された時間を記録できます。 たとえば、[カスタムスパンの作成] を使用して、関数内でApsaraDB RDSやApsara File Storage NASなどのサービスにアクセスするのにかかる時間を記録できます。
サンプルコード
Goランタイム環境でのFunction Computeのトレース分析では、OpenTracing仕様のJaeger実装に基づいて、次の方法を使用してカスタムスパンを作成できます。
OpenTelemetry用SDKの使用 (推奨)
Goコードでは、SDK for OpenTelemetryを使用してコードのインストルメンテーションを実行し、データをTracing Analysisに報告できます。 完全なサンプルコードについては、「golang-tracing-openTelemetry」をご参照ください。
サンプルコードの詳細を次に示します。
依存関係を追加します。
go get github.com/aliyun/fc-runtime-go-sdk go get go get go.opentelemetry.io/otel go get go.opentelemetry.io/otel/sdk go get go.opentelemetry.io/otel/exporters/jaeger
データをTracing Analysisに報告します。
func HandleRequest(ctx context.Context, event MyEvent) (string, error) { // Obtain the tracing information of the context in Function Compute. fctx, ok := fccontext.FromContext(ctx) if !ok { return "", fmt.Errorf("failed to get FcContext") } spanCtx, endpoint, err := getFcTracingInfo(fctx) if err != nil { return "", fmt.Errorf("failed to getFcTracingInfo, error: %v", err) } // Create a tracer provider. tp, err := NewTracerProvider(endpoint) if err != nil { return "", fmt.Errorf("OpenTracingJaegerEndpoint: %s, error: %v", fctx.Tracing.JaegerEndpoint, err) } // Set the tracer provider to a global tracer provider. otel.SetTracerProvider(tp) if err != nil { return "", fmt.Errorf("failed to getFcSpanCtx, error: %v", err) } // Create a custom span. startMySpan(trace.ContextWithSpanContext(ctx, spanCtx)) return fmt.Sprintf("hello world! Hello, %s! ", event.Name), nil }
トレーサーへのアクセスを提供する
トレーサープロバイダー
を作成します。func tracerProvider(url string) (*tracesdk.TracerProvider, error) { // Create a Jaeger exporter. exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url))) if err != nil { return nil, err } tp := tracesdk.NewTracerProvider( // Register an exporter. tracesdk.WithBatcher(exp), // Record the application information in the resource. tracesdk.WithResource(resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("FCTracer"), attribute.String("environment", "production"), attribute.Int64("ID", 1), )), ) return tp, nil }
Contextのトレース情報を取得し、OpenTracing SpanContextをOpenTelemetry SpanContextに変換します。
func getFcTracingInfo(fctx *fccontext.FcContext) (trace.SpanContext, string, error) { // Obtain the tracing information. spanContext := trace.SpanContext{} endpoint := fctx.Tracing.JaegerEndpoint OpenTracingSpanContext := fctx.Tracing.OpenTracingSpanContext if len(endpoint) == 0 { return spanContext, endpoint, fmt.Errorf("invalid jaeger endpoint") } spanContextSlice := strings.Split(OpenTracingSpanContext, ":") // Pad zeros in front of the trace ID. tid, err := trace.TraceIDFromHex("0000000000000000" + spanContextSlice[0]) if err != nil { return spanContext, endpoint, err } fid := trace.FlagsSampled spanContext = spanContext.WithTraceID(tid).WithTraceFlags(fid).WithRemote(true) return spanContext, endpoint, nil }
トレーサー
を作成し、変換されたOpenTelemetry SpanContextを使用して子スパンを作成します。 スパンは、呼び出しトレースで名前が付けられ、時間が設定された継続的に実行可能なコードスニペットを表します。 スパンに基づいて子スパンを作成することもできます。func startMySpan(ctx context.Context) { // Use a global tracer provider. tr := otel.Tracer("fc-Trace") ctx, parentSpan := tr.Start(ctx, "fc-operation") defer parentSpan.End() parentSpan.SetAttributes(attribute.Key("version").String("fc-v1")) time.Sleep(150 * time.Millisecond) child(ctx) } func child(ctx context.Context) { tr := otel.Tracer("fc-Trace") _, childSpan := tr.Start(ctx, "fc-operation-child") defer childSpan.End() time.Sleep(100 * time.Millisecond) childSpan.AddEvent("timeout") }
JaegerのSDKを使用する
JaegerのSDKを使用して、コードのインストルメンテーションを実行し、データをTracing Analysisに報告できます。 完全なサンプルコードについては、「golang-tracing」をご参照ください。
サンプルコードの詳細を次に示します。
依存関係を追加します。
go get github.com/aliyun/fc-runtime-go-sdk go get github.com/opentracing/opentracing-go go get github.com/uber/jaeger-client-go
データをTracing Analysisに報告します。
func HandleRequest(ctx context.Context, event MyEvent) (string, error) { // Obtain the tracing information of the context in Function Compute. fctx, _ := fccontext.FromContext(ctx) endpoint := fctx.Tracing.JaegerEndpoint OpenTracingSpanContext := fctx.Tracing.OpenTracingSpanContext if len(endpoint) == 0 { return "", fmt.Errorf("invalid jaeger endpoint") } // Create a tracer. tracer, closer := NewJaegerTracer("FCTracer", endpoint) defer closer.Close() // Restore the SpanContext. spanContext, err := jaeger.ContextFromString(OpenTracingSpanContext) if err != nil { return "", fmt.Errorf("OpenTracingSpanContext: %s, error: %v", fctx.Tracing.OpenTracingSpanContext, err) } // Create a custom span. startMySpan(spanContext, tracer) return fmt.Sprintf("hello world! Hello, %s! ", event.Name), nil }
トレーサーへのアクセスを提供する
トレーサー
オブジェクトを作成します。func NewJaegerTracer(service, endpoint string) (opentracing.Tracer, io.Closer) { sender := transport.NewHTTPTransport(endpoint) tracer, closer := jaeger.NewTracer(service, jaeger.NewConstSampler(true), jaeger.NewRemoteReporter(sender)) return tracer, closer }
SpanContextを変換し、カスタムスパンを作成します。 スパンに基づいて子スパンを作成することもできます。
func startMySpan(context jaeger.SpanContext, tracer opentracing.Tracer) { parentSpan := tracer.StartSpan("MyFCSpan", opentracing.ChildOf(context)) defer parentSpan.Finish() parentSpan.SetOperationName("fc-operation") parentSpan.SetTag("version", "fc-v1") time.Sleep(150 * time.Millisecond) // Enable a child span. childSpan := tracer.StartSpan("fc-operation-child", opentracing.ChildOf(parentSpan.Context())) defer childSpan.Finish() time.Sleep(100 * time.Millisecond) childSpan.LogFields( log.String("type", "cache timeout"), log.Int("waited.millis", 100)) }