0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

第2回:AWS SAMでサンプルアプリケーションを構築(X-Rayの検証の前段として)ADOT / X-RayによるLambdaの自動計装

0
Last updated at Posted at 2025-10-09

お題目

前回の記事でAWS SAMを利用して作成したLambda関数にADOTを追加することで、
アプリを改修することなくパフォーマンス情報を収集します。
今回はOTel(ADOT)を利用して、X-Rayにテレメトリーデータを連携します。

自動計装のための技術

各用語の解説になります。
まず、以降で説明するためのシーケンス図だけ掲載します。

OTel(Open Telemetry)とは

アプリケーション内部の挙動を詳細に観測するための、オープンなフレームワークです。
ベンダーに依存することなく、テレメトリーデータ(測定データ)を生成、収集、管理、エクスポートするための取り決めです。

トレース

1回のリクエスト全体を表します(クライアントからのリクエスト〜レスポンスまで)。
例えば、上記シーケンス図の(1)から(7)までを1つのシーケンスとして扱います。

(親)スパン

アプリケーション内部の処理単位です。
例えば、上記シーケンス図の(2)から(6)までを1つのスパンとして扱います。

(子)スパン

親スパンを細分化し、詳細な処理ごとに分けた単位です。
例えば、上記シーケンス図の(3)、(4)、(5)の1つ1つを指します。

X-Rayとは

分散トレーシングを実現するための、AWS上のマネージドサービスです。
アプリケーションからOTelのテレメトリーデータを連携することで、収集、管理することができます。トレースを検索したり、トレース内の各スパンにどれだけの時間を要したか図で表示することができます。
※ X-RayではOTelで言うところの"スパン"を"セグメント"と言います。

ADOT(AWS Distro for OpenTelemetry)とは

概要

オープンソースのOpenTelemetryをベースにAWSが開発したツールです。
OpenTelemetryのテレメトリーデータをX-Rayで扱えるようにします。
ECSではサイドカーとして、LambdaではLambda Layerとして提供され、アプリケーションの外部に配置するだけで自動計装を実現できます。(ただし、セグメント(スパン)の粒度には限界があります。)スパンの粒度を詳細化するには、アプリケーションにSDKを組み込んで作り込む必要があります。

ADOT Node Layer(SDK)

計測対象のLambdaに対して、Lambda Layerとして追加することで、テレメトリーデータを生成してくれるコンポーネントです。AWSがリージョンごとに公開しているARNを指定します。

OTel Export

デフォルト127.0.0.1:4318でリッスンし、ADOT Node Layerが生成したテレメトリーデータを受け取ります。

ADOT Collector Extension

収集したテレメトリーデータをX-Rayに転送してくれるコンポーネントです。
AWSがリージョンごとに公開しているARNを指定します。

検証

前回記事でAWS SAMで作成したLambdaにADOTを追加していきます。
template.yamlを書き換えるだけで、アプリケーションに手は入れません。

template.yamlの編集

template.yamlにParametersセクションとConditionsセクションを追加して、ADOTのARNを渡せるようにします。
sam deployの引数として指定して後から上書きすることもできます。
ADOT Collector Extensionは任意としています。

