LoginSignup
21

More than 5 years have passed since last update.

AWS X-RayでLambdaとAWSリソースをトレースする(Python)

Last updated at Posted at 2018-02-24

X-Rayをちゃんと使ってみてなかったので、調べて使ってみました。
ブラックボックスピタゴラスイ◯ッチやめよう。

今回はAPI GatewayをイベントトリガーにしてLambdaファンクションをキックするようにします。

ソースたちはここに入っています。
https://github.com/yKarakita/lambda_xray

環境など

  • Servereless Framework 1.22.0
  • python3.6
  • aws-xray-sdk 0.95

サンプルファンクション

これを元にしてX-Rayを導入してみる。

handler.py
import json

def hello(event, context):

    body = {
        "message": "Hello!"
    }

    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    return response

X-Rayのトレース処理を追加

handler.py
import json
from aws_xray_sdk.core import xray_recorder


def hello(event, context):
    xray_recorder.begin_subsegment('response hello')

    body = {
        "message": "Hello!"
    }

    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    xray_recorder.end_subsegment()

    return response

デコレータを使う方法

デコレータを使っても同様のことができるのでこちらのほうがシンプルでいいかもしれない。

handler.py
...

@xray_recorder.capture('response hello')
def hello(event, context):
    # ... some code

デプロイ

X-RayのSDKはLambdaにデフォルトで組み込まれていないのでをデプロイパッケージに含める必要があります。
今回はServerlessのプラグインserverless-python-requirementsを使用して$ sls deploy時にrequirements.txtを読み込んで動的にデプロイパッケージを生成しています。

requirements.txt
aws-xray-sdk==0.95
$ sls deploy

APIをキック

$ curl https://<API Gatewayエンドポイント>/dev/hello
{"message": "Hello!"}

X-Rayコンソールから確認するとこんな感じ。
スクリーンショット 2018-02-24 09.22.10.png

スクリーンショット 2018-02-24 09.22.22.png

うまくトレースできてました。

Annotationを使ってみる

Annotationを使うと、Subsegmentを任意のキーで絞込みできるようになる。

書き方はこんな感じ。

    subsegment.put_annotation('key', 'value')
handler.py
...

@xray_recorder.capture('response hello')
def hello(event, context):
    # ... some code

    status_code = 200

    subsegment = xray_recorder.current_subsegment()
    subsegment.put_annotation('response_status_code', status_code)

コンソールからはこんな感じで確認できる。

スクリーンショット 2018-02-24 11.31.06.png

フィルタ式を使用した AWS X-Ray コンソールでのトレースの検索

X-Rayコンソールの入力フィールドにannotation.response_status_code = 200といれるとannotationのresponse_status_codeに200を入れてputしたものだけを絞りこめる。

スクリーンショット 2018-02-24 21.42.30.png

結果のグループ化もAnnootationが使える。
スクリーンショット 2018-02-24 21.43.49.png

これは便利そう!

メタデータを使ってみる

サブセグメントにメタデータを追加することができます。
書き方はこんな感じ

    subsegment.put_metadata('key', dict, 'namespace')
handler.py
import uuid

from aws_xray_sdk.core import xray_recorder

@xray_recorder.capture('response hello')
def hello(event, context):
    # ... some code

    subsegment.put_metadata('key01', {'id': str(uuid.uuid4())}, 'namespace01')
    subsegment.put_metadata('key02', {'id': str(uuid.uuid4())}, 'namespace01')

スクリーンショット 2018-02-24 11.23.45.png

うん、必要になったら使ってみようかな。

AWSサービスの処理を追跡してみる

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-x-ray.html
boto3 (セッションを使用している場合は botocore) にパッチを適用して、AWS のサービスにアクセスするクライアントを作成すると自動的に X-Ray によって追跡されるようにできます。

便利そう。

こんな感じでboto3にX-Rayのパッチを適用してあげることで、AWSリソースのトレースができるようになるみたいです。

import boto3
from aws_xray_sdk.core import patch

