AWS X-RayでAWS SNSやSQSを介したリクエストをトレースしてみる

# はじめに
分散トレーシングについて勉強したので、AWSのサービスであるAWS X-rayで遊んでみようと思います。Amazon SNSのX-Rayアクティブトレースのサポートの開始が2023年2月と割と最近だったので[^1]、API Gateway → Lambda → SNS → SQS → Lambdaまでのトレースを繋げてみました。

# AWS X-rayとは
AWSが提供する分散トレーシングのためのサービスです。アプリケーションのパフォーマンスを測定するなどに利用できます。詳しくは、公式のドキュメントを参照してください。

https://docs.aws.amazon.com/ja_jp/xray/latest/devguide/aws-xray.html

# 実験の構成
API GatewayからLambda(testLambda1)を呼び出し、SNSとSQSを経由して別のLambda(testLambda2, testLambda3)が起動するまでのリクエストを繋げてみます。ざっくりとした構成図は図のとおりです。基本的にはデフォルトの設定で作成していますが、一部設定を変更している箇所があるので、そこだけピックアップします。

![aws実験構成図.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118168/095d2a5d-c397-0ad9-6718-4fed2e8b3d05.png)

## API Gateway
API Gatewayの「Enable X-ray Tracing」にチェックをいれます。

![API_Gateway_Enable_X-ray_Tracing.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118168/cb5a3eca-9761-6d0b-c2c5-802b48cf1655.png)

## Lambda
設定から、アクティブトレースを有効にします。

![Lambda_X-ray_Tracing.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118168/bdf84c90-01ff-5326-40f1-1525637b1f4c.png)

### 実験に利用したAWS Lambdaのコード

AWS Lambdaのデフォルトで利用できるbotocoreのバージョンが低いので、botocoreのバージョンを最新にします。botocoreのバージョンを上げる理由は後述します。

今回の実験で使うAWS Lambdaは基本的にsnsへのpublishとsqsのメッセージを受け取るだけの処理をおこなっているだけです。

```python:testLambda1
import json
import botocore
import boto3
import os

def lambda_handler(event, context):
    s3 = boto3.client('s3')
    sns = boto3.client('sns')
    
    topic_arn = 'SNS_TOPIC_ARN' # sns_topicのARNを設定
    message = 'hoge'

    response = sns.publish(
        TopicArn=topic_arn,
        Message=message
    )
    
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
```

```python:testLambda2
import json

def lambda_handler(event, context):
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
```

```python:testLambda3
import json
import boto3

def lambda_handler(event, context):
    sqs = boto3.client('sqs')
    queue_url = 'QUEUE_URL'　# queueのURLを設定
    
    response = sqs.receive_message(
        QueueUrl=queue_url,
        MaxNumberOfMessages=1
    )
    
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
```

## SNS
SNSの設定から、「アクティブトレースを使用する」を選択します。

![SNS_ActiveTrace.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118168/93a24c6d-280a-af17-3e45-8b7535a81128.png)

## 結果
一連のリクエストのトレースは、CloudWatch X−rayトレーシングのServiceMapから確認できます。
API Gatewayから始まり、SNSを経由して分かれるtestLambda2, testLambda3までのリクエストのトレースを確認することができました。

![Trace1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118168/ae0162e4-5610-f969-cc94-caab2e630df3.png)

各セグメントの詳細も確認することができます。

![Trace.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118168/f5924fc4-331f-219c-9c5f-1ac7c2ee12ec.png)

## つまづいた点
現在（2023年4月3日）のAWS Lambdaのランタイムにおけるbotocoreのバージョンが1.23.32です[^2]。
その場合だと、Lambda → SNS → Lambdaのリクエストが繋がらず、以下の図のとおりになりました。

![Trace2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118168/c4e00240-8708-6fd1-332d-9fd0b5d7d8c6.png)

botocoreのCHANGELOGを見てみるとSNSのActive Traceがサポートされたのがそのバージョン以降であり（以下にCHANGELOGからの引用）、実際にバージョンを上げることでリクエストが繋がることを確認できました。

> 1.29.69
> ...
> * api-change:``sns``: This release adds support for SNS X-Ray active tracing as well as other updates.

https://github.com/boto/botocore/blob/develop/CHANGELOG.rst

### 追記（2023/05/22）
AWS Lambdaのランタイムのバージョンが上がったのでこの手順は必要ないかもしれません。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtimes.html

# SDKなどを使ってもう少し詳しくトレースを取ってみる

SDKなどを用いることで、アプリケーションをより詳しく計測することができます。大きく2つの方法があると思います。

- AWS X-ray SDK
- AWSDistro for OpenTelemetry

詳しくはドキュメントを参考にしてください。

https://docs.aws.amazon.com/ja_jp/xray/latest/devguide/xray-instrumenting-your-app.html

今回は、AWS X-ray SDKを使って簡単にS3へアクセスしている場合について確認してみようと思います。


```python:testLambda3
import json
import boto3
from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.core import patch_all

patch_all()

def lambda_handler(event, context):
    xray_recorder.begin_segment("sdk test")
    sqs = boto3.client('sqs')
    queue_url = 'QUEUE_URL'　# queueのURLを設定
    
    response = sqs.receive_message(
        QueueUrl=queue_url,
        MaxNumberOfMessages=1
    )

    s3 = boto3.client('s3')

    # 保存するデータ
    data = "Hello, World!"

    # S3バケット名と保存先のオブジェクトキーを指定
    bucket_name = 'my-bucket'   # bucket名
    object_key = 'example.txt'

    # S3にデータを保存
    s3.put_object(Bucket=bucket_name, Key=object_key, Body=data)
    
    xray_recorder.end_segment()    

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
```

testLambda3の方にS3へのアクセスが伸びていることが確認できました。

![S3 Trace.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118168/bd5a177a-d130-e274-f641-7708ba9c269a.png)

# AWS SNSのActive Traceをオフにしてみる

AWS SNSのActive TraceをオフにするとSNSのノードが消えました。すでにトレースが設定されているリクエストの場合は、Active Traceが設定されていなくともパススルーしてくれるそうです。

https://docs.aws.amazon.com/ja_jp/sns/latest/dg/sns-active-tracing.html

![Trace3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118168/ce8602cb-03a2-4718-7fd1-3c29a810646f.png)

# おわりに
今回の実験ではAWSの操作をChatGPTを使って色々聞いてみました。権限を追加する際に答えとして帰ってきたポリシーをそのまま使えたり、AWSの操作が曖昧なところの最初のアクションを起こしやすかったのでやりやすかったです。しかし、下の図のようにたまにそれっぽい嘘を言うので面白いですね。

![スクリーンショット 2023-03-20 10.43.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118168/a9cd229d-596b-43fa-2ab6-19915113c7d3.png)


[^1]: https://aws.amazon.com/jp/about-aws/whats-new/2023/02/amazon-sns-x-ray-active-tracing-visualize-analyze-debug-application-performance/

[^2]: https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtimes.html