Lambda コールドスタート対策 — プロビジョンド同時実行のコスト判断
Lambda のコールドスタートは、サーバーレスを選ぶときに最もよく出てくる心配ごとです。結論を先に書くと、ほとんどのワークロードは無料の対策(パッケージ削減、軽量ランタイム、SnapStart)で十分で、プロビジョンド同時実行は p99 の遅延要件が明確なユーザー向けの処理にだけ、計算した上で掛けます。 プロビジョンド同時実行は高い保険として知られがちですが、実行単価がオンデマンドより低く設計されているため、使用率が高いと総額がむしろ安くなる逆転の区間があります。Lambda 自体の選択基準は Lambda vs Fargate で扱いました。料金は us-east-1 基準です。
一目でわかる対策比較 #
| 対策 | 追加コスト | 効果 | 対象 |
|---|---|---|---|
| パッケージ・依存関係の削減 | なし | 初期化時間の短縮 | 全ランタイム |
| メモリ増設 | 実行単価の上昇 | CPU が比例して増え初期化短縮 | CPU ボトルネックの初期化 |
| SnapStart | Java 無料、Python・.NET はキャッシュ・復元課金 | 秒単位 → 数百 ms 以下 | Java、Python、.NET |
| プロビジョンド同時実行 | GB 秒あたり $0.0000041667 常時 | コールドスタートを実質除去 | p99 要件のある処理 |
| 定期的なウォームアップ呼び出し | 呼び出し料金少額 | 不完全(同時リクエストに無力) | 非推奨 |
コールドスタートの構造 — どこで時間がかかるのか #
Lambda はリクエストが来たときに空いている実行環境がなければ、新しい環境を作ります。このときコードのダウンロード、ランタイムの起動、ハンドラー外の初期化コードの実行が順に走り、この初期化フェーズがコールドスタート遅延の本体です。短ければ 100ms 以内、重いフレームワークを載せた Java や .NET なら数秒まで伸びます。
重要なのは発生頻度です。コールドスタートは最初のリクエスト、トラフィックが増えて同時実行が拡張されるとき、デプロイ直後に発生します。常時トラフィックのある関数なら全呼び出しの 1% 未満であることが多いため、平均遅延ではなく p99 が問題なのかを先に確認する必要があります。内部のバッチ処理や非同期イベントの消費なら、数秒のコールドスタートは問題ですらありません。
お金を使う前の基本は 3 つです。デプロイパッケージと依存関係を減らしてダウンロードと初期化を短くし、ハンドラー外の初期化コード(SDK クライアントの生成、設定の読み込み)を必要最小限に整理し、初期化が CPU ボトルネックならメモリを上げて比例配分される CPU を増やします。SnapStart は初期化済みのスナップショットを事前に取っておいて復元する方式で、Java は追加料金なしで有効化できるため、Java 関数なら最初に試す対策です。Python と .NET も対応していますが、スナップショットのキャッシュ保管と復元に課金されるため、呼び出しの多い関数では料金を確認してから有効化します。
プロビジョンド同時実行の料金構造 — 二重単価 #
プロビジョンド同時実行(Provisioned Concurrency)は、指定した数だけ実行環境を初期化完了の状態で常時維持する機能です。料金は 2 つの軸です。
- 維持料金: プロビジョンした容量に対して GB 秒あたり $0.0000041667 が、実行の有無にかかわらずかかります。メモリ 1GB の関数の同時実行 1 を 1 か月維持すると約 $11 です。
- 実行料金: プロビジョンされた環境で実行された時間は GB 秒あたり $0.0000097222 で、オンデマンド単価($0.0000166667)の約 58% です。
実行単価が低いことが核心です。プロビジョンした容量が遊んでいれば維持料金は純損ですが、使用率が上がると実行料金の割引が維持料金を相殺します。維持と実行の合計がオンデマンド料金と同じになるのは使用率約 60% です。常時トラフィックがプロビジョン容量の 60% を超えて回っているなら、コールドスタートの除去はただではなく削減付きでついてきます。逆に使用率 10% なら、コールドスタート除去のために維持料金の大部分をそのまま払うことになります。
計算例を 1 つ書くと、1GB の関数に同時実行 10 を 1 か月維持すると維持料金は約 $110 です。この容量が平均 70% で回れば実行料金はオンデマンド比で約 $89 の削減になり純コストは約 $21 に縮み、平均 20% なら削減は約 $25 だけで、約 $85 をコールドスタート除去の対価として払う構造です。
運用上の注意が 2 つあります。プロビジョン容量を超えたトラフィックはオンデマンドにあふれ、その部分ではコールドスタートが再び発生します。そして Application Auto Scaling で時間帯スケジュールを掛けられるため、業務時間だけ維持すれば維持料金を 3 分の 1 に減らせます。夜間トラフィックのない社内 API ならスケジューリングが基本です。
なお EventBridge で数分ごとに関数を呼んで温めておくウォームアップ方式は、環境を 1 つ温めるだけで、同時リクエストが来ればすぐコールドスタートになるため、プロビジョンド同時実行の代わりにはなりません。
判断の手順 #
- p99 が問題なのかを先に確認します: CloudWatch で初期化時間と発生比率を見ます。非同期・バッチ処理なら、コールドスタートは対策せず受け入れるのが正解です。
- 無料の対策を先に使い切ります: パッケージ削減、初期化の整理、Java なら SnapStart まで。ここで p99 が要件内に入れば終わりです。
- プロビジョンド同時実行は容量と使用率を計算してから掛けます: 必要な同時実行数はピークの同時実行数から取り、想定使用率 60% を基準にオンデマンドと総額を比較します。
- スケジュールで維持時間を減らします: トラフィックパターンが明確なら、Auto Scaling のスケジュールで夜間・週末の維持料金を消します。
- 常時高トラフィックならアーキテクチャの再検討と並行します: 使用率が常に高ければプロビジョンド同時実行は削減になりますが、その規模なら Fargate の常時コンテナとの比較も再び成立します。
まとめ #
- コールドスタートの本体は初期化フェーズです。発生比率は通常、全呼び出しの 1% 未満なので、平均ではなく p99 の要件があるかを先に確認します。
- 無料の対策(パッケージ削減、初期化の整理、Java の SnapStart)が第一です。Python・.NET の SnapStart はキャッシュ・復元の課金を確認してから有効化します。
- プロビジョンド同時実行は維持が GB 秒あたり $0.0000041667 で、実行単価はオンデマンドの約 58% という二重構造です。損益分岐は使用率約 60% です。
- 使用率が低ければ維持料金がコールドスタート除去の対価になり、スケジューリングでその対価を減らします。ウォームアップ呼び出しは同時リクエストに無力で、代わりになりません。
- API の前段の構成(料金構造の違う API Gateway と ALB)は次の記事で続きます。