Parameters:
  # 必須:ADOT Node.js ランタイム Layer の ARN(リージョン/アーキ用)
  AdotNodeLayerArn:
    Type: String
    Default: "arn:aws:lambda:ap-northeast-1:615299751070:layer:AWSOpenTelemetryDistroJs:9"
    Description: ADOT Node.js runtime layer ARN (match your region/arch)
  AdotCollectorLayerArn:
    Type: String
    Default: ""
    Description: ADOT Collector extension layer ARN (optional)

  # OTLP エンドポイント(Collector Extension使うなら http://127.0.0.1:4318 を推奨)
  OtelOtlpEndpoint:
    Type: String
    Default: http://127.0.0.1:4318
    Description: OTLP endpoint (e.g., http://127.0.0.1:4318 or https://collector.example:4318)

  # OTLP プロトコル
  OtelOtlpProtocol:
    Type: String
    AllowedValues: ["http/protobuf", "grpc"]
    Default: http/protobuf

Conditions:
  HasCollector: !Not [!Equals [!Ref AdotCollectorLayerArn, ""]]

同様にGlobalsセクションに、OTel関連の環境変数を追記します。

Globals:
  Function:
    Runtime: nodejs20.x
    Timeout: 10
    MemorySize: 128
    Architectures: [x86_64]
    Tracing: Active
    CodeUri: .
    Environment:
      Variables:
        TABLE_NAME: !Ref MessagesTable
        QUEUE_URL: !Ref InboundQueue

        # ---------- OTel共通 ----------
        AWS_LAMBDA_EXEC_WRAPPER: /opt/otel-handler
        OTEL_RESOURCE_ATTRIBUTES: !Sub 'service.namespace=${AWS::StackName},aws.region=${AWS::Region}'
        OTEL_TRACES_EXPORTER: otlp
        OTEL_METRICS_EXPORTER: otlp
        OTEL_LOGS_EXPORTER: otlp
        OTEL_EXPORTER_OTLP_ENDPOINT: !Ref OtelOtlpEndpoint
        OTEL_EXPORTER_OTLP_PROTOCOL: !Ref OtelOtlpProtocol
        OTEL_TRACES_SAMPLER: parentbased_always_on

それぞれのLambda関数の定義にLayersセクションを追加します。
下記はEnqueueの記述例です。

  # ---------- ① Enqueue ----------
  EnqueueFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub '${AWS::StackName}-Enqueue'
      Handler: src/enqueue/index.handler
      Layers:
        - !Sub '${AdotNodeLayerArn}'
        - !If [HasCollector, !Sub '${AdotCollectorLayerArn}', !Ref "AWS::NoValue"]
      Policies:
        - AWSLambdaBasicExecutionRole
        - SQSSendMessagePolicy:
            QueueName: !GetAtt InboundQueue.QueueName
      Events:
        EnqueueGet:
          Type: HttpApi
          Properties:
            ApiId: !Ref HttpApi
            Path: /enqueue
            Method: GET

ここで気づいた。
REST APIで作ればよかった。。。
API GatewayがHTTP APIの場合、X-Rayに連携できません。

検証なのでコスト削減ということで、このままHTTP APIでいきます。

SAMのデプロイ

ap-northeast-1のADOTのARNを指定してデプロイします。

sam build
sam deploy \
  --stack-name sam-test-stack \
  --region ap-northeast-1 \
  --capabilities CAPABILITY_IAM \
  --no-confirm-changeset \
  --resolve-s3 \
  --parameter-overrides \
    AdotNodeLayerArn="arn:aws:lambda:ap-northeast-1:615299751070:layer:AWSOpenTelemetryDistroJs:9"
    OtelOtlpEndpoint=http://127.0.0.1:4318 \
    OtelOtlpProtocol=http/protobuf

AdotNodeLayerArnで指定した

arn:aws:lambda:ap-northeast-1:615299751070:layer:AWSOpenTelemetryDistroJs:9

はCollectorが含まれているということなので、Collectorの指定は省略します。

確認

では確認。

Lambda実行

前回同様、curlコマンドでリクエストを投げます。
X-Rayに情報が連携されることの確認が目的のため、curlコマンドの実行結果は割愛します。

キューに突っ込む

curl -sS "APIのURL/enqueue?msg=test"

DynamoDBの中身を確認

curl -sS "APIのURL/items?limit=10" | jq .

X-Rayを確認

X-Rayのコンソールにトレースの一覧が表示されました。

スクリーンショット 2025-10-12 6.36.00.png

トレースの内容を見てみます。
「1.現在のトレース」と、「2.リンクされたトレース」の2つが図で表示されました。
スクリーンショット 2025-10-12 6.37.11.png

スクリーンショット 2025-10-12 6.38.44.png

上記の例では「1.現在のトレース」としてEnqueue Lambdaが表示されています。
「2.リンクされたトレース」はWorker Lambdaを指しているようです。
Enqueue Lambdaがクライアントから呼ばれたあと、「Lambda Context」と「Lambda Function」の2つのアイコンがあります。

Lambda Context

Lambda Cold/Warm Startも含んだ処理全体を表します。

Lambda Function

Handler関数の処理を表します。

上記例で行くと、Lambd Functionの実行時間にInitとOverheadの時間を足したものがLambda Contextになります。InitがColod/Warm Startの処理時間、OverheadがExtensionの起動・終了を指すようです。

SQS

Lambda FunctionはさらにSQSの処理を含んでいます。
sam-test-stack-EnqueueアイコンはLambda側からのSQSの呼び出しを表し、SQSアイコン側はSQSマネージドサービス側での処理を表しているようです。

DynamoDB

SQS同様に、「2.リンクされたトレース」側のWorker Lambda内の処理としてDynamoDBの処理も表示されています。

見えた課題

それぞれのLambdaの実行時間(Init、Overhead、Invoke(Handerの実行))は確認できましたが、

* Enqueue Lambda内で実行したSQSの処理時間
* Workder Lambda内で実行したDynamoDBへのアクセス時間

は取得できませんでした。
ADOT Layerを

AdotNodeLayerArn "arn:aws:lambda:ap-northeast-1:901920570463:layer:aws-otel-nodejs-amd64-ver-1-25-0:1"
AdotCollectorLayerArn "arn:aws:lambda:ap-northeast-1:901920570463:layer:aws-otel-collector-amd64-ver-0-102-0:1"

から

AdotNodeLayerArn "arn:aws:lambda:ap-northeast-1:615299751070:layer:AWSOpenTelemetryDistroJs:9"
AdotCollectorLayerArn "" (指定しない)

に変更し

Layerのハンドラを

AWS_LAMBDA_EXEC_WRAPPER: /opt/otel-handler

から

AWS_LAMBDA_EXEC_WRAPPER: /opt/otel-instrument

に修正したところ、上記のとおりSQS、Lambda含めて自動計装できました。

まとめ

ADOTを利用することで、アプリケーションを改修せずにある程度トレースを取得することはできました。
ただし、さらに細かいセグメント(スパン)の情報を計測するにはアプリケーションの改修が必要なようです。
もしかしてApplication Signalsでサービス自動検出すればできる???(またの機会に調べます)

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?