📖

re:Invent 2024: AWS LambdaチームがPython/.NET向けSnapStartを解説

2024/01/01に公開

はじめに

海外の様々な講演を日本語記事に書き起こすことで、隠れた良質な情報をもっと身近なものに。そんなコンセプトで進める本企画で今回取り上げるプレゼンテーションはこちら!

📖 AWS re:Invent 2024 - Accelerate Python and .NET Lambda functions with SnapStart (SVS218-NEW)

この動画では、AWS LambdaのSenior Engineer SAのLeandro DamascenaとPrincipal Product ManagerのSamrat Karakが、PythonとNET向けのLambda SnapStartについて解説しています。Lambda関数の初期化時間を大幅に短縮できるSnapStartの仕組みや、LangChainを使用したLLMアプリケーションでは3.5秒のコールドスタートが500ミリ秒に、DuckDBを使用したデータ分析では8秒が1秒に改善される具体例が示されています。また、Runtime Hooksを使用したスナップショット前後の処理の実装方法や、DNSキャッシュの扱い、Provisioned Concurrencyとの使い分け、料金体系など、実装時の重要な考慮点について詳しく説明されています。
https://www.youtube.com/watch?v=D3IOL_mNrFM
※ 動画から自動生成した記事になります。誤字脱字や誤った内容が記載される可能性がありますので、正確な情報は動画本編をご覧ください。
※ 画像をクリックすると、動画中の該当シーンに遷移します。

本編

AWS Lambda SnapStartの概要と課題

Thumbnail 0

セッションへようこそ。これだけ多くの方々にお集まりいただき、大変うれしく思います。私はLeandro Damascenaと申します。AWSのSenior Engineer SAとして、AWS Lambda向けのPowerToolsの開発を担当しています。PowerToolsは、お客様がベストプラクティスを採用するのを支援するツールキットです。本日は同僚のSamrat Karak(LambdaのPrincipal Product Manager)と一緒に登壇させていただきます。彼については後ほど自己紹介があります。今日は約1ヶ月前に発表した素晴らしい新機能、PythonとNET向けのSnapStartについてお話しします。すでにJavaでサポートされていることはご存知かと思いますが、今回PythonとNETのサポートを追加しました。プレゼンテーションを始める前に、セッション終了後のアンケートへのご協力をお願いいたします。これは今後の改善に向けて非常に重要な情報となります。

Thumbnail 60

まずは、AWS Lambda関数を構築する際に誰もが直面する共通の課題について話しましょう:初期化時間の問題です。これは使用言語によって異なり、JavaとNET、Python、Node.js、Goなど、それぞれ特徴があります。特にJavaで開発されているお客様が直面する初期化時間の課題について考えてみましょう。Javaの場合、Java仮想マシンのロードが必要で、これは時間のかかる処理です。これはJavaの特性であり、Javaが登場して以来変わっていません。また、クラスのロードや静的コードの初期化も必要で、これにはお客様のクラスやJavaが必要とする様々な処理が含まれます。ただし、必要なライブラリのみを使用することで、この状況を改善することができます。不要なものを追加したり、本番環境で不要なライブラリを含めたりする必要はありません。これにより初期化時間を短縮できます。現在、多くのお客様がコールドスタートを避けるため、定期的にLambdaを呼び出してウォーム状態を保っています。また、先ほど触れたように、JavaではすでにSnapStartを利用可能で、これを使用して大幅な改善を実現したお客様の成功事例も多数あります。

Thumbnail 160

NETで開発されているお客様も、Javaと同様の課題に直面しています。NETではCLR(Common Language Runtime)のブートストラップ、クラスのロード、コードのロードが必要です。お客様はすでにこの体験の改善に取り組んでいます。NET 8以降では、AOT(Ahead of Time Compilation)の恩恵を受けることができ、実行時にビルドを行うJust-in-time compilationが不要になりました。これにより、NETでLambdaを構築する際の体験と初期化時間が改善されています。Javaと同様に、不要なライブラリや不要な名前空間のインポートを避けることもできます。

Thumbnail 220

