[アップデート]S3からダウンストリームサービスに対してX-Rayのトレースコンテキストを伝搬できるようになりました

Lambdaのトレースが捗ります
2020.11.18

CX事業本部@大阪の岩田です。

11/16付けのアップデートにより、S3からダウンストリームサービスに対してX-Rayのトレースコンテキストを伝搬できるようになりました。

S3のイベント通知機能を利用すると、S3バケット上で発生したイベントを

  • SNS
  • SQS
  • Lambda

といったサービスに対して伝搬することが可能ですが、今回のアップデートによりイベント伝搬時にS3側でX-Rayのトレースヘッダをよしなに処理してくれるようになりました。これによりアプリケーション全体を通したエンドツーエンドのトレースが実現できます。開発者は他のサービス利用時と同様にX-RayのSDKを利用するか、もしくは自前でHTTPリクエストヘッダX-Amzn-Trace-Idを設定することでトレースが可能です。

注意点としては、S3がやってくれるのはあくまでトレースヘッダの伝搬だけです。特にイベント通知先が設定されていないバケットに対してPutObjectする際にX-Amzn-Trace-Idを設定してもX-Rayにトレースデータは送信されません。トレースデータの送信はLambdaなどのダウンストリームサービス側で実施する必要があります。

やってみる

実際にLambda × S3の組み合わせでX-Rayのトレースを試してみます。今回は以下の環境で試しています

  • Lambdaのランタイム:Python3.8
    • X-Rayのトレースを有効化
  • X-Ray SDK : 2.6.0

まずは下準備としてX-Ray SDKを内包するレイヤーを作成します。

$ pip install aws-xray-sdk -t python
$ zip -r  layer.zip python
$ aws lambda publish-layer-version --zip-file fileb://layer.zip --layer-name xray-sdk --compatible-runtimes python3.8

以後利用するLambdaにはこのレイヤーを紐付けてX-Ray SDKを利用できるようにしています。

LambdaからPutObjectして、さらにLambaをトリガーしてみる

まずはシンプルにLambdaからS3にPutObjectしてみます。PutObject先のバケットはさらに別のLambdaをトリガーするよう事前に設定しておきます。 以下のコードでPutObjectを実行するLambda Functionを作成し、テストイベントを実行します。

import json

import boto3
from aws_xray_sdk.core import patch_all

patch_all()
s3_client = boto3.client('s3')

def lambda_handler(event, context):

    body = json.dumps({
        'key1': 'val1',
        'key2': 'val2',
        'key3': 'val3',
    })

    s3_client.put_object(Bucket='<バケット名>', Key='<適当なオブジェクトキー>', Body=body)

    return {
        'statusCode': 200,
        'body': 'Hello from Lambda!'
    }

X-Rayのコンソールからトレース結果を確認してみます。

X-Rayのトレース結果1
  • PutObjectするLambda
  • S3バケットのPutObjectからトリガーされるLambda

2つのLambdaを紐付けてしっかりトレースできていることが分かります。

Lambdaから発行したPresigned-URLに対してクライアントからPutObjectして、さらにLambaをトリガーしてみる

もう少し複雑なシナリオも試してみましょう。以下のような処理を想定しています。

  • LambdaからPresigned-URLを発行
  • 発行されたPresigned-URLを利用して、SPA等のクライアントからファイルアップロード処理(PutObject)を実行
  • ファイルアップロード(PutObject)完了をトリガーに、さらに別のLambdaが起動して処理を行う

まずはPresigned-URL発行用のLambdaです

import json
import os

from botocore.client import Config
import boto3
from aws_xray_sdk.core import patch_all


patch_all()
s3_client = boto3.client('s3', config=Config(signature_version='s3v4'))

def lambda_handler(event, context):

    bucket = <適当なS3バケット>
    obj_key = <適当なオブジェクトキー>
    url = s3_client.generate_presigned_url(ClientMethod='put_object', Params={'Bucket': bucket, 'Key': obj_key})
    
    return {
            'url': url,
            'xray-trace-id': os.getenv('_X_AMZN_TRACE_ID')
        }

このPresigned-URL発行用Lambdaを実行すると以下のようなレスポンスが返却されます。

{
  "url": "https://<S3バケット名>.s3.amazonaws.com/<オブジェクトキー>
  "xray-trace-id": "Root=1-5fb4631d-2326f8eb1c1d621e0887f184;Parent=cb706fc48bc18be5;Sampled=1"
}

レスポンスのxray-trace-idにセットされたルートトレースIDを後続処理でも利用することで、エンドツーエンドのトレースが可能になります。

発行されたPresigned-URLに対してcurlコマンドでファイルアップロードを実行してみましょう。リクエストヘッダのX-Amzn-Trace-Idには先程のPresigned-URL発行用Lambdaから出力されたトレースヘッダ(例: Root=1-5fb3ea8b-2931b7327d944b1e068cc275;Parent=57b80d29d09e35d7;Sampled=1等)をそのまま設定して下さい。また、ファイルアップロード先のバケットはさらに別のLambdaをトリガーするよう事前に設定しておきます。

$curl "<発行されたPresigned-URL>"  --upload-file <アップロードする適当なファイル> -H "X-Amzn-Trace-Id: <トレースヘッダ>"

ファイルアップロードが完了すると、後続のLambdaが自動起動しているはずです。少し待ってからX-Rayのトレース結果を確認してみます。

X-Rayのトレース結果2
  • Presigned-URL発行用Lambda
  • S3のPutObjectをトリガーに起動するLambda

2つのLambdaを紐付けてエンドツーエンドでトレースできていることが分かります。Lambda間の空白の時間はざっくりとクライアント側でのアップロード所要時間と見なして良いでしょう。冒頭でも記載したとおり、S3側ではあくまでもトレースヘッダの伝搬しか行わないので、PutObjectの所要時間などを詳細にトレースしたい場合はクライアント側でX-Rayに対してトレースデータを送信してもらう必要があります。

まとめ

S3 × X-Rayの連携に関するアップデートを紹介しました。S3はAWSの鉄板サービスで、Lambdaから利用する機会も多いと思います。アプリケーションのトレーサビリティーを向上させるために、今回のアップデートを活用してX-Rayとの統合を検討してみてはいかがでしょうか?