Amazon Web Services ブログ

コンテナでデプロイした Lambda から OpenTelemetry でトレースを X-Ray に送る

はじめに

分散アプリケーションにおいてオブザーバビリティはパフォーマンスボトルネックやエラー率・アプリケーション全体が正常に稼働しているかを特定するのに重要な役目を果たします。コンテナでデプロイした AWS Lambda 関数を OpenTelemetry で計装しトレースを AWS X-Ray に送信するにはいくつか準備が必要です。このブログでは、OpenTelemetry Collector をビルドし、 Amazon ECR リポジトリにプッシュし、 Lambda のコンテナイメージにそれをコピーすることでこの問題を解決する方法を示します。

今回はセキュアで AWS がサポートする OpenTelemetry プロジェクトのディストリビューションである AWS Distro for OpenTelemetry を活用します。アプリケーションコードは OpenTelemetry SDK によって計装され、Lambda 関数の中で動作している OpenTelemetry Collector がトレースを収集し X-Ray に送信します。これによりコンテナでデプロイされた Lambda のパフォーマンスやアプリケーションが正常に動作しているかを監視できるようになります。

アーキテクチャ

コンテナ化した Lambda からトレースを送信するアーキテクチャの全体像をお示しします。

  1. 画像を Amazon S3 バケットにアップロードすると Lambda 関数が実行されます
  2. Lambda 関数では外部のコンポーネントに対して API コールを実行します(今回の例だと Amazon Rekognition)
  3. この処理は OpenTelemetry SDK によって計装されておりトレースを生成します
  4. Lambda Extension に追加されている OpenTelemetry Collector のなかの otlp receiver がトレースを受信し、awsxrayexporter を使って X-Ray にトレースが送信されます

今回の例ではトレースを可視化するために X-Ray を用いていますが、他のオブザーバビリティバックエンドに切り替えることも可能です。

全体アーキテクチャ

コンテナでデプロイした Lambda から X-Ray にトレースを送信するには

zip で Lambda をデプロイする場合は Lambda Layer を使って比較的簡単にトレースを送信することができます。一方でコンテナで Lambda をデプロイする場合は Lambda Layer を使うことができず、依存物全てをコンテナにパッケージングする必要があります。

このブログでは OpenTelemetry Collector Lambda Extension を独自にビルドし、ECR リポジトリに保存し、Lambda のコンテナイメージにそれをコピーします。これによりコンテナで Lambda をデプロイしつつ OpenTelemetry を使ってトレースを X-Ray に送信することができます。

OTLP Receiver のエンドポイントについて

ソースコードの中で OTLP Exporter のエンドポイントが設定されていないことに疑問を感じる方がいらっしゃるかもしれません。これについては OpenTelemetry Specification v1.38.0 時点では OTLP Exporter のデフォルトエンドポイントは localhost:4317 になっていて、今回ビルドした OpenTelemetry Lambda の Collector も OTLP Receiver が localhost:4317 で接続を受け付ける設定になっているため明示的にエンドポイントを設定していません。今回使用している opentelemetry-otlp クレートではドキュメントにあるように opentelemetry_otlp::new_exporter()
.tonic().with_endpoint() か環境変数 OTEL_EXPORTER_OTLP_ENDPOINT でエンドポイントを明示的に指定することもできます。

手順

こちらのリポジトリの README に書かれた手順を実行していきます。

事前に必要なもの

以下のツールが必要です。

OpenTelemetry Collector Lambda Layer をビルドする

Lambda からのトレースを収集し X-Ray などのバックエンドに送信するためには OpenTelemetry Collector が必要です。OpenTelemetry Collector Lambda Extension Layer には awsxrayexporter が含まれておらず、このままでは X-Ray にトレースを送信することができません。そこで依存ライブラリに awsxrayexporter を追加した上で OpenTelemetry Collector をビルドします。まずはリポジトリをクローンしましょう。

git clone https://github.com/aws-samples/opentelemetry-lambda-container.git
cd opentelemetry-lambda-container
git clone https://github.com/open-telemetry/opentelemetry-lambda.git -b layer-collector/0.4.0
cp -r opentelemetry-lambda/collector/ otel-collector-lambda-extension
rm -rf opentelemetry-lambda/

1. awsxrayexporter を依存ライブラリに追加します。 otel-collector-lambda-extension/lambdacomponents/go.mod を以下のように編集します。

cd otel-collector-lambda-extension/lambdacomponents/
go mod edit --require=github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsxrayexporter@v0.92.0 

