Lambdaは「動かす」より「本番で安定運用する」ほうが難しいサービスです。コールドスタート、同時実行の上限、べき等性、コスト設計——PoCでは見えなかった論点を、AWS一次情報を確認しながら実務目線で整理します。

Lambdaは書き始めるのは簡単です。難しいのは、SLAのついた本番で安定して回し続けることです。PoCでは表面化しなかったコールドスタート、同時実行の上限、リトライに伴う二重処理、そして請求書を見て気づくコスト——このあたりは、最初に設計思想を押さえておかないと運用フェーズでまとめて跳ね返ってきます。この記事では、SIerや情シスの実務者が「選定・設計・落とし穴」の判断に使える形で、AWS一次情報を確認しながら整理します。数値や上限はバージョン・リージョンで変わるため、必ず公式で最新を確認してください。

01コールドスタートの正体を分解する

コールドスタートは「Lambdaが遅い」の代名詞のように語られますが、実体は明確です。リクエストを処理できる実行環境が手元にないとき、LambdaはまずInitフェーズ(コードのダウンロード、ランタイム起動、ハンドラ外の初期化コード実行)を挟んでからInvokeフェーズに入ります。このInit分の遅延がコールドスタートです。逆に、直前のリクエストで使った環境が空いていればそれを再利用するので、Initは走りません(ウォームスタート)。

AWSの公式ブログ「Understanding and Remediating Cold Starts」でも整理されているとおり、コールドスタートで支配的なのはハンドラ外の初期化です。SDKクライアントの生成、DBコネクション確立、大きな依存ライブラリのロード、設定の取得——ここが重いほどInitは伸びます。まず手を打つべきは緩和策の前に「Initを軽くする」ことです。

現場のコツ:コールドスタートを「毎回起きる恐怖」として過大評価しないことです。安定した高負荷では環境が再利用され続けるため発生率は下がります。問題になりやすいのは、トラフィックが疎で環境が回収された直後や、急なスパイクで一斉に新環境が立ち上がる局面です。まずCloudWatchのInitDurationとp99レイテンシで「どこで・どれだけ」効いているかを測ってから対策を選びます。

実行環境のライフサイクルとコールドスタート コールドスタート Init フェーズ コード読込・初期化 Invoke(処理) ← Init分だけ遅い ウォームスタート Invoke(処理) 環境を再利用 → Initなし 緩和策:Initを軽くする → SnapStart(Java/Python/.NET)→ Provisioned Concurrency(二桁ms要件) 順番が大事。まず初期化を削り、それでも足りなければ機能で埋める
図:コールドスタートはInitフェーズの遅延。環境を再利用できればInitは走らない。緩和は「初期化を軽くする」が起点。

02SnapStartとProvisioned Concurrencyの使い分け

Initを削っても要件を満たせないときに、2つの緩和機能があります。両者は排他で、同じ関数バージョンに同時には使えません

AWS公式ドキュメントは、初期化が数秒かかる関数にはSnapStart、数百ミリ秒で初期化が終わる関数で二桁ミリ秒の起動を狙うならPCを推奨しています。判断軸は「ランタイム」「求める起動レイテンシ」「コスト」の3つです。

現場のコツ:SnapStartのコストを誤解しないでください。Javaランタイムは追加料金なしですが、Python/.NETではスナップショットのキャッシュ料金(最低3時間、メモリ量に比例、関数がActiveの間は継続課金)復元料金がかかります。使わない関数バージョンを溜め込むとキャッシュ料金が地味に積み上がるため、ListVersionsByFunctionで棚卸しし不要バージョンを削除する運用を組み込んでおきます。

もう一つの落とし穴がスナップショットの一意性です。SnapStartは1つのスナップショットを複数環境の初期状態として使い回すため、初期化コードで生成した「ユニークなはずの値」(ランダムシード、一時クレデンシャル、UUID等)が全環境で同じになります。乱数・秘密・エントロピーは初期化後(ハンドラ内)に生成し直す設計が必要です。ネットワーク接続も復元時に切れている前提で、ハンドラ側で状態確認・再確立します(AWS SDKの接続は多くの場合自動で復帰します)。

