サーバーレス vs コンテナ 2026年: バックエンドチームのための実践的意思決定ガイド

2025年後半、僕のチームは3ヶ月かけてバックエンドアーキテクチャをゼロから再設計した。理由は単純で、サーバーレスで組んだシステムのAWS請求書が毎月更新されるたびに心臓に悪かったから。でも再設計の結論は、最初に予想していたのと全然違う方向に転んだ。

コールドスタートは2026年でも死んでいない

「コールドスタート問題は解決された」という記事をよく見かけるけど、正直半分しか正しくない。

AWS LambdaのSnapStartが安定してきたのは事実だ。僕が2025年10月に計測したデータだと、Node.js 22のLambda関数でコールドスタートが平均280ms。SnapStart有効にすれば40ms台まで下がる。2年前と比べると劇的な改善で、これは素直に評価している。

問題は「頻度」と「タイミング」にある。うちはB2Bサービスで、トラフィックが平日9時〜18時に集中して夜と週末はほぼゼロ。つまり月曜の朝9時が毎週地獄になる。Provisioned Concurrencyで予熱しておけばいい、という話はわかる——でもそれをやった瞬間に「使った分だけ払う」モデルが崩れる。

同僚のKentaが書いたJava製のLambda、SnapStart使っても初回invocationで800ms近くかかることがあった。エンドポイントのレイテンシ要件が500ms以下だったから実質アウト。「p50で見ればいいじゃないか」という話でもなく、SLAの問題だった。

ちなみにランタイムによってコールドスタートの体験が全然違う。Rustで書いたLambdaは5ms台だった——が、6人チームでRustをプロダクションの選択肢にするのは現実的じゃなかった。試しに1関数だけRust化してみたけど、次の担当者が途方に暮れていたので戻した。

「Lambda遅い」「Lambda速い」という議論は、ランタイムとトラフィックパターンを特定しないと何も言っていないに等しい。自分の環境で計測するまで信じないこと。

「サーバーレスは安い」神話が崩れるポイント

最初の見積もりでは、サーバーレスに移行すれば月$400→$80になるという数字を出していた。移行して最初の2ヶ月は$95で順調だった。3ヶ月目に$280。4ヶ月目に$340。

なんで上がったか。トラフィックが伸びたから——だけじゃない。本当の原因が3つあった。コールドスタートを減らすためにProvisioned Concurrencyを増やしたこと、LambdaからDynamoDBを呼ぶコストが想定より高かったこと、そしてS3のGETリクエストが思ったより積み上がっていたこと。

# この設定がコスト爆発のトリガーだった
functions:
  api-handler:
    runtime: nodejs22.x
    memorySize: 512
    timeout: 30
    # 追加した瞬間から月額+$87
    provisionedConcurrency: 10

  background-processor:
    runtime: nodejs22.x
    # 1GBにしたらDB接続が安定したが単価が倍になった
    memorySize: 1024
    reservedConcurrency: 50

同じワークロードをECS Fargateに移したら$80台に収まった。もちろんトラフィックが10倍になればサーバーレスが逆転する可能性はある。でも月間アクティブユーザー5万〜15万のうちのステージでは逆転しなかった。

ここで重要なのは「スケーラビリティの上限」と「スケーラビリティの下限」を分けて考えること。サーバーレスがコスト効率最悪になるのは、「ゼロではないが爆発的でもない」中間のトラフィック帯だ。100万リクエスト/日を超えるか、ほぼゼロかのどちらかなら話は変わる。

WebSocketで詰まって、コンテナに救われた夜

2025年8月の金曜午後4時にデプロイした。

リアルタイムコラボレーション機能をLambda + API Gateway WebSocketで組んでいた。接続状態をDynamoDBに保存して、メッセージ送信のたびにルックアップするという設計。テスト環境では完璧に動いた。

本番で100人が同時接続したとき、DynamoDBのWCUが3分間で200倍になり、Lambda関数がタイムアウトして接続が全部落ちた。ユーザーから「何も動かない」という報告が来たのが夜9時だった。あの週末のSlackスレッドは今も見たくない。

