Amazon Web Services ブログ

コンテナイメージ内でLambda レイヤーと拡張機能を動作させる

この記事では、コンテナーイメージとしてパッケージ化され、デプロイされた Lambda 関数で AWS Lambda レイヤーと拡張機能を使用する方法について説明します。

以前はLambda 関数は.zip アーカイブとしてのみパッケージされていました。これにはAWS マネジメントコンソールで作成された関数が含まれます。今はLambda 関数をコンテナイメージとしてパッケージ化およびデプロイすることもできるようになりました。

Docker CLI などの使い慣れたコンテナーツールを Dockerfile を使用してイメージをローカルで構築、テスト、タグ付けすることができます。コンテナイメージを使用したLambda 関数は 10 GBのサイズまで構築することが可能です。構築したイメージはAmazon Elastic Container Registry(ECR)リポジトリにプッシュします。このリポジトリは、AWSのフルマネージドなコンテナイメージレジストリサービスです。レジストリからECRイメージ URL をソースコードとして指定し、Lambda 関数を作成します。

Lambda Container Image Support

 

コンテナイメージとしてパッケージ化された Lambda 関数は関数設定へのLambda レイヤーの追加をサポートしていません。ただし、コンテナイメージで Lambda レイヤーの機能を使用するソリューションはあります。あなた自身がビルドプロセス中に、好みのランタイムと依存関係をコンテナイメージの一部としてパッケージ化する作業を行います。

 

Lambda レイヤーと拡張機能が.zip アーカイブとしてどのように機能するかを理解する

.zip アーカイブを使用して関数コードをデプロイする場合、ライブラリ、カスタムランタイム、およびその他の関数依存関係の配布メカニズムとして Lambda レイヤーを使用できます。

関数に 1 つ以上のレイヤーを含めると、初期化時に、各レイヤーの内容が関数実行環境の /opt ディレクトリに展開されます。各ランタイムは、言語に応じて階層が異なりますが/opt 配下のライブラリを走査します。関数ごとに最大 5 つのレイヤーを含めることができます。このレイヤーは、解凍されたデプロイパッケージのサイズ制限 (250 MB) にカウントされます。レイヤーは自動的にプライベートとして設定されますが、他の AWS アカウントと共有することも、パブリックに共有することもできます。

拡張機能は、Lambda 関数を拡張する方法であり、Lambda レイヤーとしてデプロイされます。Lambda拡張機能を使用すると、お好みのモニタリング、監視機能、セキュリティ、ガバナンスツールの機能を関数と統合することができます。AWS、AWS Lambda Readyパートナー、AWS パートナーが提供する幅広いツールセットから選択することも、独自の Lambda拡張機能を作成することもできます。詳細については、「AWS Lambda Extensions(プレビュー)のご紹介」を参照してください。

Lambda拡張機能は、内部と外部の 2 つのモードのいずれかで実行できます。外部拡張機能は、実行環境で独立したプロセスとして実行されます。ランタイムプロセスより前に開始でき、関数の呼び出しが完全に処理された後に続行できます。内部拡張機能は、ランタイムプロセスの一部としてコードとともにインプロセスで実行されます。

Lambda は /opt/extensions ディレクトリを検索し、見つかった全ての拡張機能の初期化を開始します。拡張機能は、バイナリまたはスクリプトとして実行可能である必要があります。関数コードディレクトリは読み取り専用であるため、拡張機能は関数コードを変更できません。

Lambda レイヤーと拡張機能は、単に関数の初期化中に実行環境内の特定のファイルパスにコピーされているファイルであると考えると理解するのに役立ちます。実行環境では、これらのファイルは読み取り専用です。

Lambdaのコンテナーイメージを理解する

コンテナイメージは、Dockerfile からパッケージ化されたテンプレートで構築されたものです。イメージは、親イメージやベースイメージ、またはゼロから開始して、Dockerfile のコマンドからアセンブリまたはビルドされます。各コマンドによって、イメージ内に新しいレイヤが作成され、前のレイヤの上に順番にスタックされます。パッケージ化されたテンプレートから構築したコンテナイメージは不変で読み取り専用になります。

