AWS Lambda関数の分散トレーシング
手動計装
ワークショップの第2部では、OpenTelemetryによる手動計装が計測データ収集を強化する方法を実演することに焦点を当てます。より具体的には、今回のケースでは、producer-lambda 関数から consumer-lambda 関数にトレースコンテキストデータを伝播させることができるようになります。これにより、現在は自動コンテキスト伝播をサポートしていないKinesisストリームを介しても、2つの関数間の関係を見ることができるようになります。
手動計装ワークショップディレクトリとコンテンツ #
再度、作業ディレクトリとそのファイルの一部を確認することから始めます。今回は o11y-lambda-workshop/manual ディレクトリです。ここにはワークショップの手動計装部分のすべてのコンテンツがあります。
manual ディレクトリ
#
以下のコマンドを実行して
o11y-lambda-workshop/manualディレクトリに移動しますbashcd ~/o11y-lambda-workshop/manuallsコマンドでこのディレクトリの内容を確認しますbashls出力には以下のファイルとディレクトリが含まれるはずです:
bashhandler outputs.tf terraform.tf variables.tf main.tf send_message.py terraform.tfvars
ワークショップの質問
このディレクトリと最初に始めたautoディレクトリに何か違いがありますか?auto と manual のファイルを比較する
#
見た目が同じように見えるこれらのファイルが実際に同じかどうか確認しましょう。
autoとmanualディレクトリのmain.tfファイルを比較しますbashdiff ~/o11y-lambda-workshop/auto/main.tf ~/o11y-lambda-workshop/manual/main.tf- 違いはありません!(違いがあるはずはありません。もし違いがあれば、ワークショップ進行役に支援を求めてください)
次に、
producer.mjsファイルを比較してみましょうbashdiff ~/o11y-lambda-workshop/auto/handler/producer.mjs ~/o11y-lambda-workshop/manual/handler/producer.mjs- ここにはかなりの違いがあります!
ファイル全体を表示してその内容を調べたい場合は以下を実行します
bashcat ~/o11y-lambda-workshop/handler/producer.mjs- 必要な手動計装タスクを処理するために、いくつかのOpenTelemetryオブジェクトを関数に直接インポートしていることに注目してください。
jsimport { context, propagation, trace } from "@opentelemetry/api";- プロデューサー関数でコンテキストを伝播するために、@opentelemetry/api
から次のオブジェクトをインポートしています
- context
- propagation
- trace
最後に、
consumer.mjsファイルを比較しますbashdiff ~/o11y-lambda-workshop/auto/handler/consumer.mjs ~/o11y-lambda-workshop/manual/handler/consumer.mjsここにもいくつかの注目すべき違いがあります。より詳しく見てみましょう
bashcat handler/consumer.mjs- このファイルでは、次の @opentelemetry/api
オブジェクトをインポートしています
- propagation
- trace
- ROOT_CONTEXT
- これらを使用して、プロデューサー関数から伝播されたトレースコンテキストを抽出します
- その後、抽出したトレースコンテキストに
nameとsuperpowerに基づいた新しいスパン属性を追加します
- このファイルでは、次の @opentelemetry/api
オブジェクトをインポートしています
プロデューサー関数からのトレースコンテキスト伝播 #
以下のコードはプロデューサー関数内で次のステップを実行します
- このトレース用のトレーサーを取得する
- コンテキストキャリアオブジェクトを初期化する
- アクティブスパンのコンテキストをキャリアオブジェクトに注入する
- Kinesisストリームに配置しようとしているレコードを修正し、アクティブスパンのコンテキストをコンシューマーに運ぶキャリアを含める
...
import { context, propagation, trace, } from "@opentelemetry/api";
...
const tracer = trace.getTracer('lambda-app');
...
return tracer.startActiveSpan('put-record', async(span) => {
let carrier = {};
propagation.inject(context.active(), carrier);
const eventBody = Buffer.from(event.body, 'base64').toString();
const data = "{\"tracecontext\": " + JSON.stringify(carrier) + ", \"record\": " + eventBody + "}";
console.log(
`Record with Trace Context added:
${data}`
);
try {
await kinesis.send(
new PutRecordCommand({
StreamName: streamName,
PartitionKey: "1234",
Data: data,
}),
message = `Message placed in the Event Stream: ${streamName}`
)
...
span.end();コンシューマー関数でのトレースコンテキスト抽出 #
以下のコードはコンシューマー関数内で次のステップを実行します
producer-lambdaから取得したコンテキストをキャリアオブジェクトに抽出する- 現在のコンテキストからトレーサーを抽出する
- 抽出したコンテキスト内でトレーサーを使用して新しいスパンを開始する
- ボーナス:メッセージからの値を含むカスタム属性など、追加の属性をスパンに追加する!
- 完了したら、スパンを終了する
import { propagation, trace, ROOT_CONTEXT } from "@opentelemetry/api";
...
const carrier = JSON.parse( message ).tracecontext;
const parentContext = propagation.extract(ROOT_CONTEXT, carrier);
const tracer = trace.getTracer(process.env.OTEL_SERVICE_NAME);
const span = tracer.startSpan("Kinesis.getRecord", undefined, parentContext);
span.setAttribute("span.kind", "server");
const body = JSON.parse( message ).record;
if (body.name) {
span.setAttribute("custom.tag.name", body.name);
}
if (body.superpower) {
span.setAttribute("custom.tag.superpower", body.superpower);
}
...
span.end();これでどのような違いが生まれるか見てみましょう!
