OpenTelemetry 向けマネージドサービスは、トレースマッピング、呼び出し統計、トレーストポロジー、アプリケーション依存関係分析など、分散アプリケーション開発のための一連のツールを提供します。Service Mesh (ASM) は OpenTelemetry 向けマネージドサービスと統合されています。このガイドでは、ヘッダーを使用して ASM 内の gRPC ベースのサービスをトレースする方法について、Java、Go、Node.js、Python の各言語での実装例を交えて説明します。
トレースヘッダー伝播の仕組み
各 ASM サイドカーは、リクエストが通過するたびにスパンを生成します。アプリケーションレベルでのヘッダー伝播がない場合、各スパンは OpenTelemetry 向けマネージドサービス内で独立したエントリとして表示され、完全な呼び出しチェーンを可視化できなくなります。
接続されたトレースを生成するには、アプリケーションで次のことを行う必要があります:
抽出:各受信リクエストからトレースヘッダーを抽出します。
転送:その受信リクエストによってトリガーされるすべての送信リクエストに、それらのヘッダーを転送します。
これにより、OpenTelemetry 向けマネージドサービスは、異なるサービスからのスパンを単一のトレースに相関させることができます。
前提条件
ASM インスタンスが作成されていること。詳細については、「ASM インスタンスの作成」をご参照ください。
OpenTelemetry 向けマネージドサービスが Alibaba Cloud アカウントで有効化されていること。課金の詳細については、「課金ルール」をご参照ください。
サンプルプロジェクト
このガイドの例は、hello-servicemesh-grpc サンプルプロジェクト (別名 hello-servicemesh-grpc) に基づいています。リポジトリをクローンして、手順に従ってください。
gRPC サーバーでのヘッダーの抽出
サーバー側では、各受信リクエストからトレースヘッダーを抽出します。実装は、言語と gRPC 通信パターンによって異なります。
基本的な抽出メソッド
| 言語 | API | 戻り値の型 |
|---|---|---|
| Java | ServerInterceptor.interceptCall(ServerCall<ReqT, RespT> call, final Metadata m, ServerCallHandler<ReqT, RespT> h) を実装します。m.get(k) を呼び出します。ここで、k の型は Metadata.Key<String> です。 | String |
| Go | metadata.FromIncomingContext(ctx) | MD (map[string][]string) |
| Node.js | call.metadata.getMap() | {[key: string]: MetadataValue} ここで MetadataValue は string | Buffer |
| Python | context.invocation_metadata() | (key, value) タプルの配列。各エントリには m.key と m.value でアクセスします。 |
RPC パターン別の抽出
次の表は、各 gRPC 通信パターンでコンテキストまたはメタデータオブジェクトを取得する方法を示しています。Java は ServerInterceptor を通じてすべてのパターンを均一に処理します。Go、Node.js、Python は 4 つのパターンすべてで同じ API を使用します。
| RPC パターン | Java | Go | Node.js | Python |
|---|---|---|---|---|
| Unary | ServerInterceptor | metadata.FromIncomingContext(ctx) -- Talk メソッドの入力からの ctx | call.metadata.getMap() | context.invocation_metadata() |
| サーバー ストリーミング | ServerInterceptor | metadata.FromIncomingContext(stream.Context()) -- TalkOneAnswerMore の入力からの stream | call.metadata.getMap() | context.invocation_metadata() |
| クライアント ストリーミング | ServerInterceptor | metadata.FromIncomingContext(stream.Context()) -- TalkMoreAnswerOne の入力からの stream | call.metadata.getMap() | context.invocation_metadata() |
| 双方向ストリーミング | ServerInterceptor | metadata.FromIncomingContext(stream.Context()) -- TalkBidirectional の入力からの stream | call.metadata.getMap() | context.invocation_metadata() |
Go のストリーミング RPC の場合、stream パラメーターから stream.Context() を呼び出して ctx を取得します。Unary RPC の場合、ctx はメソッドのパラメーターとして直接渡されます。
gRPC クライアントからのヘッダーの挿入
ダウンストリームのサービスがトレースヘッダーを受信できるように、各送信リクエストにヘッダーをアタッチします。
基本的な挿入メソッド
| 言語 | API | 詳細 |
|---|---|---|
| Java | ClientInterceptor.interceptCall(MethodDescriptor<ReqT, RespT> m, CallOptions o, Channel c) を実装します。返された ClientCall<ReqT, RespT>.start(Listener<RespT> l, Metadata h) 内で、h.put(k, v) を呼び出します。ここで、k は Metadata.Key<String> で、v は String です。 | インターセプタベースの挿入 |
| Go | metadata.AppendToOutgoingContext(ctx, kv...) | キーと値のペアが追加された新しい context.Context を返します |
| Node.js | metadata = call.metadata.getMap() の後、metadata.add(key, headers[key]) | 各ヘッダーをメタデータマップに追加します |
| Python | dict (metadata_dict[c.key] = c.value) を構築し、list(metadata_dict.items()) | タプルのリストをメタデータとして渡します |
RPC パターン別の挿入
4 つの言語すべてで、RPC パターンに関係なく同じ挿入メソッドが使用されます。Java は ClientInterceptor を介して挿入を処理し、Go は metadata.AppendToOutgoingContext(ctx, kv) を呼び出し、Node.js と Python はそれぞれ上記で説明した基本的なメソッドを使用します。
サーバーとクライアント間でのヘッダーの伝播
完全なトレースを作成するには、サーバーがダウンストリームの呼び出しを行う前に、受信したトレースヘッダーをクライアント側に渡す必要があります。フローは次のとおりです:
サーバーが受信リクエストからヘッダーを抽出します。
サーバーがクライアント側のコードにヘッダーを渡します。
クライアントが送信リクエストにヘッダーを挿入します。
Go、Node.js、Python
サーバー側のメソッドは、呼び出しコンテキストまたはメタデータオブジェクトを介してヘッダーを直接受信します。抽出されたヘッダーを同じメソッドスコープ内のクライアント呼び出しに渡します。追加のメカニズムは必要ありません。
Java:メタデータ-コンテキスト伝播
Java は、ヘッダーの読み取りと書き込みに 2 つの別々のインターセプタ (ServerInterceptor と ClientInterceptor) を使用します。gRPC サービスは複数のリクエストを同時に処理する場合があるため、単純な共有キャッシュでは 2 つのインターセプタを確実に接続することはできません。
代わりに、Java はメタデータ-コンテキスト伝播を使用します:

