LambdaからカスタムサブセグメントをX-Rayに送信する – ClassmethodサーバーレスAdvent Calendar 2017 #serverless #adventcalendar

2017.12.16

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

西田@大阪です。今回は Lambda から X-Ray に対してのカスタムセグメントを送信して、その情報を検索してみたいと思います

このエントリはServerless Advent Calendar 2017 16日目の記事です

X-Rayで使われる用語

単語 意味
セグメント X-Rayの基本となるトレースデータ
サービス マイクロサービスにおける1つのサービスを表す。Lambdaであったり、HTTPサーバーなど
ダウンストリーム サービスから呼び出される別のサービス、もしくはDynamoDB、PostgreSQL、MySQLなどのミドルウェア

セグメント

X-Rayの基本となるデータです。最低限必要なパラメーターとしては以下になります

項目 説明
name セグメントの名前
id セグメントのID
trace_id 複数のサービスのトラッキングに使われるID
start_time 計測開始時間
end_time 計測終了時間

サブセグメント

サブセグメントは一つのサービスの中でダウンストリームの呼び出し、計測したい任意の処理をトレースするためのものです

最低限必要なパラメーターに以下のパラメーターが追加されます。

項目 説明
parent_id 親のセグメントのID

parent_idに親のセグメントのIDを設定することによって、親子関係をトレースできるようになります

Node.jsの X-Ray SDKだとキャプチャ関数内で現在のコンテキストから取得できるセグメントのIDが parent_id にセットされたサブセグメントが生成されます

AWSXRay.captureAsyncFunc('send', function(subsegment) {
// この subsegment の prent_id には今のコンテキストのセグメントのIDが設定されている
sendRequest(host, function() {
console.log('rendering!');
res.render('index');
subsegment.close();
});
});

Lambdaのカスタムサブセグメントを送信する

Lambdaを作成しアクティブトレースを有効にする

Lambdaのblue printから microservice-http-endpoint と DynamoDB を作成します

以下の記事が参考になります

【アップデート】AWS X-Ray が AWS Lambda に対応しました(プレビュー)

Lambda作成後Lambdaの設定画面からアクティブトレースを有効にするをチェックし保存します

カスタムサブセグメントを送信する

X-Ray SDKを宣言します

const AWSXRay = require('aws-xray-sdk-core');

X-Ray SDK の captureAsyncFuncを使用し非同期の関数を計測するサブセグメントを作成します

以下の例では非同期処理の計測を行い検索用のAnnotationをサブセグメントに追加しています

AWSXRay.captureAsyncFunc('timeout', (subsegment) => {
// この subsegment の parent_id には今のコンテキストのセグメントのidが設定されている
setTimeout(()=>{
console.log('Do Async');
subsegment.addAnnotation("key", "setTimeout");
subsegment.close();
}, 2000);
});

X-Rayのサービスマップで確認する

annotationはannotation.<キー> = "<値>"のシンタックスで検索できます

検索結果より、トレースされた内容を確認します

内容を確認すると setTimeout で設定した2秒かかってることがわかります

Lambdaから別のLambdaの呼び出しをトレースする

AWS SDK を X-Ray SDK をつかってトレースできるようにします

const AWS = AWSXRay.captureAWS(require('aws-sdk'));
const Lambda = new AWS.Lambda({});

後は呼び出しを行うだけで、Lambdaの呼び出しリクエストに X-Amzn-Trace-Idヘッダが付与され、呼び出された側のLambdaのセグメントのtrace_idとして利用されます

Lambda.invoke({
FunctionName: "x-ray-sample-2",
Payload: JSON.stringify(event, null, 2)
}, (error, data) => {
if (error) {
console.log(error);
console.log('failed to invoke another lambda');
} else {
console.log('Succeed to call another lambda');
}
});

これはX-Ray SDK の captureAWS 関数内で AWS SDK の HTTPリクエストをカスタマイズする仕組みを使って X-Amzn-Trace-Idヘッダをつなぐフックが設定されるからです

var captureAWS = function captureAWS(awssdk) {
if (!semver.gte(awssdk.VERSION, minVersion))
throw new Error ('AWS SDK version ' + minVersion + ' or greater required.');

for (var prop in awssdk) {
if (awssdk[prop].serviceIdentifier) {
var Service = awssdk[prop];
// フックする処理を登録
Service.prototype.customizeRequests(captureAWSRequest);
}
}

return awssdk;
};
function captureAWSRequest(req) {
// ~~~ 省略

req.on('build', function(req) {
// X-Amzn-Trace-IdをHTTPヘッダに設定
req.httpRequest.headers['X-Amzn-Trace-Id'] = 'Root=' + traceId + ';Parent=' + subsegment.id +
';Sampled=' + (subsegment.segment.notTraced ? '0' : '1');
}).on('complete', function(res) {
subsegment.addAttribute('namespace', 'aws');
subsegment.addAttribute('aws', new Aws(res, subsegment.name));

// ~~~ 省略

サービスマップでLambda同士がつながっていることが確認できます

呼び出された側のトレースもできてることが確認できます

最後に

いかかでしたでしょうか?

X-Rayの仕組みを理解しカスタムセグメントを活用すれば、任意の処理を計測することが容易になります

複雑で大規模のシステムになればなるほど、活用できる場面が多くなるのではないでしょうか

次回はX-Ray SDK の仕組みについて深く調べて見たいと思います