Amazon Web Services ブログ

AWS Lambda Extensions (プレビュー) を構築する

本投稿は AWS サーバーレス アプリケーションのシニアデベロッパーアドボケートであるJulian Woodによる寄稿です。

AWS Lambda は使い慣れたモニタリング、オブザーバビリティ、セキュリティ、ガバナンスツールをお客様の Lambda と簡単に統合するための新たな手段として、Lambda Extensions のプレビューを発表しました。各種ツールは、Extensions を利用することでより深く Lambda 実行環境を制御し、そのライフサイクルに関与することができます。この単純化によって現在お客様がご利用中のツールを、より簡単に、アプリケーション・ポートフォリオに対して横断的に適用することが可能となります。

本稿では Lambda Extensions の仕組みや Lambda のライフサイクルの変化、および Extension を構築する方法について解説します。お客様の関数で Extensions を利用する方法については、関連するブログ “AWS Lambda Extentions(プレビュー)のご紹介” をご覧ください。

Extensions は新たに導入された Lambda Extensions API を利用して構築します。各種ツールは Extensions API を利用することで、関数の初期化、呼び出し、終了に対してより強くコントロールすることができます。この API は、 Lambda にカスタムランタイムを導入する際に利用される、既存の Lambda ランタイムインターフェースの上に構築されています。

お客様はアプリケーションパフォーマンス監視や機密管理、構成管理、脆弱性検知等のユースケースのためにAWSやAWS Lambda Ready パートナー、オープンソースプロジェクトによって提供される extensions を利用することができます。また自ら extensions を構築することで、Extensions API を使用してお客様固有のツールを統合することも可能です。

2020年10月08日時点で AppDynamics、Check Point、Datadog、Dynatrace、Epsagon、HashiCorp、Lumigo、New Relic、Thundra、Splunk、AWS AppConfig、そしてAmazon CloudWatch Lambda Insights の extensions をご利用いただけます。これらの詳細は AWS Lambda Extentions(プレビュー)のご紹介 をご覧ください。

Lambda実行環境

Lambda関数は実行環境と呼ばれるサンドボックス化された環境内で実行されます。実行環境は関数を他の関数から隔離し、メモリを含むリソースを関数の設定に基づいて提供します。

Lambda は自動的にコンピューティングリソースのライフサイクルを管理することで価値に対する支払いを実現します。関数の実行の合間には、 Lambda サービスは実行環境をフリーズさせます。その後の呼び出しのために Lambda サービスが必要とした場合、実行環境は解凍されます。

従来、ランタイムプロセスだけが実行環境のライフサイクルに影響を及ぼすことができました。この際ランタイムプロセスは、実行環境内に Lambdaサービスと通信するための HTTP API エンドポイントを提供する、ランタイムインターフェースと通信を行います。

Lambda and Runtime API

Lambda and Runtime API

ランタイムはランタイムインターフェースを利用することで Lambda に対して呼び出しイベントを要求し、関数コードへと提供します。そしてイベントの処理が完了すると Lambda サービスに対して通知を行います。その後 Lambda サービスは実行環境をフリーズします。

これまでラインタイムプロセスは Lambda 実行環境における2つの異なるフェーズを提示してきました: InitInvoke です。

1. Init (初期化): Init フェーズでは Lamba サービスはランタイムを初期化し、(メインハンドラー外のコードである) 関数の初期化コードを実行します。Init フェーズは初回呼び出し時、またはプロビジョニングされた同時実行数が有効化されている場合には呼び出しに前に発生します。

2. Invoke (呼び出し): Invoke フェーズでは、ランタイムはランタイムインターフェースを通じて Lambdaサービスに対して呼び出しイベントを要求し、関数ハンドラーを呼び出します。

関数が実行された後、Lambda サービスは実行環境をフリーズし、別の関数呼び出しに備えてしばらくの間これを維持します。

一定時間に渡って Lambda 関数が呼び出しを受けなかった場合、 Lambdaサービスは環境を終了、除去します。

Previous Lambda lifecycle

Previous Lambda lifecycle

Extensions API が追加されたことで、 extensions は実行環境のライフサイクルに対して関与し、制御し、また参加することも可能になりました。Extensions API を利用すると Lambda サービスがいつ実行環境をフリーズするかに影響を及ぼすことができます。

AWS Lambda execution environment with the Extensions API