コンテキストへの書き込み:
ServerInterceptor内で、抽出したヘッダーを gRPC のContextに書き込みます。keyパラメーターの型はContext.Key<String>です。ctx.withValue(key, metadata)コンテキストからの読み取り:
ClientInterceptor内で、ヘッダーを読み戻します。デフォルトでは、getメソッドはContext.current()を使用するため、読み取りと書き込みの操作が同じコンテキストを共有することが保証されます。key.get()
このメカニズムにより、サーバーが複数の同時リクエストを処理している場合でも、ヘッダーが正しく伝播されることが保証されます。
ASM トポロジーのデプロイと検証
トレーシングを有効にする前に、ASM インスタンスのトポロジーをデプロイし、正しく動作することを確認します。
サンプルプロジェクトのトレーシングディレクトリには、Java、Go、Node.js、Python 用のデプロイスクリプトが含まれています。次の例では Go を使用します。
cd go
# Deploy the topology
sh apply.sh
# Verify the topology
sh test.shエラーが発生しなければ、トポロジーの準備は完了です。
次の図は、デプロイされたトポロジーを示しています。

トレーシングデータの表示
ASM がトレーシングデータを OpenTelemetry 向けマネージドサービスに送信するように設定します。詳細については、「ASM トレーシングデータを OpenTelemetry 向けマネージドサービスに収集する」をご参照ください。
OpenTelemetry 向けマネージドサービスコンソールにログインします。
左側のナビゲーションウィンドウで、[Trace Entrance] をクリックします。
[Trace Entrance] ページで、対象のアプリケーションの [Application Topology] をクリックします。完全なトレースチェーンが表示されます:ローカルリクエスタ > Ingress Gateway > grpc-server-svc1 > grpc-server-svc2 > grpc-server-svc3。

[End-to-End Aggregation] タブをクリックして、集計されたトレースを表示します。

[Span Name] 列で、スパンをクリックしてその詳細を表示します。