そして今回、Pythonが加わりました。PythonはJavaやNETと異なり、インタープリタ言語であるため、AWS Lambdaでのコールドスタートはそれほど多くありません。しかし、お客様の事例から、依然としてコールドスタートの課題に直面していることがわかっています。他の言語と同様に、不要なインポートを減らしたり、PowerToolsのような軽量なフレームワークやSDKを選択したり、過剰なライブラリをLambdaコードに含めないようにすることができます。また、マイクロサービスを構築する際に、アプリケーションのすべてまたは大部分のエンドポイントを1つのLambdaで処理するmonolambdaまたはfat lambdaというアプローチを採用することもできます。

Thumbnail 290

しかし、これらの改善策だけでは初期化時間の改善が十分でない場合があります。そこで今回、新機能のSnapStartについて、その重要な側面をご紹介させていただきます。AWS Lambdaのオンデマンド実行モデル、Lambda SnapStart、ユースケースなどについて説明していきます。このプレゼンテーションを通じて、PythonとNET向けのSnapStartを使用して体験を改善する方法について、十分な知識を得ていただければ幸いです。

Lambda関数の従来の呼び出しモデルとコールドスタートの課題

Thumbnail 330

プレゼンテーションを始める前に、PythonやDotNETでAWS Lambdaを構築している方は何人いらっしゃいますか?素晴らしいですね。では、コードの起動時間が長いという問題に直面している方は?ご安心ください、皆様お一人ではありません。私も何度もそのような経験をしてきました。

まず、Lambda関数の従来の呼び出し方式であるオンデマンド呼び出しモードについてお話ししましょう。Lambda関数がイベントやリクエストを受け取ると、AWS Lambda実行環境と呼ばれる新しい環境をブートストラップして作成する必要があります。これには、FirecrackerのマイクロVM環境の起動、コードのダウンロード、展開などが含まれます。AWS Lambdaを使用する際には、実行環境の作成、再利用、破棄が行われます。新しい環境をゼロから作成し、すべてをブートストラップして読み込む必要がある場合、これがコールドスタートとして経験されることになります。

Thumbnail 390

Thumbnail 410

コールドスタートについて共通の理解を持つために、呼び出しモデルの各ステップを見ていきましょう。 Lambdaを初めて呼び出す際、コードと依存関係を含むファイルのダウンロードと展開が必要です。AWS Lambdaで大きなパッケージを使用している場合、大きなファイルをダウンロードする必要があるため、コールドスタート時により大きな遅延が発生する可能性があります。 これは、キロバイト単位のファイルとメガバイト単位のファイルをダウンロードする場合では大きく異なります。

Thumbnail 430

その後、Lambdaは実行環境を開始し、Lambda関数を実行する際の分離とセキュリティを提供するFirecrackerを使用してマイクロVMを作成します。 そして、マイクロVM上でランタイムマネージャーがブートストラップされます。その後、実行環境がコードを初期化します。これには、Lambdaハンドラー外のトップレベルのインポート、トップレベルのステートメント、Lambdaハンドラー外のHTTP接続など、すべてが含まれます。

Thumbnail 460

Thumbnail 490

Lambdaハンドラー、ビジネスロジック、コード自体の実行についてお疑問があるかもしれません。これは、新しい環境を作成するために必要なすべてのステップが完了した後の次のステップで実行されます。これが私たちがコールドスタートと呼ぶものです。Lambda関数を初めて実行する際、Lambdaがすべてをゼロから作成する必要があるため、コールドスタートを経験します。 その後の呼び出しでは、Lambdaは以前に作成したコンテナを賢く再利用するため、もはやコールドスタートではありません。

Lambdaは1回の実行につき1回の呼び出しであることを覚えておいてください。Lambdaはコンテナを再利用することはできますが、それは保証されていません。Lambda基盤のEC2フリートが更新されるのか、あるいはLambdaのロードバランシングメカニズムが新しいコンテナに振り向けるのかは分かりません。そのため、コンテナの再利用とコールドスタートの回避は保証されていません。でも、どうやってこれを解決するのか疑問に思われるかもしれません。そこで、より詳しい説明のために、同僚のSamratをお招きしたいと思います。

