Featured image of post サーバーレスアーキテクチャパターン:Lambda関数を超えて Featured image of post サーバーレスアーキテクチャパターン:Lambda関数を超えて

サーバーレスアーキテクチャパターン:Lambda関数を超えて

イベント駆動設計、ファンアウト/ファンイン、サーガパターン、コールドスタート対策、コスト最適化戦略など、サーバーレスアーキテクチャの高度なパターンを探求。

サーバーレスコンピューティングは、API Gateway背後にLambda関数を置く単純なAPI置き換えから大きく進化しました。現代のサーバーレスアーキテクチャは完全にイベント駆動で非同期であり、モノリスを調整された関数ワークフローに分解します。主要企業がAWS Lambda、Azure Functions、Cloudflare Workersで本番ワークロードを運用する現在、基本を超えたパターンの理解が不可欠です。

イベント駆動アーキテクチャの基礎

サーバーレスの核心はイベント駆動モデルです。イベントプロデューサーがイベントを発行し、イベントルーターが配信し、イベントコンシューマーが反応します。AWSは複数のルーティングサービスを提供します。EventBridge(スキーマ認識イベントバス)、SQS(キューによる疎結合)、SNS(パブサブメッセージング)、Kafka(高スループットストリーミング)です。

冪等性は重要です。関数は重複イベントを安全に処理する必要があります。必ず冪等性キーと重複排除ロジックを実装しましょう。

interface OrderEvent {
  orderId: string;
  idempotencyKey: string;
  items: Array<{ sku: string; quantity: number }>;
}

async function handleOrder(event: OrderEvent) {
  const processed = await checkIdempotency(event.idempotencyKey);
  if (processed) return { status: "duplicate" };
  await processOrder(event.items);
  await markIdempotent(event.idempotencyKey);
  return { status: "processed" };
}

デッドレターキュー(DLQ)は失敗したイベントを保持し、後で分析できるようにします。DLQがないと、 poisoned message がパイプライン全体をブロックする可能性があります。


ファンアウト / ファンインパターン

ファンアウトはワークロードを複数の並列ワーカーに分散し、ファンインは結果を収集します。S3イベント通知は複数のLambda関数にファンアウトして、サムネイル生成、メタデータ抽出、OCR処理を並列実行できます。

AWS Step FunctionsはMap状態でネイティブなファンアウトを提供します:

{
  "Map": {
    "ItemsPath": "$.files",
    "MaxConcurrency": 10,
    "Iterator": {
      "StartAt": "ProcessFile",
      "States": {
        "ProcessFile": {
          "Type": "Task",
          "Resource": "arn:aws:lambda:process-file",
          "End": true
        }
      }
    },
    "ResultPath": "$.results",
    "End": true
  }
}

ファンインにはDynamoDBが結果集約器として機能します。各並列ワーカーは相関IDをキーとするDynamoDBアイテムに結果を書き込み、最終関数が全結果の件数を確認してから処理を完了します。

パターンユースケースレイテンシ
S3 → SQS → Lambda画像処理パイプライン秒単位
Step Functions Map調整された並列タスク分単位
DynamoDB Streams → Lambdaリアルタイム集約サブ秒

サーガパターンによる分散トランザクション

サーバーレスマイクロサービスでは、2相コミットなしで分散トランザクションを調整する必要があります。サーガパターンは、コレオグラフィ(各サービスがイベントを発行して次のサービスをトリガー)またはオーケストレーション(中央コーディネーターがワークフローを管理)で実現します。

AWS Step Functionsはサーガオーケストレーターとして優れています。各ステップはTask状態で、失敗時に補償トランザクションが実行されます。航空券、ホテル、レンタカーの予約システムの例:

const sagaDefinition = {
  Comment: "旅行予約サーガ",
  StartAt: "BookFlight",
  States: {
    BookFlight: {
      Type: "Task",
      Resource: "arn:aws:lambda:book-flight",
      Catch: [{ ErrorEquals: ["States.ALL"], Next: "CancelFlight" }],
      Next: "BookHotel"
    },
    BookHotel: {
      Type: "Task",
      Resource: "arn:aws:lambda:book-hotel",
      Catch: [{ ErrorEquals: ["States.ALL"], Next: "CancelFlight" }],
      Next: "BookCar"
    },
    BookCar: {
      Type: "Task",
      Resource: "arn:aws:lambda:book-car",
      Catch: [{ ErrorEquals: ["States.ALL"], Next: "CancelHotel" }],
      End: true
    },
    CancelFlight: { Type: "Task", Resource: "arn:aws:lambda:cancel-flight", Next: "Fail" },
    CancelHotel: { Type: "Task", Resource: "arn:aws:lambda:cancel-hotel", Next: "Fail" },
    Fail: { Type: "Fail" }
  }
};

レンタカー予約が失敗した場合、サーガはホテルと航空券の予約を補償Lambdaでロールバックします。サーガの状態はDynamoDBなどに永続化し、部分的な障害からの復旧に備えます。


コールドスタート対策

コールドスタートはLambdaが新しい実行環境を起動する際に発生します。Javaと.NETランタイムのペナルティが最も大きく、Node.jsとPythonは数十ミリ秒で起動します。VPC対応関数はENIアタッチメントにさらに数秒追加されます。

ランタイムコールドスタート(中央値)コールドスタート(p99)
Node.js45 ms250 ms
Python60 ms300 ms
Java (SnapStart)150 ms500 ms
Java (SnapStartなし)3,000 ms8,000 ms
.NET2,500 ms7,000 ms

Provisioned Concurrencyは指定数の環境をウォーム状態に保ちますが、コストが発生します。Java向けSnapStartは初期化済み環境のスナップショットを取得し、起動時間を大幅に短縮します。「最適化してから緩和」戦略が効果的です。デプロイパッケージを最小化し、ランタイムを適切に選択し、依存関係を整理します。

EventBridgeを使用したウォームアップスケジューラで、予測可能なトラフィックパターンに関数をウォームに保つこともできます:

export async function warmUp() {
  const functions = [
    "order-processor", "payment-handler", "notification-service"
  ];
  for (const fn of functions) {
    await lambda.invoke({
      FunctionName: fn,
      InvocationType: "RequestResponse",
      Payload: JSON.stringify({ warmup: true })
    }).promise();
  }
}

可観測性とコスト最適化

サーバーレスの可観測性には課題があります。関数は一時的で、トレースは複数サービスにまたがり、ログボリュームは高くなりがちです。JSONによる構造化ログと相関IDの伝搬が不可欠です。

const logger = {
  info: (msg: string, context?: object) => {
    console.log(JSON.stringify({
      level: "INFO",
      message: msg,
      requestId: context?.awsRequestId,
      timestamp: new Date().toISOString(),
      ...context
    }));
  }
};

AWS X-Rayが分散トレーシングを提供し、OpenTelemetryがLambda Telemetry APIを介してベンダー中立の自動インストルメンテーションを実現します。

コスト面では、ARM64(Graviton2)関数はx86より20%安価です。大きなペイロードにはLambda呼び出しではなくS3を利用し、予約同時実行数でスケーリングコストを制御します。最も効果的な最適化は不要な呼び出しを排除することです。定期的に関数トリガーを監査し、古いイベントルールを削除し、類似ハンドラーを統合しましょう。

サーバーレスアーキテクチャは本番運用に十分成熟しています。イベント駆動設計、サーガオーケストレーション、コールドスタート対策、可観測性は必須です。「サーバーレスファースト」の考え方を採用しつつ、コンテナが適切な選択となるケースについても常に検討しましょう。