Lambda の場合、コンテナイメージには、ベースオペレーティングシステム、ランタイム、Lambda 拡張機能、アプリケーションコード、およびその依存関係が含まれます。Lambda は、コンテナイメージの構築に使用できるオープンソースの基本イメージのセットを提供します。Lambda は、関数初期化中にイメージを使用して実行環境を構築します。Lambda はまたイメージを最適化し、関数の実行場所の近くにキャッシュするので、コールドスタート時間は.zip アーカイブの場合と同じになります。AWS Server Application Model (AWS SAM)CLI または Docker CLI などのネイティブコンテナツールを使用して、コンテナイメージをローカルで構築およびテストできます。

 

コンテナイメージでの Lambda レイヤーの使用

コンテナレイヤーはコンテナイメージに追加されます。これは、Lambda レイヤーを.zip アーカイブ関数に追加する方法に似ています。

コンテナイメージのレイヤリングを使用して Lambda 関数コンテナイメージに Lambda レイヤーの機能を追加する方法はいくつかあります。

 

Lambda レイヤーのコンテナイメージバージョンを使用する

Lambda レイヤーの発行者は、Lambda レイヤーと同等のコンテナイメージ形式を持つ場合があります。Lambda レイヤーと同じファイルパスを維持するには、公開されたコンテナイメージは /opt ディレクトリに同等のファイルが配置されていなければなりません。拡張機能を含むイメージには、/opt/extensions ディレクトリにファイルが含まれている必要があります。

ここで.zip アーカイブとしてパッケージ化され2 つのレイヤーと共に作成された関数を例に取ります。1 つのレイヤーは共有ライブラリを含み、もう 1 つのレイヤーは AWS パートナーからの Lambda 拡張機能です。

 

aws lambda create-function --region us-east-1 --function-name my-function \
 --role arn:aws:iam::123456789012:role/lambda-role \
 --layers \
"arn:aws:lambda:us-east-1:123456789012:layer:shared-lib-layer:1" \
"arn:aws:lambda:us-east-1:987654321987:extensions-layer:1" \
 …

コンテナイメージとしてパッケージ化された関数の対応する Dockerfile 構文には、次の行が含まれます。これらは、Lambda レイヤーのコンテナイメージバージョンをプルし、関数イメージにコピーします。共有ライブラリイメージは ECR から取得され、拡張機能イメージは Docker Hub からプルされます。

FROM public.ecr.aws/myrepo/shared-lib-layer:1 AS shared-lib-layer 
# Layer code 
WORKDIR /opt 
COPY --from=shared-lib-layer /opt/ . 

FROM aws-partner/extensions-layer:1 as extensions-layer 
# Extension code 
WORKDIR /opt/extensions 
COPY --from=extensions-layer /opt/extensions/ .

 

Lambda レイヤーの内容をコンテナイメージにコピーする

既存の Lambdaレイヤーを使用し、Docker build 時にレイヤーの内容を関数コンテナイメージの/opt ディレクトリにコピーできます。

これを自動ビルドプロセスで使用するには、AWS コマンドラインインターフェイスを含む Dockerfile を作成し、Amazon S3 から Layer ファイルをコピーします。

S3 からファイルをコピーするには、Dockerfile に認証情報を送信する必要があるため、このアプローチのセキュリティ面を考慮する必要があります。最終コンテナイメージに AWS 認証情報を保存しないよう常に確認してください。この例では、マルチステージのDockerビルドプロセスを使用して、認証情報を使用してレイヤーを取得し、以前のイメージレイヤーを無くすためにFROM scratchへ継続します。これにより、最終イメージから資格情報が削除されます。これは、docker historyを使用して確認することができます。アクセスが必要なリソースのみを対象とするアクセス許可を使用して、ビルド時のみに固有の認証情報を使用、保存してください。

この自動化されたアプローチの Dockerfile には、2 つのレイヤーを 1 つのイメージに追加するために、Lambda レイヤーの内容をコピーする次の行が含まれています。

FROM alpine:latest as layer-copy # Create a build stage to copy the files from S3 using credentials 

ARG AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-"us-east-1"} 
ARG AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-""} 
ARG AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-""} 
ENV AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION} 
ENV AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} 
ENV AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} 

RUN apk add aws-cli curl unzip 

RUN mkdir -p /opt 

RUN curl $(aws lambda get-layer-version-by-arn --arn arn:aws:lambda:us-east-1:1234567890123:layer:shared-lib-layer:1 --query 'Content.Location' --output text) --output layer.zip 
RUN unzip layer.zip -d /opt 
RUN rm layer.zip 