AWS Lambda execution environment with the Extensions API

Extensions はランタイムや関数よりも前に初期化されます。そしてその後も関数と並行して実行され、関数呼び出し中にはより強いコントロールを与えられ、終了時にロジックを実行することもできます。

Extensions は Lambdaのライフサイクルに以下の変化を導入することで、Lambda サービスとの統合を可能にします。

  1. 更新された Init フェーズ. 3つの個別の Init タスクが導入されました: extensions Init、runtime Init、そして function Initです。これによってextensionsとランタイムが関数コードの実行前にセットアップタスクを実施するといった順序が作られます。
  2. 呼び出し中のさらなるコントロール. Invokeフェーズにおいて、ランタイムは以前のように呼び出しイベントを要求し、関数ハンドラーを呼び出します。加えて、extensions は Lambda サービスに対してライフサイクルイベントを要求することができるようになりました。extensions はこれらのライフサイクルイベントへの反応としてロジックを実行し、完了後に Lambaサービスへ応答することができます。Lambdaサービスはランタイムおよび全てのextensionsからの応答を確認した後に実行環境をフリーズします。このように、extensionsはフリーズ/解凍の振る舞いに影響を及ぼすことができます。
  3. Shutdown (終了) フェーズ: 実行環境が終了する際に extensions がクリーンに停止できるよう、新たに Shutdown フェーズが提示されるようになりました。
New Lambda lifecycle with extensions

New Lambda lifecycle with extensions

Lambda ライフサイクルの各フェーズは、 Lambda サービスからラインタイムおよび登録された全ての extensions に送られるイベントによって開始されます。ランタイムや extensions は、ランタイムインターフェースExtensions API を通じて Next 呼び出しイベントを要求することで処理の完了を伝えます。処理待ちのイベントが無くなると、Lambdaは実行環境と全ての extensions をフリーズします。

Lambda lifecycle for execution environment, runtime, extensions, and function.png

Lambda lifecycle for execution environment, runtime, extensions, and function.png

ライフサイクルフェーズや Extensions API に関する更なる情報はドキュメントをご確認ください。

Extensions はどのように提供し、実行されるのですか?

Extensionsは共有ライブラリや他の依存関係を内包したZIPアーカイブである Lambda レイヤーとしてデプロイします。

レイヤーを追加するためには AWS Management Console や AWS Command Line Interface (AWS CLI) の他、 AWS CloudFormationAWS Serverless Application Model (AWS SAM)、 Terraform といった infrastructure as code ツール群を利用することができます。

Lambdaサービスは、関数の実行環境を開始する際にLambdaレイヤーに含まれるExtensionのファイルを /opt ディレクトリへと抽出します。その後 Lambda は /opt/extensions ディレクトリに存在する extensions に対して初期化を開始します。Extensions は実行可能なバイナリかスクリプトである必要があります。また関数コードのディレクトリは読み取り専用であるため、extension が関数コードを変更することはできません。

Extensions は2つのモードのいずれかで実行できます: internal または external です。

  • Internal extensions はランタイププロセスの一部として、お客様のコードと同一プロセス内で実行されます。これらは個別のプロセスとはなりません。Internal extensionを利用することで、言語固有の環境変数やラッパースクリプトによってランタイムプロセスのスタートアップ処理を変更できます。言語固有の環境変数を利用することでJava Corretto 8 および 11、Node.js 10 および 12、.NET Core 3.1 などに対してオプションやツールを適用することができます。ラッパースクリプトを利用することでランタイムのスタートアップ処理を自身のスクリプトに移譲し、スタートアップ時の振る舞いをカスタマイズすることが可能になります。ラッパースクリプトはNode.js 10 および 12、Python 3.8、Ruby 2.7、Java 8 および 11、そして.NET Core 3.1 で利用することができます。詳しくは “Modifying-the-runtime-environment” をご覧ください。
  • External extensions では、Lambda 関数と同一の実行環境内にランタイムとは別個のプロセスを実行することが可能となります。External extensions はランタイムプロセスよりも前に開始し、ランタイムの終了後まで継続することができます。External extensions は Node.js 10 および 12、Python 3.7 および 3.8、Ruby 2.5 および 2.7、Java Corretto 8 および 11、.NET Core 3.1、そしてカスタムランタイムと共に利用することができます。

