Lambda のコールドスタートと Snap Start 機能 (java向けの機能)を、ドキュメントと公式ブログから確認してみる

記事タイトルとURLをコピーする

こんにちは。🐱カスタマーサクセス課の山本です。
Lambda のコールドスタートと Snap Start 機能 (java向けの機能)を、ドキュメントと公式ブログから確認してみました。

参考リンク

公式ドキュメント:Lambda SnapStart による起動パフォーマンスの向上
公式ブログ:New – Lambda SnapStart で Lambda 関数を高速化

Snap Start 関連のブログです。

試してみた前編:
blog.serverworks.co.jp

試してみた後編:
blog.serverworks.co.jp

一意性チェックツール編:
blog.serverworks.co.jp

スナップショットの保存期間を超過させてみた編: blog.serverworks.co.jp

SnapStart が解決すること

公式ブログには以下の記述がございます。

すでに Lambda を使用している方ならご存知かもしれませんが、関数は安全で隔離された実行環境内で実行されます。環境の各ライフサイクルは、初期化、起動、シャットダウンの 3 つの主要フェーズで構成されています。とりわけ、Init フェーズは関数のランタイムをブートストラップし、関数の静的コードを実行します。多くの場合、これらの操作はミリ秒以内に完了し、フェーズがそれほど長くなることはありません。残りのケースでは、いくつかの理由でかなりの時間がかかることがあります。まず、一部の言語ではランタイムの初期化にコストがかかる場合があります。例えば、Java ランタイムのいずれかを Spring Boot、 Quarkus、 Micronaut などのフレームワークと組み合わせて使用する Lambda 関数のInit フェーズには、10 秒もかかることがあります (これには、依存性注入、関数のコードのコンパイル、クラスパスコンポーネントのスキャンが含まれます)。

すなわち、公式ドキュメントに記載のある、以下フェーズのうち、一番最初の「初期化フェーズ」を高速化する機能になります。

1. 初期化フェーズ
2. 復元フェーズ (Lambda SnapStart のみ)
3. 呼び出しフェーズ
4. 呼び出しフェーズ中の失敗
5. シャットダウンフェーズ

図の中では、以下の部分です。

初期化フェーズ

Lambda は「初期化フェーズ」では具体的に以下を行なっています。 以下、ドキュメントより引用します。

  • すべての拡張機能を起動する (Extension init)
  • ランタイムをブートストラップする (Runtime init)
  • 関数の静的コード (Function init) を実行する
  • 任意の beforeCheckpoint ランタイムフックを実行する ( Lambda SnapStart のみ )

もう少しわかりやすい表現が、「re:Invent 2021」の資料にあるので、そちらも引用します。

動画内の解説を聞くと、 「初期化フェーズ」では具体的に以下を行なっています。

  1. 関数実行用の仮想マシンを作成
  2. コードのダウンロード
  3. ランタイムの初期化(Node.js、Java のみ)
  4. ハンドラの外にあるコードの実行

なお、「初期化フェーズ」は Lambda 関数の最初の実行時に行なっています。
公式ドキュメントに記載はないものの、re:Invent 2021 のセッションにて、以下の解説がありました。(16分52秒〜)

From a first Invocation, that's gonna run the initialization process.
山本による和訳:ある最初の実行時に、「初期化フェーズ」を実行します。

また、「初期化フェーズ」を実行した環境はしばらくの間、利用可能になります。
2回目の実行時には、最初の実行時に「初期化フェーズ」を行なった仮想マシンを使用して、Lambda を実行します。

実行時に、「初期化フェーズ」が走る場合を コールドスタート
実行時に、「初期化フェーズ」を実行時済みの環境を使用する場合を、 ウォームスタート
と呼ぶようです。

コールドスタートとウォームスタートのイメージ(複数のイベントが同時に来た時):

「初期化フェーズ」を実行済みの環境は、AWS 側で管理しており、エラーが発生した環境やメンテナンスの都合によって、削除するようです。 そのため、削除タイミングは不定期になります。 なお、Lambda 関数の新しいバージョンを作成した場合には、コールドスタートが発生しますので、ご注意ください。

通常、初期化フェーズは 1〜100 ミリ秒で終わることが多いため、あまり問題にはなりません。 しかし、 ハンドラの外にあるコードの実行に時間がかかる場合には、時間がかかることがあります。 ダウンロードするライブラリやパッケージが大きい場合です。

参考:ハンドラの外にあるコード例:

例えば、Java ランタイムのいずれかを Spring Boot、 Quarkus、 Micronaut などのフレームワークと組み合わせて使用する Lambda 関数の初期化フェーズには、10 秒もかかることがあります。 (ブログより)

ご参考:ハンドラの外にあるコードに関するベストプラクティス

利用するライブラリのインポートをより細かく指定することにより、ダウンロードするパッケージ量が減り、早くすることもできます。
"aws-sdk" というインポートよりも、"aws-sdk/clients/dynamodb" というインポートの方が初期化の速度が上がります。

SnapStart の仕組み

SnapStart は上に説明した「初期化フェーズ」を行なった際に、 AWS側で実行環境のスナップショットを取得する機能です。 SnapStart を有効にすると、 関数の新しいバージョンを公開したタイミング で、「初期化フェーズ」を実行し、スナップショットを取得します。 そのため、 コールドスタートを回避し、多くの関数実行がウォームスタートになります。 このスナップショットは 14 日間、有効なようです。 また、SnapStart の使用に料金はかかりません。

Lambda SnapStart と「プロビジョニングされた同時実行」の比較

Lambda SnapStart と似た機能として、「プロビジョニングされた同時実行」という機能があります。 この機能は、ユーザーの指定する並列実行数分、あらかじめ「初期化フェーズ」を行い、ウォームアップしておく機能です。 「プロビジョニングされた同時実行」は料金がかかるため、コールドスタートを必ず避けたい場合に使用すると良さそうです。

引用:

Lambda SnapStart とプロビジョニングされた同時実行は、ど ちらも関数がスケールアップするときのコールドスタート時間と異常なレイテンシーを削減することができます。SnapStartは、起動パフォーマンスを最大 10 倍向上させるために役立ち、追加のコストはかかりません。プロビジョニングされた同時実行は、関数を、初期化され、2 桁ミリ秒台で応答できる状態に維持します。プロビジョニングされた同時実行を設定すると、AWS アカウントに料金が請求されます。プロビジョニングされた同時実行は、アプリケーションに厳格なコールドスタートレイテンシー要件がある場合に使用してください。SnapStart とプロビジョニングされた同時実行の両方を同じ関数バージョンで使用することはできません。

料金情報:aws.amazon.com

SnapStart の制約

ドキュメントから、制約事項になりそうな箇所を抜粋しました。
完璧ではないかもしれません・・。

1

SnapStart は Java 11 (java11) マネージドランタイムをサポートします。これ以外のマネージドランタイム (nodejs18.x や python3.9 など)、カスタムランタイム、およびコンテナイメージはサポートされていません。

2

SnapStart は、プロビジョニングされた同時実行、arm64 アーキテクチャ、Amazon Elastic File System (Amazon EFS)、AWS X-Ray、または 512 MB を超えるエフェメラルストレージをサポートしません。

3

一意性 スナップショットに包含される一意のコンテンツを初期化コードが生成する場合、そのコンテンツは、複数の実行環境で再利用されるときに一意にならない可能性があります。SnapStart の使用時に一意性を維持するには、初期化後に一意のコンテンツを生成する必要があります。これには、一意の ID、一意のシークレット、および疑似ランダム性を生成するために使用されるエントロピーが含まれます。一意性を回復する方法については、「Lambda SnapStart での一意性の取り扱い」を参照してください。

4

ネットワーク接続 Lambda がスナップショットから関数を再開するときは、関数が初期化フェーズ中に確立する接続の状態が保証されません。ネットワーク接続の状態を検証し、必要に応じて再確立してください。ほとんどの場合、AWS SDK が確立するネットワーク接続は、自動的に再開されます。その他の接続については、ベストプラクティスを確認してください。

5

一時的なデータ 関数には、初期化フェーズ中に一時的な認証情報やキャッシュされたタイムスタンプなどのエフェメラルデータをダウンロード、または初期化するものがあります。SnapStart を使用しない場合でも、エフェメラルデータは関数ハンドラーで更新してから使用してください。

まとめ

Lambda のコールドスタートと Snap Start 機能 (java向けの機能)を、ドキュメントと公式ブログから確認してみました。

余談

晴れ間が恋しいです。

山本 哲也 (記事一覧)

カスタマーサクセス部のエンジニア(一応)

好きなサービス:ECS、ALB

趣味:トレラン、サウナ、音楽鑑賞(J-Pops)、お笑い鑑賞(ラランド)