Lambda の同時実行 1,000 の裏側 - Firecracker ウォームプールとワーカーマネージャーの仕組み

Lambda が同時実行数 1,000 のデフォルト制限内でマイクロ VM を管理する仕組み、ウォームプールによる再利用戦略、ワーカーマネージャーの配置判断、コールドスタート率を下げる内部最適化を解説します。

Lambda の実行環境のライフサイクル

Lambda の関数呼び出しが到着すると、Lambda サービスは実行環境 (Execution Environment) を割り当てます。実行環境は Firecracker マイクロ VM 上で動作するサンドボックスで、関数のコード、ランタイム、設定された環境変数を含みます。実行環境のライフサイクルは 3 つのフェーズで構成されます。INIT フェーズでは、ランタイムの起動とハンドラ外のグローバルスコープのコードが実行されます。INVOKE フェーズでは、ハンドラ関数が呼び出されます。SHUTDOWN フェーズでは、ランタイムの終了処理が実行されます。重要なのは、INVOKE フェーズが完了した後、実行環境は即座に破棄されるのではなく、一定時間「フリーズ」状態で保持されるという点です。次のリクエストが到着すると、フリーズされた実行環境が「解凍」されて再利用されます。これがウォームスタートです。フリーズ状態の実行環境は、CPU 時間を消費せず、メモリだけを占有します。Lambda はこのフリーズされた実行環境のプールを管理しており、これが「ウォームプール」です。

ウォームプールの管理戦略

Lambda のウォームプール管理は、コールドスタート率の最小化とリソース効率の最大化のバランスを取る最適化問題です。ウォームプールに実行環境を長時間保持すれば、コールドスタートは減りますが、メモリを占有し続けるため、物理ホストのキャパシティが圧迫されます。逆に、すぐに破棄すればリソースは解放されますが、コールドスタートが増えます。AWS は 2019 年の論文で、Lambda のウォームプール管理に機械学習ベースの予測モデルを使用していることを示唆しました。関数の呼び出しパターン (頻度、時間帯、バースト特性) を分析し、次のリクエストが到着する確率が高い関数の実行環境を優先的に保持します。頻繁に呼び出される関数の実行環境は長時間保持され、まれにしか呼び出されない関数の実行環境は早期に破棄されます。ウォームプールの保持時間は公式には文書化されていませんが、実測では 5〜15 分程度と報告されています。ただし、この時間は関数の呼び出しパターンや物理ホストのキャパシティ状況によって動的に変化します。

ワーカーマネージャー - どの物理ホストに配置するか

Lambda のワーカーマネージャーは、新しい実行環境をどの物理ホスト (ワーカー) に配置するかを決定するコンポーネントです。この配置判断は、複数の制約を同時に満たす必要があります。第 1 に、物理ホストのキャパシティ (CPU、メモリ) に空きがあること。第 2 に、同一の顧客の実行環境が複数の物理ホストに分散されること (障害隔離)。第 3 に、VPC 接続が必要な関数の場合、対象の VPC のサブネットに ENI が確保できること。2019 年以前、VPC 接続の Lambda 関数は起動時に ENI を作成する必要があり、これがコールドスタートに 10〜30 秒の遅延を追加していました。2019 年の改善で、Lambda は VPC の ENI を事前にプロビジョニングし、Hyperplane (AWS の内部ネットワーク仮想化レイヤー) を使用して実行環境と ENI を動的にマッピングする方式に変更されました。この改善により、VPC 接続の Lambda 関数のコールドスタートは非 VPC の関数とほぼ同等になりました。

同時実行数の管理とバーストリミット

Lambda のアカウントあたりのデフォルト同時実行数 1,000 は、リージョン内のすべての関数で共有されます。関数 A が 800 の同時実行を使用していると、関数 B は 200 しか使用できません。バーストリミットは、同時実行数が急増する速度の制限です。us-east-1、us-west-2、eu-west-1 では初期バーストが 3,000、その後は 1 分あたり 500 ずつ増加します。他のリージョンでは初期バーストが 500〜1,000 です。つまり、同時実行数が 0 から 3,000 に瞬間的に増加することは可能ですが、3,000 から 10,000 に増加するには約 14 分かかります。このバーストリミットは、Lambda の内部インフラ (Firecracker マイクロ VM の起動、ENI の確保、物理ホストのキャパシティ割り当て) が急激な負荷増加に対応するための時間を確保する設計です。Provisioned Concurrency を使用すれば、バーストリミットに関係なく、指定した数の実行環境を事前にウォーム状態で維持できます。

実行環境の再利用で注意すべきこと

実行環境の再利用 (ウォームスタート) は、パフォーマンスの観点では有利ですが、開発者が注意すべき副作用があります。第 1 に、グローバル変数の状態が保持されることです。前回の呼び出しで設定したグローバル変数の値が、次の呼び出しでも残っています。これを利用してデータベース接続をキャッシュする (接続プーリング) のは推奨されるパターンですが、リクエスト固有のデータをグローバル変数に保存すると、次のリクエストで前のリクエストのデータが漏洩するバグが発生します。第 2 に、/tmp ディレクトリのファイルが保持されることです。前回の呼び出しで /tmp に書き込んだファイルが残っているため、ディスク容量 (最大 10GB) を使い切る可能性があります。第 3 に、バックグラウンドプロセスが残ることです。ハンドラ関数内で非同期処理を開始し、完了を待たずにレスポンスを返した場合、その非同期処理は次の呼び出し時にまだ実行中の可能性があります。Lambda の内部動作を深く理解するには、専門書籍 (Amazon)が参考になります。