External extensions は関数と異なる言語で記述することも可能です。External extensions はコンパイル型言語を利用して、自己完結するバイナリとして実装することを推奨します。こうすることで全てのサポートされるランタイムに対して extension に互換性を持たせることができます。もし非コンパイル型言語を利用する場合は、確実に互換性のあるランタイムを extension に含めてください。

Extensionsは関数と同一の実行環境で実行されるため、CPUやメモリ、ディスクストレージなどのリソースは関数と共有されます。また共に関数の AWS Identity and Access Management (IAM) ロールを利用するため権限を共有し、さらには環境変数も共有されます。

Extensions利用時におけるリソースやセキュリティ、パフォーマンスに関する詳細は関連ブログ “AWS Lambda Extentions(プレビュー)のご紹介” をご覧ください。

ご自身でExtensionsを構築する際に利用可能なサンプルのExtensionsやラッパースクリプトは GitHub リポジトリ をご覧ください。

Extensionsを動かしてみる

このデモでは external extensions が関数やLambdaランタイムと深く統合される様子をご覧いただきます。デモの中では例として1つのextensionを持つLambda関数を、AWS CLIもしくはAWS SAMを利用して作成します。

この例ではexternal extensionがランタイムよりも前に開始し、Lambda関数の呼び出し中も実行され、ランタイム終了後に終了する様子をご覧いただきます。

ご自身でこの例をセットアップするには GitHub リポジトリ を訪問し、README.md に記載された指示に従ってください。

このLambda関数例では Amazon Linux 2 をベースとしたカスタムの provided.al2 ランタイムを使用します。カスタムランタイムを使用することでLambdaサービスとランタイムインターフェース、そして関数が通信する様子をより細かく描くことができます。Extensions は Lambaレイヤーを利用して提供されます。

ランタイム、関数、および extension はそれぞれのステータスイベントを Amazon CloudWatch Logs に記録します。extension は別個のプロセスとして初期化され、Extensions API を通じて関数呼び出しイベントを受信するまで待機します。extension はその後、次のイベント受信に向けた登録のためにAPI を再度呼び出すまで5秒間スリープします。extension のスリープは並行プロセス上の処理をシュミレートしたものです。実際にはこの間に、例えばテレメトリーデータを取得して外部のオブザーバビリティサービスへ送信するなどの処理が可能です。

Lambda関数が呼び出される際には、extension、ランタイム、および関数は以下のステップを実行します。ログの出力を元に各ステップをウォークスルーしてみましょう。

1. Lambdaサービスが設定されたextensionのLambdaレイヤーを追加します。その後 /opt/extensions フォルダーを探索し、extension1.sh という extension を発見します。extension の実行モジュールはランタイムの初期化より先に起動し、以下の API 呼び出しによって Extensions API に対して INVOKE および SHUTDOWN イベントを取得するための登録を行います。

curl -sS -LD "$HEADERS" -XPOST "http://${AWS_LAMBDA_RUNTIME_API}/2020-01-01/extension/register" --header "Lambda-Extension-Name: ${LAMBDA_EXTENSION_NAME}" -d "{ \"events\": [\"INVOKE\", \"SHUTDOWN\"]}" > $TMPFILE
Extension discovery, registration, and start

Extension discovery, registration, and start

2. Lambda のカスタムランタイム provided.al2bootstrap ファイルによって初期化されます。

Runtime initialization

Runtime initialization

3. ランタイムが以下の API 呼び出しによって、次のイベントを取得するためにランタイムインターフェースを呼び出します。HTTP リクエストはイベントを受信するまでブロックされます。

curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next" > $TMPFILE &

extension が Extensions API を呼び出し、次のイベントを待機します。この HTTP リクエストもイベントを受信するまでブロックされます。

curl -sS -L -XGET "http://${AWS_LAMBDA_RUNTIME_API}/2020-01-01/extension/event/next" --header "Lambda-Extension-Identifier: ${EXTENSION_ID}" > $TMPFILE &
Runtime and extension call APIs to get the next event

Runtime and extension call APIs to get the next event

4. Lambda サービスが呼び出しイベントを受信し、ランタイムインターフェースを使用してイベントのペイロードをランタイムへ送信します。Lambda サービスはまた、Extensions API を使用して呼び出しを伝えるためのイベントを extension へ送信します。

Runtime and extension receive event

Runtime and extension receive event