RUN curl $(aws lambda get-layer-version-by-arn --arn arn:aws:lambda:us-east-1:987654321987:extensions-layer:1 --query 'Content.Location' --output text) --output layer.zip 
RUN unzip layer.zip -d /opt 
RUN rm layer.zip 

FROM scratch # Start second stage from blank image to squash all previous history, including credentials. 
WORKDIR /opt 
COPY --from=layer-copy /opt .

 

Dockerfile 内で AWS CLI を実行するには、AWS_ACCESS_KEYAWS_SECRET_ACCESS_KEY を指定し、コマンドライン引数として必要な AWS_DEFAULT_REGION を含めます。最小限のアクセス資格情報を安全に使用し、保存してください。

docker build . -t layer-image1:latest \
 --build-arg AWS_DEFAULT_REGION=us-east-1 \
 --build-arg AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE \
 --build-arg AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

認証情報がイメージに保存されていないことを確認します。

docker history layer-image1:latest

これによって既存の Lambda レイヤーと拡張機能ファイルを含むコンテナイメージが作成されました。ECR にプッシュして関数で使用することができます。

 

Lambda レイヤーからコンテナイメージを構築する

Lambda レイヤーファイルの内容を再パッケージ化しコンテナイメージとして公開可能になりました。レイヤーごとに別々のコンテナイメージを作成すると、それらを複数の関数に追加し、Lambda レイヤーと同様の方法で共有することができます。

1 つのレイヤのファイルを含む別のコンテナイメージを作成することも、複数のレイヤのファイルを 1 つのイメージに結合することもできます。レイヤーファイル用に個別のコンテナイメージを作成する場合は、これらのイメージを関数イメージに追加します。

言語コードの依存関係を管理するには、2 つの方法があります。依存関係を事前にビルドしてコンテナイメージにファイルをコピーするか、docker buildの実行中に依存関係をビルドすることができます。

この例では、既存の Python アプリケーションを移行します。これは Lambda 関数と拡張機能で構成され、.zip アーカイブから関数と拡張機能のコンテナイメージを分離します。拡張機能は S3 にログを書き込みます。

リポジトリにイメージを格納する方法を選択できます。両方のイメージを、異なるイメージタグを使用して同じ ECR リポジトリにプッシュするか、別のリポジトリにプッシュできます。この例では、別個の ECR リポジトリを使用します。

サンプルコードをセットアップするためにGitHub リポジトリにアクセスし、readme.md ファイルの指示に従います。

既存のサンプルの拡張機能ではmakefile を使用してrequirements.txt ファイルとともに pip install を使用して boto3 をインストールします。これは、docker build プロセスに移行されます。ビルドプロセスの一部として pip install を実行できるようにするには、Python ランタイムを追加する必要があります。Python: 3.8-alpineを最小限のベースイメージとして使用できます。

関数と拡張機能用に別々の Dockerfile を作成します。拡張機能用 Dockerfile には次の行が含まれます。

FROM python:3.8-alpine AS installer 
#Layer Code 
COPY extensionssrc /opt/ 
COPY extensionssrc/requirements.txt /opt/ 
RUN pip install -r /opt/requirements.txt -t /opt/extensions/lib 

FROM scratch AS base 
WORKDIR /opt/extensions 
COPY --from=installer /opt/extensions .

拡張機能コンテナイメージをビルド、タグ付け、ログインして既存の ECR リポジトリにプッシュします。

docker build -t log-extension-image:latest . 
docker tag log-extension-image:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/log-extension-image:latest 
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com 
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/log-extension-image:latest

関数用Dockerfile には、先に作成した拡張機能イメージのファイルを関数イメージに追加します。追加の依存関係を必要としないため、関数に対してpip installを実行する必要はありません。

FROM 123456789012.dkr.ecr.us-east-1.amazonaws.com/log-extension-image:latest AS layer
FROM public.ecr.aws/lambda/python:3.8 
# Layer code 
WORKDIR /opt 
COPY --from=layer /opt/ . 
# Function code 
WORKDIR /var/task 
COPY app.py . 
CMD ["app.lambda_handler"]

関数コンテナイメージをビルドし、タグ付けし、個別に既存の ECR リポジトリにプッシュします。これにより、Lambda 関数のイミュータブルなイメージが作成されます。

docker build -t log-extension-function:latest . 
docker tag log-extension-function:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/log-extension-function:latest 
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/log-extension-function:latest

