AWS X-Ray で Lambda の処理の遅い箇所を見つける
記事の「実践」を 1 項目ずつ進めるためのページです。AWS マネジメントコンソールで操作したら、右の一覧にチェックを入れてください。チェックはこのブラウザに保存されます。
前提条件
- リージョン:
ap-northeast-1(東京) - AWS マネジメントコンソールにログイン済みで、Lambda・S3・IAM・CloudWatch・AWS CloudShell を操作できる権限があること
- 事前に作成しておくリソースはありません
| リソース種別 | リソース名 | 用途 |
|---|---|---|
| S3 バケット | basics-xray-<アカウントID> | Lambda 関数がレポートを書き込むバケット |
| Lambda レイヤー | basics-xray-sdk | X-Ray SDK for Python と boto3 をまとめたレイヤー |
| Lambda 関数 | basics-xray-function | 2 秒の sleep と S3 への書き込みを行う関数(Python 3.14) |
| IAM ロール | (Lambda が自動作成) | Lambda 実行ロール |
| IAM インラインポリシー | basics-xray-s3-put | 実行ロールに付ける、バケットの reports/ への書き込み権限 |
S3 バケットを作成する
- AWS マネジメントコンソールにログインし、リージョンが
ap-northeast-1(東京)であることを確認します - 上部の検索バーに
S3と入力し、表示された「S3」を選択します - 左側ナビゲーションの「汎用バケット」を選択し、「バケットを作成」をクリックします
- バケット名に
basics-xray-<アカウントID>(例:basics-xray-123456789012)を入力します - その他の項目はデフォルトのままにし、ページ下部の「バケットを作成」をクリックします
CloudShell で X-Ray SDK のレイヤーを作成する
Lambda の Python ランタイムには X-Ray SDK が含まれていないため、AWS CloudShell でパッケージをまとめてレイヤーとして登録します。X-Ray SDK をインストールすると依存関係として botocore も入ります。レイヤーの依存関係はランタイムに含まれるライブラリより優先して読み込まれるため、boto3 も一緒にレイヤーに入れてバージョンをそろえておきます。
- コンソール上部の CloudShell アイコンをクリックして(または検索バーに
CloudShellと入力して)CloudShell を開きます - CloudShell のリージョンが
ap-northeast-1であることを確認し、以下のコマンドを実行します
mkdir xray-layer && cd xray-layer
pip3 install aws-xray-sdk boto3 \
--platform manylinux2014_x86_64 --only-binary=:all: --python-version 3.14 \
-t python/
zip -r -q layer.zip python
aws lambda publish-layer-version \
--layer-name basics-xray-sdk \
--zip-file fileb://layer.zip \
--compatible-runtimes python3.14 \
--compatible-architectures x86_64 \
--region ap-northeast-1
pip3 installの最後に、CloudShell にあらかじめ入っているaws-sam-cliとboto3のバージョンが合わないというERROR: pip's dependency resolver ...が表示されることがありますが、Successfully installed ...と出ていればpython/への配置は完了しています(-t python/で別ディレクトリに入れているため、CloudShell 側の環境には影響しません)- 出力された JSON の
LayerVersionArnがarn:aws:lambda:ap-northeast-1:<アカウントID>:layer:basics-xray-sdk:1になっていることを確認します
publish-layer-version を実行し、LayerVersionArn に basics-xray-sdk:1 が表示されている画面Lambda 関数を作成する
- 上部の検索バーに
Lambdaと入力し、表示された「Lambda」を選択します - 左側ナビゲーションの「関数」を選択し、「関数の作成」をクリックします
- 「一から作成」が選択されていることを確認し、以下を入力します
- 関数名:
basics-xray-function - ランタイム:
Python 3.14
- 関数名:
- 「アクセス許可」に、基本的な Lambda アクセス許可を持つ新しい実行ロールが作成されると表示されていることを確認します(アーキテクチャは
x86_64がデフォルトです) - その他の項目はデフォルトのままにし、「関数の作成」をクリックします
タイムアウトと環境変数を設定する
関数は 2 秒の sleep を含むため、デフォルトの 3 秒のタイムアウトでは足りなくなるおそれがあります。タイムアウトを延ばし、書き込み先のバケット名を環境変数で渡します。
- 「設定」タブ →「一般設定」を選択し、「編集」をクリックします
- タイムアウトを
0分10秒に変更し、「保存」をクリックします - 「設定」タブ →「環境変数」を選択し、「編集」をクリックします
- 「環境変数を追加」をクリックし、キー
BUCKET_NAME、値basics-xray-<アカウントID>を入力して「保存」をクリックします
レイヤーを追加してコードをデプロイする
- 「コード」タブを選択し、ページ下部の「レイヤー」セクションで「編集」をクリックします
- 「レイヤーを編集」画面で「レイヤーの追加」をクリックします
- 「レイヤーを選択」で、「レイヤーソース」の「カスタムレイヤー」を選択し、カスタムレイヤーに
basics-xray-sdk、バージョンに1を選択して「追加」をクリックします - 「レイヤーを編集」画面に戻ったら「保存」をクリックします
- 「コード」タブに戻り、
lambda_function.pyの内容を以下に書き換えます
import os
import time
import boto3
from aws_xray_sdk.core import patch_all, xray_recorder
# boto3 などのライブラリにパッチを当て、AWS サービスの呼び出しをサブセグメントとして記録する
patch_all()
s3 = boto3.client('s3')
BUCKET_NAME = os.environ['BUCKET_NAME']
@xray_recorder.capture('## slow_process')
def slow_process():
# わざと 2 秒待って、遅い処理を再現する
time.sleep(2)
@xray_recorder.capture('## save_report')
def save_report(order_id):
# 検索に使える注釈として注文 ID を記録する
xray_recorder.current_subsegment().put_annotation('order_id', order_id)
s3.put_object(
Bucket=BUCKET_NAME,
Key=f'reports/{order_id}.txt',
Body=f'order {order_id} processed'.encode('utf-8'),
)
def lambda_handler(event, context):
order_id = event.get('order_id', 'unknown')
slow_process()
save_report(order_id)
print(f'processed order {order_id}')
return {'statusCode': 200, 'body': f'processed order {order_id}'}
- コードエディタ左側の「DEPLOY」セクションにある「Deploy」をクリックし、関数が更新されたことを確認します
## slow_process と ## save_report は @xray_recorder.capture でそれぞれ 1 つのサブセグメントになり、save_report の中の put_object は patch_all() によって S3 のサブセグメントとして記録されます。
実行ロールにバケットへの書き込み権限を付ける
- 「設定」タブ →「アクセス権限」を選択し、「実行ロール」のロール名のリンクをクリックして IAM コンソールでロールを開きます
- 「許可」タブで「許可を追加」→「インラインポリシーを作成」をクリックします
- ポリシーエディタで「JSON」を選択し、以下の内容に書き換えます(
<アカウントID>は自分のアカウント ID に置き換えます)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "BasicsXrayReportsPut",
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::basics-xray-<アカウントID>/reports/*"
}
]
}
- 「次へ」をクリックし、ポリシー名に
basics-xray-s3-putを入力して「ポリシーの作成」をクリックします
Lambda サービストレースを有効にする
- Lambda コンソールの
basics-xray-functionに戻り、「設定」タブ →「モニタリングおよび運用ツール」を選択します - 「その他の監視ツール」の「編集」をクリックします
- 「CloudWatch アプリケーションシグナルと AWS X-Ray」の「Lambda サービストレース」で「有効化」にチェックを入れます(「アプリケーションシグナル」はオフのままにします)
- 「保存」をクリックします
Lambda コンソールでトレースを有効にすると、X-Ray にトレースデータを送るための権限(xray:PutTraceSegments と xray:PutTelemetryRecords)を許可する AWSLambdaTracerAccessExecutionRole-… というポリシーが実行ロールに自動でアタッチされます。
テスト実行する
- 「テスト」タブを選択し、「新しいイベントを作成」が選択されていることを確認します
- イベント名に
order001を入力し、イベント JSON を以下に書き換えます
{
"order_id": "A001"
}
- 「保存」をクリックしてから「テスト」をクリックします
- 「実行中の関数: 成功」と表示されることを確認し、「詳細」を展開して、
processed order A001のレスポンスと、2 秒を超える「所要時間」が表示されていることを確認します
- 数秒待ってから、もう一度「テスト」をクリックします(2 回目は実行環境が再利用され、初期化のフェーズを含まないトレースになります)
CloudWatch コンソールでトレースを探す
- 上部の検索バーに
CloudWatchと入力し、表示された「CloudWatch」を選択します - 左側ナビゲーションの「Application Signals (APM)」→「トレース」を選択します
- 時間範囲を「15分」にします
- 「クエリリファイナー」の「ノード」の一覧で、
basics-xray-function(種類は「Lambda Function」)にチェックを入れ、「クエリに追加」をクリックして、basics-xray-functionだけに絞り込みます - 「クエリの実行」をクリックします
- 「トレース」セクションに、テスト実行した 2 回分のトレースが表示されることを確認します(
basics-xray-function以外の関数のトレースが同じアカウントにある場合も、この絞り込みで混ざらなくなります)
basics-xray-function のトレースが 2 件表示され、それぞれの応答時間が 2 秒を超えている状態)タイムラインで遅い区間を特定する
- 一覧から 2 回目に実行したトレースの ID をクリックし、トレースの詳細を開きます
- 「セグメントのタイムライン」の
AWS::Lambda::Functionのグループで、basics-xray-functionの下に並ぶサブセグメントの「所要時間」と横棒の長さを確認します
## slow_process の横棒が約 2 秒と長く、## save_report とその下の S3 のサブセグメントが短い状態)## slow_process がおよそ 2 秒を占めている一方、## save_report と、その中の S3 の PutObject の呼び出しは短時間で終わっていることが確認できます。関数全体の所要時間を押し上げているのは S3 の呼び出しではなく、自分のコードの slow_process だと特定できました。
- タイムラインで
## save_reportの下にあるS3のサブセグメントをクリックし、右側に開く「セグメントの詳細: S3」パネルの「リソース」タブで、呼び出したオペレーション(PutObject)が記録されていることを確認します ## save_reportのサブセグメント名をクリックし、「注釈」タブにorder_idがA001として記録されていることを確認します
PutObject が表示されている状態)- 「トレースの詳細」の上部に表示されるトレースマップで、
basics-xray-function(Lambda Function)から S3 へのエッジが描かれていることも確認できます - 1 回目に実行したトレースも開くと、
AWS::Lambda::FunctionのグループにInit(初期化。コールドスタート)の行が追加で表示され、1 秒前後かかっていることが確認できます