判断軸SnapStartProvisioned Concurrency
対応ランタイムJava 11+ / Python 3.12+ / .NET 8+全ランタイム
狙える起動サブ秒(数秒→サブ秒)二桁ミリ秒(実質コールドスタートなし)
追加料金Javaは無料。Python/.NETはキャッシュ+復元課金常時課金(プロビジョン量×時間)
制約公開バージョンのみ・EFS/512MB超一時ストレージ非対応・一意性/接続の注意アイドルでも課金・急スパイクはオンデマンドに溢れる
向く場面初期化が重くスケールするAPI/データ処理厳格な低レイテンシSLA・予測可能な定常負荷

実務では「まずSnapStart(対応ランタイムなら)で様子を見て、二桁ミリ秒が要件のスパイク前提エンドポイントだけPCを、Application Auto Scalingでスケジュール/使用率連動させる」という組み合わせがコスト効率の良い落とし所になります。

03メモリはCPUのつまみ — 「小さいほど安い」は誤り

Lambdaで最も誤解されるのがメモリ設定です。Lambdaはメモリに比例してCPUを割り当てます。設定範囲は128MB〜10,240MB(1MB刻み)で、1,769MBでちょうど1 vCPU相当になります。つまりメモリ設定は「使えるCPUパワーのつまみ」でもあります。

ここから重要な帰結が出ます。CPUバウンドな処理では、メモリを増やすと処理時間が短くなり、メモリ単価×実行時間の総額はむしろ下がることがあります。128MBが常に最安ではありません。AWS自身も「128MBは、イベントを変換して他サービスへルーティングするような単純な関数向け」と位置づけています。

現場のコツ:メモリの最適値は勘で決めず、AWS Lambda Power Tuning(Step Functionsで複数メモリ設定を並列実行しコスト×速度を可視化)で測ります。CI/CDに組み込んで新関数デプロイ時に自動計測すると、勘に頼らない運用になります。x86_64ならCompute Optimizerのメモリ推奨も併用できます。

04同時実行 — RPSではなく「並列数」で考える

本番設計で最も事故りやすいのが同時実行(Concurrency)です。同時実行は秒間リクエスト数(RPS)とは別物で、AWS公式の計算式はシンプルです。

アカウントのデフォルト上限は1リージョンあたり1,000同時実行で、全関数がこのプールを共有します。使い切るとスロットリング(429 TooManyRequestsException)が発生します。さらに見落としがちなのがRPS上限で、AWSはアカウント同時実行の10倍をRPS上限として課します(デフォルト1,000なら10,000 RPS)。処理が100ms未満の関数では、同時実行に余裕があってもRPS側で先に詰まることがあります。

スケール速度にも制約があります。現行の関数単位スケールモデルでは、各関数・各リージョンで10秒あたり最大1,000実行環境(=10秒あたり10,000リクエスト/秒)までスケールします。これより低い『10秒あたり500同時実行/5,000 RPS』といった独立したバースト上限は存在せず、この1,000環境/10,000 RPS(10秒あたり)がスケール速度そのものです。通常は十分ですが、瞬間的な巨大スパイクを受ける設計では、この立ち上がり速度がボトルネックになり得ます。

アカウント同時実行プール(既定1,000)の配分 1リージョンの全関数が共有するプール critical関数A 予約同時実行 400 上限=下限で確保 うちPC 200を 事前初期化 critical関数B 予約同時実行 400 他関数から隔離 その他の関数群 未予約プール 200 残りを奪い合う 枯渇で429 RPS上限 = 同時実行 × 10(既定10,000 RPS) / スケール速度 = 10秒で1,000環境 予約は無料・上限かつ下限。プロビジョンドは事前初期化で常時課金
図:予約同時実行はプールから隔離枠を確保(無料・上限=下限)。プロビジョンド同時実行はその中で環境を事前初期化(課金)。未予約プールが枯渇すると429。

05予約同時実行と上限設計 — 「守る」と「暴走させない」の両立

同時実行の制御レバーは2つあります。混同しやすいので整理します。