5. ランタイムが関数ハンドラーを呼び出し、関数はイベントのペイロードを受信します。

Runtime invokes handler

Runtime invokes handler

6. 関数がハンドラーのコードを実行します。Lambda ランタイムは関数の応答を受信し、以下の API 呼び出しによってランタイムインターフェースへ応答を返します。

curl -sS -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE" > $TMPFILE
Runtime receives function response and sends to Runtime API

Runtime receives function response and sends to Runtime API

7. Lambda ランタイムは次の呼び出しイベントを待機します。 (warm スタート)

Runtime waits for next event

Runtime waits for next event

8. extension が5秒間処理を継続することで、関連プロセスの処理をシュミレートします。extension が終了すると、次のイベントを待機するために、再度登録のために Extensions API を使用します。

Extension processing

Extension processing

9. 関数の呼び出しレポートがログに記録されます。

Function invocation report

Function invocation report

10. Lambda が実行環境を終了する際に、ランタイムインターフェースに終了イベントを送信します。

Lambda runtime shut down event

Lambda runtime shut down event

11. Lambda が extensions に対して終了イベントを送信します。extensionは処理を終了し、ランタイムの後に終了します。

Lambda extension shut down event

Lambda extension shut down event

このデモは Lambda のライフサイクルにおいてランタイムや関数、extension が取るステップを表したものです。

external extensionはランタイムよりも前に登録され、開始されます。Lambdaは受信した呼び出しイベントをランタイムに送り、その後呼び出しを知らせるイベントを extension に送信します。ランタイムは関数ハンドラーを呼び出し、extensionはイベントに対する独自の処理を行います。extension は関数の呼び出しが完了した後も処理を継続します。Lambdaが実行環境を終了する際に、ランタイムに終了イベントを送信します。その後ランタイムは extension が処理を終了できるよう、同様のイベントを extension にも送信します。

このフローのシーケンス図をご覧になるには Extensions API documentation を参照してください。

料金

xtensionsはLambda関数と同じ料金モデルで提供されます。Lambda関数をextensionsと共に利用する場合、処理したリクエスト数の他に、コードと全てのextensionを合計した、100ms単位のコンピューティング時間に対して支払いを行います。extensions の請求に関する更なる情報をご確認になられたい場合は Lambda よくある質問 ページをご覧ください。

まとめ

Lambda extensions によってお客様は Lambda の実行環境をより簡単に慣れ親しんだモニタリング、オブザーバビリティ、セキュリティ、ガバナンスツールと統合することが可能になります。

Extensions は関数の呼び出し前、呼び出し中、および呼び出し後に追加のコードを実行することができます。本日時点で AWS Lambda Ready パートナーからも extensions が提供されており、これらはアプリケーションパフォーマンス監視や機密管理、構成管理、脆弱性検知などのユースケースをカバーします。Extensions によって既存のツールをサーバーレスアプリケーションに対して利用することがより簡単になります。提供される extensions の詳細を確認するには、関連ブログ “AWS Lambda Extentions(プレビュー)のご紹介“ をご覧ください。

Extensions API を利用することで、独自のツールを統合するために自ら extensions を構築することも可能です。extensions やラッパースクリプトの例は GitHub リポジトリ をご覧ください。

この投稿でご紹介したウォークスルーデモをセットアップするには GitHub リポジトリ にある README.md ファイルの指示に従ってください。

Extensionsは以下のリージョンでプレビューとして提供されています: 米国東部(バージニア北部)、米国東部 (オハイオ)、US East (Ohio)、米国西部 (北カリフォルニア)、米国西部 (オレゴン)、AWS GovCloud (米国東部)、AWS GovCloud (米国西部)、カナダ (中部)、欧州 (アイルランド)、欧州 (ロンドン)、欧州 (パリ)、欧州 (フランクフルト)、欧州 (ストックホルム)、欧州 (ミラノ)、南米 (サンパウロ)、アフリカ (ケープタウン)、中東 (バーレーン)、アジアパシフィック (東京)、アジアパシフィック (ソウル)、アジアパシフィック (シンガポール)、アジアパシフィック (シドニー)、アジアパシフィック (ムンバイ)、アジアパシフィック (香港)

サーバーレスについてさらに学びたい方は https://serverlessland.com をご覧ください。

翻訳はソリューションアーキテクト石井が担当しました。原文はこちらです。