この関数では、ログファイルを保存するための一意の S3 バケットが必要です。これはS3 コンソールで作成します。ECR リポジトリイメージから Lambda 関数を作成し、バケット名を Lambda 環境変数として指定します。

aws lambda create-function --region us-east-1 --function-name log-extension-function \
 --package-type Image --code ImageUri=123456789012.dkr.ecr.us-east-1.amazonaws.com/log-extension-function:latest \
 --role "arn:aws:iam:: 123456789012:role/lambda-role" \
 --environment "Variables": {"S3_BUCKET_NAME": "s3-logs-extension-demo-logextensionsbucket-us-east-1"}

続く拡張機能のコード変更では、拡張機能イメージと関数イメージの両方を更新する必要があります。関数コードだけが変更された場合は、関数イメージを更新する必要があります。関数イメージを :latest イメージとしてECRにプッシュします。次に更新された :latestのECR イメージを使用するように、関数コードのデプロイを更新します。

aws lambda update-function-code --function-name log-extension-function --image-uri 123456789012.dkr.ecr.us-east-1.amazonaws.com/log-extension-function:latest

 

コンテナイメージでのカスタムランタイムの使用

.zip アーカイブ関数では、Lambda レイヤーを利用してカスタムランタイムを追加します。コンテナイメージを使用すると、カスタムランタイムの Lambda レイヤーコードをコピーする必要がなくなります。

AWS が提供するカスタムランタイム用のベースイメージから始めて、独自のカスタムランタイムイメージを作成できます。これらのイメージには、任意のランタイム、依存関係、およびコードを追加できます。Lambda と通信するには、イメージにLambda Runtime API を実装する必要があります。AWSはサポートされているすべてのランタイム用に Lambda Runtime Interfae Clientsを提供しています。また追加されたランタイム用に独自のクライアントを実装することもできます。

 

コンテナーイメージで拡張機能の実行

コンテナイメージとしてパッケージ化された関数で実行される Lambda 拡張機能は、.zip アーカイブ関数と同じように機能します。拡張機能のファイルを含む関数コンテナイメージを構築するか、拡張機能イメージレイヤーを追加します。Lambdaは /opt/extensions ディレクトリ内の外部拡張機能を検索し、初期化を開始します。拡張機能は、バイナリまたはスクリプトとして実行する必要があります。

内部拡張機能は、言語固有の環境変数またはラッパースクリプトを使用して Lambda ランタイムのスタートアップの振る舞いを変更します。言語固有の環境変数の場合、関数設定で次の環境変数を設定して、ランタイムコマンドラインを拡張できます。

  • JAVA_TOOL_OPTIONS (Java Corretto 8 および11)
  • NODE_OPTIONS (Node.js 10 および 12)
  • DOTNET_STARTUP_HOOKS (.NET Core 3.1)

JAVA_TOOL_OPTIONSを利用したLambda環境変数の例:

-javaagent:"/opt/ExampleAgent-0.0.jar"

ラッパースクリプトは、ランタイムスタートアップをスクリプトに委譲します。スクリプトでは、引数の挿入と変更、環境変数の設定、メトリック、エラー、その他の診断情報の取得を行うことができます。次のランタイムは、ラッパースクリプトをサポートしています。

Node.js 10および12、Python 3.8、Ruby 2.7、Java 8 および11、.NET Core 3.1

スクリプトを指定するには、AWS_LAMBDA_EXEC_WRAPPER 環境変数の値を、実行可能バイナリまたはスクリプトのファイルシステムパスとして設定します。次に例を示します。

/opt/wrapper_script

 

結論

.zip アーカイブに加えて、Lambda 関数をコンテナイメージとしてパッケージ化およびデプロイできるようになりました。コンテナイメージとしてパッケージ化された Lambda 関数は、.zip アーカイブのように、関数設定に対してLambda レイヤーの追加を直接はサポートしません。

この記事では、Dockerfile のサンプルを含む、コンテナーイメージで Lambda レイヤーと拡張機能を使用する、いくつかのソリューションを紹介しました。

どのように既存の Lambda 関数と拡張機能をを.zip アーカイブから個別の関数と拡張機能コンテナイメージに移行するかを示しました。GitHub リポジトリreadme.md ファイルの指示に従ってください。

サーバーレスラーニングリソースの詳細については、https://serverlessland.com をご覧ください。

 

 

日本語訳はSA 福井 厚が担当しました。原文はこちらです。