関数レベルで予約できるのはデフォルトで最大900までで、Lambdaは常に100ユニットを未予約プール向けに残します。全部を予約枠に取られて予約なし関数が起動不能になる事故を防ぐ仕組みです。予約と上限の考え方は、RDS等の下流を守る観点でも重要になります(RDS Proxyによるコネクション管理と併せて設計すると効果的です)。

現場のコツ:予約同時実行の最大の実務価値は「ダウンストリーム保護」です。RDBやサードパーティAPIなど、無限にスケールされると困る下流がある場合、Lambda側に上限キャップをかけて守ります。バッチ的な関数をreserved=0にして一時的に完全停止させる、といった運用にも使えます。

06タイムアウトとべき等性 — リトライは必ず起きる前提で

タイムアウトは最大15分ですが、「上限まで長く設定しておけば安全」ではありません。長すぎるタイムアウトは、ハングした処理が同時実行枠を占有し続け、コストと枯渇の両方を悪化させます。期待処理時間+余裕の妥当な値にし、下流呼び出しのタイムアウトはそれより短く設定して、Lambdaが強制終了される前に自前でハンドリングできるようにします。

そして本番で最も見落とされるのがべき等性です。非同期呼び出しやイベントソースマッピング(ESM。SQS/Kinesis/DynamoDB Streams等)はat-least-once(少なくとも1回)のセマンティクスで、リトライにより同じイベントが複数回届き得ます。AWS公式も、ESMはべき等性を保証しないため、関数コード側でべき等性を実装するか、べき等キーを使う設計を求めています。

07観測性 — 分散した関数群を「1つのシステム」として見る

Lambdaは細かく分割されるほど、1関数のログを見ても全体像が掴めなくなります。本番運用では最初から観測性を作り込みます。

アラートの閾値設計そのものは監視・アラート設計の考え方と共通です。Lambda固有の勘所は「ThrottlesとInitDurationを最初から見る」ことだと考えてください。

08コスト設計と「いつLambdaを選ぶか」

Lambdaの課金はリクエスト数×実行時間(1ms単位・メモリ量に比例)が基本で、これにPCやSnapStart(Python/.NET)の料金が乗ります。コスト設計の勘所を整理します。

最後に「いつLambdaを選ぶか」の判断軸です。Lambdaが強いのは、イベント駆動・トラフィックが変動的・アイドル時間が長い・運用負荷を下げたいワークロードです。逆に、常時高負荷で稼働率が高い・処理が15分を超える・二桁ミリ秒の一貫した低レイテンシが絶対要件・特殊なランタイムやGPUが必要な場合は、コンテナやEC2のほうが素直です。入口の設計はAPI Gatewayか関数URLかでも変わります(API Gateway対ALB/Lambda URLを参照)。

特性Lambdaが向くコンテナ/EC2が向く
トラフィック変動的・スパイキー・アイドル長い常時高稼働・予測可能
処理時間短〜中(〜15分)長時間・15分超
レイテンシ要件多少のばらつき許容一貫した二桁ms必須
運用負荷下げたい(サーバー管理不要)細かい制御が欲しい

まとめ

Lambdaの本番運用は、機能を並べる話ではなく設計思想の話です。コールドスタートはまずInitを軽くし、足りなければSnapStart→PCの順で埋める。同時実行はRPSではなく「並列数」で見積もり、予約で守り・上限で暴走を防ぐ。リトライは必ず起きる前提でべき等化し、ThrottlesとInitDurationを最初から観測する。コストはメモリ=CPUの関係を踏まえて測って決める。そして「常時高負荷・15分超・厳格な低レイテンシ」ならコンテナやEC2を素直に選ぶ——この判断軸を持っておけば、PoCから本番への移行で大きく踏み外すことはありません。数値・上限・料金はバージョンとリージョンで変わるため、設計確定前に必ずAWS公式で最新をご確認ください。

参考(一次情報)

同時実行設計やコスト試算、既存Lambda群の棚卸しでお悩みでしたら、お問い合わせください。現場で手を動かすところまでご一緒します。

相談する
← ブログ一覧へ戻る