土日を丸々使って原因調査して、月曜にECS Fargateへ移行した。コンテナはメモリ上に接続状態を持てるから、WebSocket管理がそのまま書けた。

// コンテナ版: 接続オブジェクトをメモリで直接管理
class ConnectionManager {
  private connections = new Map<string, WebSocket>();

  register(id: string, ws: WebSocket) {
    this.connections.set(id, ws);
    // Lambda だとこのオブジェクトを次のinvocationまで保持できない
  }

  broadcast(roomId: string, message: string) {
    for (const [id, ws] of this.connections) {
      if (ws.readyState === WebSocket.OPEN) {
        ws.send(message);
      }
    }
  }

  cleanup(id: string) {
    this.connections.delete(id);
  }
}

サーバーレスで解決できないわけじゃない。Redis PubSubをかませれば動く。でもその複雑さを抱える価値があるかという問いに対して、答えは「ない」だった。

ステートレスにできないものを無理やりステートレスにしようとするのが、サーバーレス移行で最もよく見る失敗パターンだと思う。設計の問題以前に、ミスマッチの問題だ。

AIワークロードがこのトレードオフをひっくり返している件

ここが2026年に特有の話で、正直まだ全部整理できていない部分がある。

LLM推論をバックエンドに組み込むケースが増えてきた。うちも今年からAnthropicのAPIをプロダクションで使い始めた。これ、サーバーレスとコンテナの選択に影響している。

外部LLM APIを呼ぶだけなら、タイムアウトを長く設定(うちは120秒にした)すればLambdaで普通に動く。ストリーミングレスポンスもLambda Function URLsを使えばそのままストリームできる。この用途ではサーバーレスで十分だ。

問題はローカルモデルの場合。OllamaやONNX Runtimeでエッジ推論するなら、GPUインスタンスが必要になる。これはサーバーレスではカバーできない。EKSにGPUノードを追加して管理、という構成が現実解になる——ただこれは6人チームには重すぎる選択肢だった。

あと、RAGアーキテクチャでpgvectorに接続を常に持っておきたい場合。接続プールをコンテナで管理した方が安定する。Lambdaでも接続プールの再利用はできるけど、コールドスタートのたびに接続を張り直すコストがじわじわ効いてくる。

感覚として、推論レイヤーはコンテナ、前処理・後処理のステートレスな部分はサーバーレス、という組み合わせが現実的に増えている気がする。断言できるほど経験が溜まっていないのが正直なところだけど。

6人チームが出した実際の結論

比較してきたけど、うちのチームは「ハイブリッド」に落ち着いた。ただし設計ポリシーを明確にしたハイブリッドで、「どっちでもいいや」の気持ちいいとこ取りじゃない。

Lambdaに向いているもの:非同期バッチ処理(メール送信・レポート生成)、Webhookの受け取りとSQSへの投入、スケジュールドジョブ、低頻度・低レイテンシ要件のエンドポイント。ECS Fargateに向いているもの:メインのREST/GraphQL API、WebSocket接続が必要なサービス、LLM接続を持続させるミドルウェア、セッション状態を持つ処理全般。

分け方の基準は2軸だけ——「トラフィックの形(予測不能か安定か)」と「状態の必要性(ステートレスにできるか)」。それ以外の理由でどちらかを選ぶのはやめた。

KubernetesとEKSは選ばなかった。6人チームには管理コストが重すぎる。EKSが必要になるのは、20〜30人以上のチームか、GPUワークロードがある場合だと思っている。

もし今から新しいプロジェクトを始めるなら——最初の3ヶ月はサーバーレスで全部作る。スタートアップ初期はトラフィックが予測できないから、ゼロコストで始められるメリットが大きい。月間リクエスト数が安定してきたら、コスト計算して移行を検討する。この順番が一番リスクが低い。

「サーバーレスかコンテナか」を最初に決めようとしなくていい。「このユースケースに何が合うか」を毎回問う癖をつけた方がいい。どちらかに統一したくなる気持ちはわかる——運用の複雑さを一元化したいなら、それはそれで正当な理由だ——2026年においてはハイブリッドが現実解になっているケースの方が多いと感じている。

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top