このトピックでは、Go ランタイム環境におけるトレース分析機能について説明します。
背景
Alibaba Cloud Tracing Analysis は OpenTracing 標準を採用しており、オープンソースコミュニティと互換性があります。トレース分析は、分散アプリケーションの開発者にさまざまな機能を提供します。たとえば、開発者は分散トレースのクエリと診断、分散トポロジの動的な検出、アプリケーションパフォーマンスのリアルタイムでの集計が可能になります。
Function Compute はトレース分析と統合されています。Jaeger SDK または OpenTelemetry を使用してトレース情報をアップロードし、関数呼び出しを追跡できます。トレース分析は、サーバーレスアーキテクチャにおけるパフォーマンスボトルネックの分析と診断に役立ちます。これにより、サーバーレスシナリオでの開発と診断の効率が向上します。
概要
Function Compute コンソールでトレース分析を設定できます。詳細については、「トレース分析の有効化」をご参照ください。
サービスのトレース分析を有効にすると、Function Compute はシステムで消費された時間を自動的に記録します。この時間には、コールドスタート時間、イニシャライザー関数の実行時間、および関数の実行時間が含まれます。次の図のシステムスパンの詳細については、「スパン名の説明」をご参照ください。
ビジネス側の関数で消費された時間を記録できます。たとえば、「カスタムスパンの作成」を使用して、関数内で ApsaraDB RDS や File Storage NAS などのサービスへのアクセスにかかった時間を記録できます。
サンプルコード
Go ランタイム環境の Function Compute のトレース分析では、OpenTracing 仕様の Jaeger 実装に基づいて、次のメソッドを使用してカスタムスパンを作成できます。
SDK for OpenTelemetry の使用 (推奨)
Go コードでは、SDK for OpenTelemetry を使用してコードインストルメンテーションを実行し、データをトレース分析にレポートできます。完全なサンプルコードについては、「golang-tracing-openTelemetry」をご参照ください。
次のリストは、サンプルコードの詳細を説明しています。
依存関係を追加します。
go get github.com/aliyun/fc-runtime-go-sdk go get go.opentelemetry.io/otel go get go.opentelemetry.io/otel/sdk go get go.opentelemetry.io/otel/exporters/jaegerデータをトレース分析にレポートします。
func HandleRequest(ctx context.Context, event MyEvent) (string, error) { // 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) } // トレーサープロバイダーを作成します。 tp, err := NewTracerProvider(endpoint) if err != nil { return "", fmt.Errorf("OpenTracingJaegerEndpoint: %s, error: %v", fctx.Tracing.JaegerEndpoint, err) } // トレーサープロバイダーをグローバルトレーサープロバイダーに設定します。 otel.SetTracerProvider(tp) if err != nil { return "", fmt.Errorf("failed to getFcSpanCtx, error: %v", err) } // カスタムスパンを作成します。 startMySpan(trace.ContextWithSpanContext(ctx, spanCtx)) return fmt.Sprintf("hello world! Hello, %s!", event.Name), nil }トレーサーへのアクセスを提供する
tracerProviderを作成します。func tracerProvider(url string) (*tracesdk.TracerProvider, error) { // Jaeger エクスポーターを作成します。 exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url))) if err != nil { return nil, err } tp := tracesdk.NewTracerProvider( // エクスポーターを登録します。 tracesdk.WithBatcher(exp), // リソースにアプリケーション情報を記録します。 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) { // トレース情報を取得します。 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, ":") // トレース 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 }tracerを作成し、変換された OpenTelemetry SpanContext を使用して子スパンを作成します。スパンは、呼び出しトレースで名前が付けられ、時間が計測される、継続的に実行可能なコードスニペットを表します。スパンから子スパンを作成することもできます。func startMySpan(ctx context.Context) { // グローバルトレーサープロバイダーを使用します。 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") }
SDK for Jaeger の使用
SDK for Jaeger を使用してコードインストルメンテーションを実行し、データをトレース分析にレポートできます。完全なサンプルコードについては、「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データをトレース分析にレポートします。
func HandleRequest(ctx context.Context, event MyEvent) (string, error) { // Function Compute のコンテキストのトレース情報を取得します。 fctx, _ := fccontext.FromContext(ctx) endpoint := fctx.Tracing.JaegerEndpoint OpenTracingSpanContext := fctx.Tracing.OpenTracingSpanContext if len(endpoint) == 0 { return "", fmt.Errorf("invalid jaeger endpoint") } // トレーサーを作成します。 tracer, closer := NewJaegerTracer("FCTracer", endpoint) defer closer.Close() // SpanContext を復元します。 spanContext, err := jaeger.ContextFromString(OpenTracingSpanContext) if err != nil { return "", fmt.Errorf("OpenTracingSpanContext: %s, error: %v", fctx.Tracing.OpenTracingSpanContext, err) } // カスタムスパンを作成します。 startMySpan(spanContext, tracer) return fmt.Sprintf("hello world! Hello, %s!", event.Name), nil }トレーサーへのアクセスを提供する
tracerオブジェクトを作成します。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) // 子スパンを有効にします。 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)) }