SnapStartの仕組みと利点

Thumbnail 570

Thumbnail 600

ありがとう、Leandro。コールドスタートがアプリケーションのパフォーマンスに与える影響、特に依存関係のロードによって生じる遅延について理解していただいたところで、この初期化時間を短縮し、アプリケーションのパフォーマンスを向上させる方法についてお話ししましょう。先月Lambdaチームが発表したPythonと.NET向けのAWS Lambda SnapStartについてご説明します。 Lambdaでスケーラブルかつレスポンシブなアプリケーションを構築し、起動時の遅延を削減したいというお客様向けに、今ではJavaに加えてPythonと.NETもサポートしています。

まずは利点から見ていき、SnapStartがどこでサポートされているかを確認しましょう。SnapStartのサポート状況については、Pythonランタイムバージョン3.12、3.13、.NET 8、そしてJava 11、17、20で利用可能で、今後さらに新しいバージョンも対応予定です。利点とユースケースを考える際に念頭に置いておくべきことは、SnapStartが特に役立つ2つの主要なシナリオがあるということです。1つ目は、レイテンシーに敏感なWeb APIやWebアプリケーション、特にユーザー向けでレイテンシーが重要な場合です。2つ目は、より長い初期化時間のために処理に時間がかかる可能性のある外れ値の関数を除去したいバッチデータ処理のシナリオです。

Javaの場合、コールドスタートの中央値レイテンシーが約4秒であるのに対し、Python関数は通常より高速に実行されます。しかし、Flask、Django、LangChainなどのフレームワークを使用する場合、コールドスタートは2秒から5秒程度かかることが観察されています。SnapStartを使用すると、このレイテンシーは1秒未満に削減されます。さらに、DuckDBやNumPyなどのライブラリを使用する処理ワークロードについては、通常5秒以上かかるこれらの処理ワークフローも、SnapStartを使用することで全体のレイテンシーが1秒未満になることを確認しています。

.NETのAhead-of-Time(事前)コンパイルに関しては、最新バージョンでも特にトリミングの概念において課題があります。Ahead-of-Timeコンパイルでは、アプリケーションがJust-in-Timeコンパイル中に動的なルックアップを実行し、アプリケーションの特定の部分がトリミングされている場合、アプリケーションの動作は決定論的ではなくなります。このような場合、コードの変更なしでレイテンシーを削減するために、SnapStartと組み合わせた.NETの使用をお勧めします。

Thumbnail 800

Thumbnail 880

それでは、SnapStartのワークフローについて詳しく見ていきましょう。 関数でSnapStartを有効にすると、ステータスは最初「pending(保留中)」となり、関数のバージョンを取得するために関数をパブリッシュする必要があります。これが全プロセスの始まりです。ここで見ていただいているのは、関数の初期化フェーズ全体を開始するプロセスです。SnapStartの一環として初期化が行われると、メモリとディスクの状態を暗号化したFirecracker microVMスナップショットが取得され、小さなチャンクに分けて低レイテンシーのキャッシュに保存されます。その後、Lambda関数が呼び出されると、この暗号化されたスナップショットが再度読み込まれます。 最も頻繁にアクセスされるチャンクが優先的に読み込まれ、レイテンシーが改善されます。関数のステータスは、SnapStartを有効にして関数をパブリッシュすると「pending」から「active(アクティブ)」に変わり、アクティブになると、それに対応するLambdaでの呼び出しが行われるようになります。

Thumbnail 920

アクティブになると、AWS Lambdaは暗号化されたスナップショットから実行環境の読み込みを開始し、コールドスタートのレイテンシーを削減します。 Lambda関数に対して複数のリクエストや呼び出しが発生すると、Lambdaはスケールアップして複数の実行環境インスタンスを起動し、それらすべてが保存されたスナップショットを使用して関数を再開し、設定された関数ハンドラーを実行します。

Thumbnail 970