2. otel-collector-lambda-extension/lambdacomponents/default.go を編集しawsxrayexporter を exporter に追加します。

import (
    // import awsxrayexporter
    "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsxrayexporter"
)

func Components(extensionID string) (otelcol.Factories, error) {
    ...
    exporters, err := exporter.MakeFactoryMap(
        // add awsxrayexporter here
        awsxrayexporter.NewFactory(),
        
        loggingexporter.NewFactory(),
        otlpexporter.NewFactory(),
        otlphttpexporter.NewFactory(),
        prometheusremotewriteexporter.NewFactory(),
    )
    ...

    return factories, multierr.Combine(errs...)
}

3. otel-collector-lambda-extension/Dockerfile を作成します。

FROM public.ecr.aws/docker/library/golang:1.20 as collector-builder
WORKDIR /src
COPY . .
RUN go mod tidy
RUN GO111MODULE=on CGO_ENABLED=0 installsuffix=cgo go build -trimpath -o collector .

FROM scratch
COPY --from=collector-builder /src/collector src/collector

OpenTelemetry Collector のコンテナイメージをビルドし、ECR リポジトリにプッシュして Lambda 関数が Collector を Lambda Extension として使えるようにします。手順の中では us-east-1 リージョンを使用しますが、AWS_DEFAULT_REGION で変更することも可能です。

export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
export AWS_DEFAULT_REGION=us-east-1
cd opentelemetry-lambda-container/otel-collector-lambda-extension
aws ecr create-repository --repository-name lambda-extension/otel-collector
aws ecr get-login-password | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com
docker build -t lambda-extension/otel-collector .
docker tag lambda-extension/otel-collector:latest ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/lambda-extension/otel-collector:v1
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/lambda-extension/otel-collector:v1

Collector を Lambda 関数に含める

opentelemetry-lambda-container/lambda-function/Dockerfile<Your AWS account id><AWS Region> をお使いの AWS アカウントの ID とリージョンに書き換えます。

FROM public.ecr.aws/docker/library/rust:1.76.0 as rust-builder

WORKDIR /rust/rust_app
COPY src/ /rust/rust_app/src/
COPY Cargo.toml /rust/rust_app/

RUN apt-get update
RUN apt-get install musl-tools -y
RUN rustup update && \
    rustup target add x86_64-unknown-linux-musl
RUN cargo build --release --target x86_64-unknown-linux-musl

FROM public.ecr.aws/lambda/provided:al2023

COPY --from=rust-builder /rust/rust_app/target/x86_64-unknown-linux-musl/release/bootstrap ${LAMBDA_RUNTIME_DIR}/bootstrap
COPY --from=<Your AWS account id>.dkr.ecr.<AWS Region>.amazonaws.com/lambda-extension/otel-collector:v1 /src/collector /opt/extensions/collector
COPY adot-config.yaml /opt/collector-config/config.yaml
CMD [ "lambda-handler" ]

AWS CDK でリソースをデプロイする

Lambda 関数とその他のリソースを CDK でデプロイします。S3 バケットに画像をアップロードすると Lambda 関数が実行されるように設定されており、CloudFormation に出力される RekognitionSourceBucketName がその S3 バケット名を表しています。

以下のコマンドでデプロイします。

cd opentelemetry-lambda-container/lambda-trace
npm install
cdk deploy

Lambda 関数を実行して動作確認する

以下のコマンドで画像ファイルを S3 バケットにアップロードします。

aws s3api put-object --bucket <RekognitionSourceBucketName> --key image.png --body <Some image file>

ここで生成される detect-label スパンが X-Ray のコンソール上で確認できます。

span の全体

ここで付与される label_num メタデータも確認できます。

span のメタデータ

おわりに

このブログでは OpenTelemetry Collector の設定をカスタマイズして X-Ray 向けの Exporter を追加してビルドすることで、コンテナでデプロイした Lambda 関数から X-Ray にトレースを送信する方法を示しました。より詳細な情報は AWS Distro for OpenTelemetry, AWS Distro for OpenTelemetry Lambda で確認することができます。 One Observability Workshop を試して AWS でのオブザーバビリティを手を動かして体感いただけます。

 

著者について

苅野 秀和(Hidekazu Karino)

飛行機の勉強をしていたのが気づいたらなんやかんやあって AWS に入社してました。現在はウェブ系のお客様の技術支援を行いながら Rust を書いています。
週末は美味しいクロワッサンを求めてパン屋を探訪しています。