patch(['boto3'])

API Gateway -> Lambda -> S3 Putのサンプル。

handler.py
import json
import uuid
import os

import boto3
from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.core import patch

patch(['boto3'])

s3_client = boto3.client('s3')

bucket_name = os.environ['S3_BUCKET']


@xray_recorder.capture('handler')
def hello(event, context):
    keyword = event['queryStringParameters']['keyword']

    put_object_into_s3(bucket_name=bucket_name, key=str(uuid.uuid4()), body=keyword)

    response = {
        "statusCode": 200,
        "body": json.dumps({'message': 'ok'})
    }

    return response


@xray_recorder.capture('put_object')
def put_object_into_s3(bucket_name, key, body):
    subsegment = xray_recorder.current_subsegment()
    response = s3_client.put_object(Bucket=bucket_name, Key=key, Body=body)
    status_code = response['ResponseMetadata']['HTTPStatusCode']
    subsegment.put_annotation('put_response', status_code)
$ curl https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello?keyword=hogeeeeeeeeeeee
{"message": "ok"}

詳細とサービスマップはこんな感じ。うまくいってるっぽいすね。

スクリーンショット 2018-02-24 20.52.27.png

スクリーンショット 2018-02-24 20.53.07.png

非同期処理をトレースしたい

こんな感じの非同期処理だとどうだろ。

API Gateway
↓
Lambda(SNSにPubした後HTTPレスポンスを返す)
↓
SNS
↓
Lambda(SNSからメッセージをSubしてS3にPutする)
↓
S3

やってみます。
何も考えずにすべての関数にX-Rayデコレータをつけてみる。
まずAPI Gatewayからイベントを受け取ってSNSにPubするファンクション。

handler.py
import json
import os

import boto3
from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.core import patch

patch(['boto3'])

sns = boto3.resource('sns', 'ap-northeast-1')
sns_client = boto3.client('sns')

topic_arn = os.environ['SNS_TOPIC_ARN']

@xray_recorder.capture('handler')
def hello(event, context):
    keyword = event['queryStringParameters']['keyword']

    sns_client.publish(
        TopicArn=topic_arn,
        Message=keyword,
        Subject='object_body'
    )

    response = {
        "statusCode": 200,
        "body": json.dumps({'message': 'ok'})
    }

    return response

そしてSNSをSubして、S3にPutするファンクション。

put_s3.py
import uuid
import os

import boto3
from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.core import patch

patch(['boto3'])

s3_client = boto3.client('s3')

bucket_name = os.environ['S3_BUCKET']


@xray_recorder.capture('put_s3_handler')
def handler(event, context):
    subscribed_data = event['Records'][0]['Sns']
    body = subscribed_data['Message']

    put_object_into_s3(bucket_name, str(uuid.uuid4()), body)

@xray_recorder.capture('put_object')
def put_object_into_s3(bucket_name, bucket_key, body):
    subsegment = xray_recorder.current_subsegment()
    response = s3_client.put_object(Bucket=bucket_name, Key=bucket_key, Body=body)
    status_code = response['ResponseMetadata']['HTTPStatusCode']
    subsegment.put_annotation('put_response', status_code)
$ curl https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello?keyword=hogeeeeeeeeeeee
{"message": "ok"}

実行するとこんな感じでSNSより前とそれ以降が分かれてしまい、うまくトレースできませんでした。
スクリーンショット 2018-02-24 21.01.17.png

調べてみたところ現時点でこれをうまくトレースさせたい場合は、SNSとその次のLambdaの間に、もうひとつ別のLambdaを用意する必要があるとのことでした。
Lambda -> SNS -> Lambda -> Lambda

最初のLambdaで生成されたTrace IDをSNSで中継役のLambdaに渡して、そいつがTrace IDを含めて次のLambdaを起動すればいけるみたい。

こちらの記事で詳しく紹介されています。
AWS X-Rayで非同期メッセージをトレースする

まとめ

入れておこう。

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21