初期化の過程で何が起きているのか、そしてスナップショットの作成前後でどのようなロジックを実行すべきかについてのベストプラクティスについて、詳しく見ていきましょう。Firecrackerを使用した呼び出しモデルについてお話しします。先ほど、SnapStartの一部としてFirecracker microVMを使用することについて触れましたが、これはスナップショットの作成と再読み込みの両方において、セキュリティと低レイテンシーを提供します。スナップショット作成前にどの初期化部分を設定すべきか、そしてスナップショット作成後に実行するコードにとって理想的なユースケースは何か、これらのプリフックとポストフックについて説明していきます。

Thumbnail 1030

初期化フェーズについて説明しましたが、スナップショットが利用可能になった後の各呼び出しは、アプリケーション用に作成されたFirecrackerベースのスナップショットを使用して再開されます。プリスナップショットフックとポストスナップショットフックの詳細に入る前に、いくつかの具体的なユースケースを見てみましょう。その後、これらのフックの詳細に進みたいと思います。Leandroさん、お願いします。

Thumbnail 1080

Thumbnail 1100

ありがとうございます。通常のLambdaのオンデマンド呼び出しモデルとSnapStartを使用した場合の違いがお分かりいただけると思います。SnapStartではスナップショットを作成し、そのスナップショットから再開します。いくつかのユースケースについて見ていきましょう。例えば、現在のホットトピックの1つがLLMアプリケーションの構築です。誰もが生成AIの機能を向上させ、LLMアプリケーションの構築にその利点を活用したいと考えています。一般的なユースケースとして、LangChainライブラリの使用があります。LangChainは、多くのモデルや、お客様がAWS Lambdaを使用して構築しているダウンストリームモデルやサービスのためのプロキシとして機能する抽象化を提供します。

Thumbnail 1160

この例を見てみましょう。LangChain ライブラリ、FastAPI、そして FastAPI の上にラッパーを作成する Mangum をインポートしています。この例では、顧客は API のように機能する生成 AI ソリューションを作成できます。誰かが質問をすると、このアプリケーションはチャットと OpenAI インスタンスを作成し、LLM に問い合わせて応答を生成します。このコードをオンデマンドモデルで実行し、その後 SnapStart で実行してみましょう。グラフィックで確認できるように、 1ギガバイトのメモリでこの関数を実行すると、最初の実行では約3.5秒のコールドスタートが発生します。しかし、SnapStart を使用すると、最初の呼び出しの結果は約500ミリ秒になります。これは、環境をゼロから作成する必要がなく、コードをダウンロードして解凍し、すべてをロードする必要がないためです。これは AWS Lambda が再開できるスナップショットであり、エクスペリエンスを向上させます。

SnapStartの実装とユースケース

Thumbnail 1200

しかし、顧客は生成 AI だけを構築しているわけではありません。 顧客は、大規模または中規模のデータセットに対してクエリを実行したいデータ分析も構築しています。

このようなデータ分析タスクでは、データの要約のためにクエリを実行したり、大規模なデータセットに対して迅速な応答が必要な場合など、考慮すべき多くのユースケースがあります。大規模なデータセットをチェックできる API があることを想像してみてください。このコードでは、有名な非リレーショナルデータベースである DuckDB をインポートしています。また、Pandas もインポートしており、Python で開発を行い Pandas を使用している人なら、NumPy などのコンポーネントを含む大規模なパッケージであることを知っているでしょう。そして、FastAPI で API を作成します。この例を見てみましょう。ただし、ここで考慮すべき点があります - データベースに接続しているということです。データベース接続は通常それほど時間がかからないことは分かっていますが、それでもデータベースへの接続は必要です。

Thumbnail 1260

これは、最終的な実装におけるベストプラクティスを理解するのに役立ちます。1ギガバイトのメモリでこの関数を実行すると、コードスタートで8秒の初期化時間が発生することがわかります。これは、データセットやモデルなど、すべてをロードしているためです - 多くのユースケースがあります。SnapStart を使用すると、何時間にもわたって Lambda を呼び出してテストを行った結果、リストア時間は約1秒程度であることがわかります。これが SnapStart のコードスタート時間です。Python のような解釈型言語でも、SnapStart を使用してその恩恵を受けることができます。多くの顧客は重大なコードスタートのタイミングの問題に直面していませんが、最適化のために SnapStart を使用することができます。

Thumbnail 1330

Thumbnail 1370

ユースケースを理解し、SnapStart についてより詳しく学んだところで、それを有効にして関数で使用する方法について説明しましょう。 新規または既存の関数で SnapStart を有効にすることができます。この点は非常に重要です - SnapStart は Lambda 関数の公開バージョンまたはエイリアスでのみ機能します。SnapStart を有効にしたものの、SnapStart が有効になっている関数のバージョンを公開したり参照したりしなかったケースがありました。 Lambda コンソール、AWS SDK、CLI、SAM を使用して SnapStart を有効にすることができ、これらの一部について今から説明します。

Thumbnail 1390

関数のバージョンやエイリアスを指定しない場合、SnapStartは機能しません。コンソールでの実装を見てみましょう。Lambda関数を作成した後、関数の設定の一部としてSnapStartを有効にし、公開バージョンを選択するオプションがあります。先ほど説明したように、関数を公開する必要があり、これによってスナップショットプロセスが開始されます。利用可能になると、ステータスがアクティブになり、その時点以降のすべての呼び出しはスナップショットを使用してロードされます。

Thumbnail 1440

Thumbnail 1500

こちらがSnapStart設定で公開バージョンを使用するCDKコンストラクトの例です。関数の設定を更新する際、get function configで最適化ステータスを確認でき、この関数でSnapStartが有効になっているかどうかを示します。ステータスがアクティブの場合、SnapStartが有効化され、スナップショットが呼び出し可能な状態であることがわかります。同様に、CDKと共に、後ほど説明するCloudFormation SAMにもSnapStartエンティティがあります。 ご覧のSnapStartプロパティは、他の例と同様に、公開バージョンに適用されます。

Thumbnail 1510

Thumbnail 1520

Thumbnail 1550

ここで、SnapStartを有効にした後に利用可能なオプションの詳細について説明します。PythonとDotNETのフックについて言及しました。 スナップショット作成の前後でコードを実装する方法について説明しましょう。スナップショット作成前にコードを実装する場合、通常、外部ファイルのダウンロードや計算負荷の高いタスクなどを処理します。 これらのフックは、クリーンアップや初期化の実行、動的な設定の管理、外部との統合の管理に役立ちます。スナップショット作成の前後でこのコードを実行することの理想的なタイミングを決定できます。また、パフォーマンスチューニングは、スナップショット作成の前後にどのようなロジックを追加するかによって異なります。

Pythonでランタイムフックを使用する際は、登録されたすべてのフックが適切にインポートされ、関数のコードに含まれていることを確認することが重要です。別のファイルやモジュールでランタイムフックを登録する場合、そのモジュールが大きなパッケージの一部として、または関数のハンドラーファイルに存在することを確認する必要があります。ファイルやモジュールが関数ハンドラーにインポートされていない場合、Lambdaはランタイムフックを無視します。

Thumbnail 1630

初期化の異なるステップに戻ると、実行環境の作成、コードのダウンロード、ランタイムの開始があり、どの部分をプリスナップショットフックに移動できるかを決定すると、そのロジックが初期化の一部として実行されます。スナップショットが作成された後の後続の呼び出しでは、データベースとの接続、一意の識別子の処理、シークレットの取得など、ポストスナップショットのロジックと共にスナップショットが再開されます。

Thumbnail 1690

Thumbnail 1730

続いて、Runtime Hooksのために提供されているPythonプロジェクト向けのSnapshot Restoreについてご説明します。主要なコンポーネントは2つあります:Register Before SnapshotとRegister After Restoreです。これらのデコレーターと関数をインポートして、スナップショット作成前やリストア後に実行したいロジックを追加することができます。 こちらの例では、Before SnapshotとAfter Restoreをコードの一部としてインポートし、Lambdaのチェックポイント前とリストア後の間にロジックを配置している様子が分かります。

SnapStartのRuntime Hooksとベストプラクティス

ここで2つの重要なポイントについてお話しします。1つ目はデコレーターのオプションです。デコレーターと関数のどちらを使うかを選択できますが、後ほど両方のコードをお見せします。ベストプラクティスとしては、デコレーターは便利ですが、特に複数のHookがあり、その実行順序を制御する必要がある場合は、Hook登録に関数を使用する方が明示的な制御が可能です。2つ目はモジュールのインポートについてです。先ほど申し上げたように、Lambda Handler内でHookをインポートしても実行されないため、メインHandlerでインポートされるファイル内でHookを登録する必要があります。Hookを定義して登録すると、関数で実行できるようになります。

Thumbnail 1820

Thumbnail 1840

Runtime Hooksの説明を続けます。こちらはPythonで関数メソッドを使用する例です。次に、 .NETのRuntime Hooksを見ていきましょう。Amazon.Lambda.Coreライブラリを使用して、スナップショット作成の前後にコードを登録します。これらの.NET Runtime HooksはLambda Coreパッケージの一部として提供されており、RegisterBeforeSnapshotとRegisterAfterRestoreという2つのメソッドを使用して、前後のコードを実行できます。

Thumbnail 1870

こちらはRegisterBeforeSnapshotとRegisterAfterRestoreという2つのチェックポイントのコード例です。重要なガイドラインとしては、初期化コードでHookを登録する必要があります。Executive Assemblyアプローチでは、RunAsyncでLambdaブートストラップを開始する前にHookを登録します。Class Libraryアプローチでは、HandlerクラスのコンストラクタでHookを登録します。.NET Coreアプリケーションの場合は、WebApplications.Runメソッドを呼び出す前にHookを登録します。

Thumbnail 1940

Thumbnail 1960

同じコードをOn-demandモデルやSnapStartモデルで呼び出すことができます。ほとんどの場合、コードを変更する必要はありません。ただし、SnapStartにはいくつかのエッジケースと制限があります。 一意性を扱う必要がある場合のシナリオを考えてみましょう。暗号化のために一意のシードソルトを作成し、そのユニークIDでリクエストを暗号化する必要があるとします。シード、ソルト、またはIDの生成をLambda Handler外に配置すると、スナップショット作成時に一度だけ実行されます。これは一つのコードとなり、Lambdaが凍結されたコンテナから再開されるため、この一意の値は各呼び出しで変更されません。同じコンテナを使用する数百万、数十億の実行で同じコード、値、またはハッシュが生成される可能性があるため、一意性を扱う際は注意が必要です。

Thumbnail 2030

もう1つの考慮事項として、バージョンとエイリアスの問題があります。SnapStartは最新バージョンでは動作しません。スナップショットを有効にしてもバージョンを公開しない場合、オンデマンドとして実行されることになります。これはLambdaのログで確認できますし、Lambdaの環境変数を調べることでも、実行モデルがオンデマンドになっていることが分かります。必ずバージョンを作成し、必要に応じてエイリアスを付けることができますが、バージョンを指定して呼び出す必要があります。

Thumbnail 2070

Thumbnail 2110

DNSキャッシュに関して、Javaでは当初Java Virtual MachineがDNSをキャッシュするという問題がありましたが、PythonとNETではこの問題は発生しません。PythonとNETのランタイムではDNSをキャッシュせず、毎回DNSを更新します。スナップショットフェーズでアドレスをクエリし、その後Lambdaファンクション内でそのアドレスに対して呼び出しを行う場合でも問題ありません。PythonとNETを使用したLambdaファンクションでDNSを扱う場合、 キャッシュを行わない新しいDNSとLambdaリゾルバーを使用します。

Thumbnail 2130

DNSの取り扱いについて、Pythonの場合、意図的にDNSをキャッシュしない最新のライブラリを使用することが重要です。ランタイム自体がDNSをキャッシュしない場合でも、 ライブラリに起因する問題が発生する可能性があります。NETでは自動的にソケット接続を確立しようとしますが、使用するライブラリがこの機能をサポートしているかどうかを確認する必要があります。これは、ランタイムにDNSキャッシュの問題がない場合でも、使用するライブラリに注意を払う必要があるという点で重要です。

Thumbnail 2150

Thumbnail 2200

Thumbnail 2240

もう1つの重要なトピックとして、すでに多くのお客様がProvisioned Concurrencyを使用しているという点があります。Provisioned Concurrencyについてご存じない方のために説明すると、これは設定可能な数のLambdaコンテナを事前にウォームアップする機能です。例えば100個のコンテナを指定すると、それらは即座に応答できる状態で待機します。SnapStartは、予測不可能なスパイクのあるワークロードで大きな価値を発揮します。例えば、通常は線形的な呼び出しパターンであっても、1日の特定の時間帯に1000回の呼び出しが発生するような場合です。 このようなシナリオでは、SnapStartがコールドスタート時間を改善できます。Provisioned Concurrencyは、200〜300ミリ秒以内に応答する必要があるAPIなど、低レイテンシーのワークロードに最適です。私の意見では、予測不可能なトラフィックスパイク時の99%のリクエストに対するレイテンシーを削減するためにSnapStartを使用し、 Lambdaファンクションからの線形的なレスポンスを維持するにはProvisioned Concurrencyが適しています。

Thumbnail 2270

SnapStartやサーバーレス全般を使用する際、モニタリングと観察は非常に重要な側面です。ファンクションとその動作を観察する必要があります。SnapStartでは、CloudWatchを使用してスナップショットの作成にかかる時間を示すInit Reportを監視できます。 また、呼び出しログを監視し、SnapStartの復元と再開にかかる時間を示すRestore Reportメトリクスを追跡することもできます。オンデマンドモデルを使用している場合でも、SnapStartモデルを使用している場合でも、ファンクションを観察することで貴重な洞察が得られます。

Thumbnail 2290

AWS X-Rayを使用してロードランタイムを監視することができます。すでにオンデマンド呼び出しモデルでX-Rayを使用してInit時間を確認している場合、SnapStartではその代わりにRestore時間が表示されます。これらは異なるメトリクスですが、同様に観察することができます。JSONでレポートを作成したりデータをクエリしたりする場合は、Lambda Telemetry APIも利用できます。

Thumbnail 2320

Thumbnail 2380

SnapStartを使用する際のベストプラクティスについて、例を挙げて説明しましょう。Lambdaハンドラの外でHTTP接続やデータベース接続を作成する場合、これらの接続はSnapStartがスナップショットを作成する時点で保存されます。しかし、数時間後にLambda関数を呼び出すと、Lambdaが別のサーバーでコンテナを復元する可能性があるため、あるいはダウンストリームの接続に問題が発生している可能性があるため、その接続が利用できなくなっているかもしれません。実際のユースケースについて詳しく見ていきますが、接続の取り扱いには注意が必要です。接続が利用可能かどうかを確認するために、SnapStartのAfter-restoreフックを使用するか、 Lambdaハンドラ内で確認することができます。もう一つの考慮点は、スナップショット作成時にSecretや一時的な情報を取得する場合などの一時的なデータに関するものです。ファイルやSecret、その他の一時的なデータについて、復元後にそのデータが有効であると想定せずに検証する必要があります。

Thumbnail 2420

Thumbnail 2430

Thumbnail 2440

コンテナのスナップショットは数日間保持されるため、一時的なデータには注意が必要です。例えば、 5日前にスナップショットを作成し、その間にローテーションされた可能性のある同じSecretを使用している場合、問題が発生する可能性があります。実例を見てみましょう。 スナップショットを作成するInit段階でAmazon Relational Database Service(Amazon RDS)への接続を確立するLambda関数を考えてみましょう。Lambdaがこのコンテナを再開する際に、接続を確認せずに有効であると想定すると、 ソケットが利用できなくなっている可能性や、アドレスが知らないうちに変更されている可能性、あるいは様々な理由でSecretがローテーションされている可能性があるため、呼び出しは失敗します。

Thumbnail 2460

Thumbnail 2470

Thumbnail 2480

Thumbnail 2490

一時的なデータの概念についてさらに詳しく見てみましょう。 AWS Secrets Managerからシークレットを取得し、スナップショット内のメモリに保存する別のLambda関数を考えてみましょう。再開フェーズ中に、 データが依然として有効であるか、あるいはローテーションされたかどうかを確認しないと、問題が発生する可能性があります。データベースに接続しようとする際、 まず接続状態を確認する必要があります。pingを実行してソケット接続を確立できたとしても、 認証を試みた際に、認証情報がローテーションされているために失敗する可能性があります。例えば、会社の方針でAWS Secrets Managerのシークレットを2日ごとにローテーションする場合があります。SnapStartを使用する際は、以前に作成されたコンテナを使用することになるため、これは重要な考慮事項です。

SnapStartの料金体系と総括

Thumbnail 2550

Thumbnail 2560

これは、これらの考慮事項を認識しておく必要のあるお客様と共有するのが楽しみな価値ある情報です。プレゼンテーションの終わりに近づいていますが、まだ重要なトピックが一つ残っています:料金についてです。Samrat、続けてください。ありがとうございます。料金について説明しましょう。 SnapStartの料金は2つの次元に基づいています。 料金は、SnapStartが実行するキャッシングの量とRestoreの回数に基づいて計算されます。例えば、1か月間アクティブな状態を維持する1GBの関数を考えた場合、キャッシング料金についてはGB-秒-月にウェブサイトで提供されているレートを掛けます。Restoreに関しては、通常、全呼び出しの小さな割合、一般的に全呼び出しの約1%程度を占めます。

Thumbnail 2690

Thumbnail 2700

PythonとNET向けのSnapshotは、14日間呼び出されなくても、Function Versionに対してアクティブな状態を維持します。これはJava SnapStartとは異なります。Java SnapStartでは、14日間呼び出しがない場合、SnapStartは非アクティブになり、呼び出しによって再アクティブ化する必要があります。キャッシュ料金については、最低3時間の計算期間があり、その後はミリ秒単位で課金されます。料金は、Functionに割り当てられたメモリ量によって異なります。1GBのFunctionで実行時間が300ミリ秒、1億回の呼び出しの例を見てみましょう。

実行時間300ミリ秒、1億回の呼び出し、約25万回のRestoreの場合、現在の料金でトータル約558ドルとなります。これは、現在の料金に基づく計算料金とリクエスト料金の内訳に、Lambdaによってキャッシュされたスナップショットのキャッシュ料金とRestore料金を加えたものです。重要なポイントが2つあります。1つ目は、キャッシュ料金は呼び出し量に関係なく一定であること。2つ目は、スナップショットが利用可能になると、Warm実行環境のRestoreの数が少なくなるため、トラフィック量が増えるにつれてコールドスタートと呼び出しの比率が下がることです。

Thumbnail 2820

覚えておくべき重要な点として、list version by functions APIがあります。この既存のAPIを使用すると、どのFunction VersionでSnapStartが有効になっているかを追跡できます。また、料金はFunction Versionごとに発生するため、ベストプラクティスとして、使用していない古いFunction Versionを削除することをお勧めします。Lambdaは古いVersionでもスナップショットを利用可能な状態に保ち続けるためです。したがって、不要なFunction Versionを探し、SnapStartが有効になっている場合は、無効化するかVersionを削除することがベストプラクティスとなります。

要点をまとめますと、本日は多くの内容をカバーしました。SnapStartは現在、Python、.NET、そして以前からサポートしているJavaで利用可能です。SnapStartは特定のFunction VersionまたはAliasでのみ機能することを覚えておいてください。呼び出し時にVersionを指定しない場合、Functionで SnapStartが有効になっていても、その恩恵を受けることはできません。使用していないVersionを削除することでコストを削減できます。以上が本日の内容です。ご参加いただき、ありがとうございました。改善のため、セッションフィードバックの記入をお忘れなく。質問がある方は、この場にいますのでお気軽にお声がけください。


※ こちらの記事は Amazon Bedrock を利用することで全て自動で作成しています。
※ 生成AI記事によるインターネット汚染の懸念を踏まえ、本記事ではセッション動画を情報量をほぼ変化させずに文字と画像に変換することで、できるだけオリジナルコンテンツそのものの価値を維持しつつ、多言語でのAccessibilityやGooglabilityを高められればと考えています。

Discussion