EventBridge × VPC Lattice × Fargate 統合本番運用 完全ガイド

EventBridge × VPC Lattice × Fargate 統合本番運用 — イベント駆動マイクロサービスの最短ルート

EventBridge × VPC Lattice × Fargate 統合アーキテクチャ|Event-Driven Microservices 4本柱

EventBridge × VPC Lattice × Fargate 統合本番運用 — 3軸隣接領域統合編
本記事は Serverless本番運用 Vol2 (EventBridge×SQS×SNS×Kinesis) + Network本番運用 Vol2 (TGW×PrivateLink×DX) + Container本番運用 Vol1 (ECS×Fargate×ECR) の隣接領域を統合し、イベント駆動マイクロサービスを Fargate + VPC Lattice + EventBridge の3層で実装する本番運用ガイドです。

本記事の対象

  • EventBridge Rule/EventBus基礎を理解しており Fargate Service 連携を本番設計したいエンジニア
  • VPC Lattice Service Network で Microservice 間通信を実装中のチーム
  • Service Discovery + Auth Policy + Container Insights の3層統合に課題を感じる中堅

本記事完遂後の発展: Service Connect + Container Vol2 GitOps編 (ArgoCD/Kustomize) で本番Kubernetes統合へ。


1. なぜEventBridge × VPC Lattice × Fargate 統合か — 3軸隣接領域の本番統合パターン

単一サービス運用の限界と3軸統合の必然性

マイクロサービスアーキテクチャが成熟するにつれ、EventBridge単体VPC Lattice単体Fargate単体での運用では回避できない本番課題が顕在化します。

EventBridge単体の限界: Rule → Lambda 直結は Lambda の15分制限という絶対的な壁に当たります。バッチ処理・動画エンコード・機械学習推論・大規模データ変換など、分単位〜時間単位の処理では Fargate Task への統合が必須です。Lambdaのタイムアウト上限を設計時に無視すると、本番で突然ジョブが切断されるインシデントを引き起こします。

VPC Lattice単体の限界: Service Network でマイクロサービス間通信を整備しても、イベント駆動でのサービス呼び出し機構がありません。同期HTTP呼び出しのみのアーキテクチャでは、サービス間の依存が密結合になり、一時的な障害が連鎖します。EventBridge との統合で非同期イベント発行とサービス間デカップリングが実現します。

Fargate単体の限界: ECS Service として常時稼働するコンテナは、低頻度バッチやイベントトリガー処理には過剰なコストです。EventBridge Scheduler + Fargate Task の組み合わせで、実行時のみコンテナを起動するサーバーレスバッチが実現します。VPC Lattice の Auth Policy と組み合わせることで、Fargate Task 間の IAM 認証通信も統一されます。

3軸統合で実現する5つの本番品質

品質軸実現要素単体では困難な理由
Event-DrivenEventBridge Rule/EventBus/PipesLambda 15分制限超ジョブは Fargate Task 必須
Service MeshVPC Lattice Service Network + TargetGroupサービス発見と認証をネットワーク層で統合
Container OrchestrationFargate + ECS Task Definition + awsvpcイベント受信後のコンテナライフサイクル管理
IAM AuthVPC Lattice Auth Policy + Fargate Task Role二重認証の統合設計が単体では複雑化
X-Ray TraceEventBridge → Fargate → VPC Lattice の3層トレースTrace Context 伝搬が層をまたぐと断絶

隣接3シリーズとの差別化

本記事は以下の3シリーズの延長上に位置しますが、統合設計という点で質的に異なります。

  • Serverless本番運用 Vol2 (EventBridge×SQS×SNS×Kinesis): EventBridge の詳細設定と Lambda/SQS との非同期統合にフォーカス。コンテナ・ネットワーク層の統合は対象外。
  • Network本番運用 Vol2 (TGW×PrivateLink×DX): VPC 間接続と Transit Gateway 中心。VPC Lattice のサービスメッシュ応用と EventBridge 統合は対象外。
  • 本記事 (EventBridge×VPC Lattice×Fargate): 3層を同時に設計・実装し、イベント発火からコンテナ実行・サービス間通信・分散トレースまで一貫した本番運用パターンを提供。
痛点5選: 3軸統合本番運用で直面する地雷

  • 痛点1: Lambda 15分制限超で Fargate 統合パターン不明
    EventBridge Rule → Lambda でジョブを起動していたが、処理時間が15分超になると強制終了。Fargate Task Target への切り替え方法が不明で、TaskOverride で環境変数をイベントペイロードとして渡す設計にたどり着けない。
  • 痛点2: VPC Lattice TargetGroup に Fargate Task が登録されない
    ECS Service を VPC Lattice TargetGroup に登録しようとすると失敗。原因は Fargate が awsvpc ネットワークモード必須なのに対し、TargetGroup が IP Type で ENI の IP を直接登録する仕様を知らないこと。
  • 痛点3: Pipes + DynamoDB Streams → Fargate でデッドレターキュー設計不在
    EventBridge Pipes の Source に DynamoDB Streams を設定し Fargate Task を起動する構成で、タスク失敗時の DLQ が未設定。失敗イベントがサイレントに消えて障害原因調査が不可能になる。
  • 痛点4: VPC Lattice Auth Policy + Fargate Task Role の二重認証設計が複雑化
    VPC Lattice の Service Auth Policy (SigV4 必須) と Fargate Task の IAM Role (ECS Task Execution Role + Task Role) が重なり、どの IAM Principal に何の権限が必要か整理できない。
  • 痛点5: 3層 X-Ray 分散トレースが断絶
    EventBridge → Fargate → VPC Lattice という呼び出し連鎖で X-Ray Service Map が繋がらない。EventBridge は X-Ray トレースを自動注入しないため、Fargate Task 側でのトレース ID 引き継ぎが必要なことを見落とす。

統合本番運用5原則

本記事を通じて以下の5原則を実装します。それぞれのセクションで具体的な Terraform コードとともに解説します。

  1. EventBus分離原則: 業務イベントはカスタム EventBus で分離。Default EventBus は AWS サービスイベント専用として混在を防ぐ。
  2. Service Network階層化原則: VPC Lattice Service Network はワークロード境界 (prod/staging) 単位で分離。一つの Service Network への過剰な Service 集約を避ける。
  3. Fargate awsvpcモード統一原則: すべての Fargate Task/Service で awsvpc ネットワークモードを使用。VPC Lattice TargetGroup (IP Type) との整合性を確保。
  4. Auth Policy統合原則: VPC Lattice Auth Policy と Fargate Task Role の IAM Principal を明示的にマッピング。暗黙の権限継承に頼らない。
  5. X-Ray 3層トレース原則: EventBridge → Fargate Task → VPC Lattice Endpoint の各層でトレース Context を明示的に伝搬。X-Ray SDK の手動計装を省略しない。

Serverless本番運用 Vol2 — EventBridge×SQS×SNS×Kinesis


2. EventBridge本番運用 — Rule × EventBus × Pipes × Scheduler

EventBridge 4本柱と Fargate Task統合パターン

EventBridge 4本柱の役割と選定基準

EventBridge は単一サービスのように見えて、実際には4つの独立した機能柱で構成されています。本番運用では各柱の役割を明確に分離し、用途に応じて使い分けることが重要です。

コンポーネント主用途Fargate統合パターン特記事項
EventBridge Ruleイベントパターンマッチング → Target起動Rule → ECS RunTask1 Rule = 最大5 Targets
EventBusイベントルーティングの分離・アーカイブカスタムEventBus → Rule → FargateCross-account送信可能
Pipesソース→Target 変換パイプラインSQS/DDB Streams → Fargate TaskFiltering + Enrichment 内蔵
Schedulercron/one-time スケジュール実行Scheduler → ECS RunTaskスケジューラ専用 (Rule.schedule_expression の後継)

Rule Event Pattern 設計 — source + detail-type 厳密化

Event Pattern は sourcedetail-type の二段階で絞り込むのが本番品質の基本です。detail のみで絞り込むと、他サービスや他環境のイベントが意図せず一致するリスクがあります。

{
  "source": ["com.myapp.order-service"],
  "detail-type": ["OrderCreated"],
  "detail": {
 "status": ["PENDING"],
 "amount": [{ "numeric": [">=", 10000] }]
  }
}

このパターンは com.myapp.order-service が発行した OrderCreated イベントのうち、status=PENDING かつ amount >= 10000 のものだけを Fargate Task で処理します。source を省略すると Default EventBus の全イベントにマッチする可能性があるため、必ず指定してください。

Terraform での実装:

resource "aws_cloudwatch_event_rule" "order_fargate" {
  name  = "order-fargate-processor"
  description = "OrderCreated(PENDING, amount>=10000) → Fargate Task"
  event_bus_name = aws_cloudwatch_event_bus.app.name

  event_pattern = jsonencode({
 source= ["com.myapp.order-service"]
 detail-type = ["OrderCreated"]
 detail = {
status = ["PENDING"]
amount = [{ numeric = [">=", 10000] }]
 }
  })

  tags = {
 Environment = "production"
 ManagedBy= "terraform"
  }
}

カスタムEventBus vs Default EventBus — 本番使い分け基準

Default EventBus は AWS サービス (EC2 State Change, RDS, S3 Event Notification 等) が自動的にイベントを送信するバスです。業務アプリケーションのカスタムイベントを Default EventBus に混在させると、イベントの整理・アーカイブ・監査が複雑化します。

resource "aws_cloudwatch_event_bus" "app" {
  name = "myapp-production"

  tags = {
 Environment = "production"
 Purpose  = "application-events"
  }
}

# EventBus Policy: Cross-account イベント受信 (同一Org内)
resource "aws_cloudwatch_event_bus_policy" "app" {
  event_bus_name = aws_cloudwatch_event_bus.app.name

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Sid = "AllowOrgAccounts"
Effect = "Allow"
Principal = { AWS = "*" }
Action = "events:PutEvents"
Resource  = aws_cloudwatch_event_bus.app.arn
Condition = {
  StringEquals = {
 "aws:PrincipalOrgID" = var.organization_id
  }
}
 }]
  })
}

カスタムEventBus を使うべき場合:
– アプリケーション固有のカスタムイベント発行
– 複数アカウント・複数環境への Cross-account ルーティング
– イベントのアーカイブ・リプレイが必要な業務フロー
– Default EventBus とのイベント分離が監査要件になる場合

Default EventBus を使うべき場合:
– EC2 インスタンス状態変化・RDS フェイルオーバー等の AWS サービスイベント受信
– CloudTrail イベントのリアルタイム反応 (API Call 監視等)

EventBridge Pipes 実装 — SQS/DDB Streams → Fargate Task

EventBridge Pipes は Source からのイベントを Filtering → Enrichment → Target の順に処理するフルマネージドパイプラインです。従来 Lambda を中継レイヤーとして挿入していた変換処理を、Pipes の Input Transformation で代替できます。

resource "aws_pipes_pipe" "ddb_to_fargate" {
  name  = "orders-ddb-to-fargate"
  role_arn = aws_iam_role.pipes.arn

  source = aws_dynamodb_table.orders.stream_arn
  source_parameters {
 dynamodb_stream_parameters {
starting_position= "LATEST"
batch_size = 10
maximum_retry_attempts = 3

dead_letter_config {
  arn = aws_sqs_queue.pipes_dlq.arn
}
 }

 filter_criteria {
filter {
  pattern = jsonencode({
 eventName = ["INSERT"]
 dynamodb  = { NewImage = { status = { S = ["PENDING"] } } }
  })
}
 }
  }

  target = "arn:aws:ecs:${var.region}:${var.account_id}:cluster/${aws_ecs_cluster.app.name}"
  target_parameters {
 ecs_task_parameters {
task_definition_arn = aws_ecs_task_definition.order_processor.arn
launch_type= "FARGATE"

network_configuration {
  aws_vpc_configuration {
 subnets = var.private_subnet_ids
 security_groups  = [aws_security_group.fargate_task.id]
 assign_public_ip = "DISABLED"
  }
}

overrides {
  container_override {
 name = "order-processor"
 environment {
name  = "EVENT_PAYLOAD"
value = "$.detail"
 }
 environment {
name  = "TRACE_ID"
value = "$.id"
 }
  }
}

task_count = 1
 }
  }

  tags = {
 Environment = "production"
 ManagedBy= "terraform"
  }
}

resource "aws_sqs_queue" "pipes_dlq" {
  name  = "orders-pipes-dlq"
  message_retention_seconds  = 1209600  # 14日
  visibility_timeout_seconds = 300

  tags = {
 Environment = "production"
  }
}
Pipes DLQ 設計の必須チェックポイント

  • DLQ は Pipes Source ごとに独立して設定: dead_letter_config を省略すると、Target (Fargate Task) 失敗時のイベントがサイレントに消える。本番では必ず SQS DLQ を設定すること。
  • maximum_retry_attempts は 3 が基準: 一時的なネットワーク障害・Fargate キャパシティ不足に対応。処理の冪等性 (idempotency) を実装した上で設定する。
  • DLQ のメッセージ保持期間は14日: 障害発生から分析・修正・リプレイまでの猶予として14日 (1209600秒) が実運用での推奨値。
  • DLQ アラーム設定: ApproximateNumberOfMessagesVisible > 0 で CloudWatch Alarm を設定し、失敗イベントの蓄積を即座に検知する。

EventBridge Scheduler — cron + Group + DLQ + Retry 設計

EventBridge Scheduler は従来の CloudWatch Events Schedule Rules の後継で、タイムゾーン対応Group管理フレキシブルウィンドウDLQが追加されています。

resource "aws_scheduler_schedule_group" "batch" {
  name = "batch-jobs-production"

  tags = {
 Environment = "production"
  }
}

resource "aws_scheduler_schedule" "nightly_batch" {
  name = "nightly-order-aggregation"
  group_name = aws_scheduler_schedule_group.batch.name

  flexible_time_window {
 mode = "FLEXIBLE"
 maximum_window_in_minutes = 15
  }

  schedule_expression = "cron(0 2 * * ? *)"
  schedule_expression_timezone = "Asia/Tokyo"

  target {
 arn= "arn:aws:ecs:${var.region}:${var.account_id}:cluster/${aws_ecs_cluster.app.name}"
 role_arn = aws_iam_role.scheduler.arn

 ecs_parameters {
task_definition_arn = aws_ecs_task_definition.aggregation.arn
launch_type= "FARGATE"

network_configuration {
  assign_public_ip = false
  security_groups  = [aws_security_group.fargate_task.id]
  subnets = var.private_subnet_ids
}

task_count = 1
 }

 retry_policy {
maximum_retry_attempts = 3
maximum_event_age_in_seconds = 3600
 }

 dead_letter_config {
arn = aws_sqs_queue.scheduler_dlq.arn
 }
  }
}

resource "aws_sqs_queue" "scheduler_dlq" {
  name = "scheduler-dlq-production"
  message_retention_seconds = 1209600

  tags = { Environment = "production" }
}

Scheduler 本番設計のポイント:
flexible_time_window で最大15分のずれを許容することで、同時刻起動によるリソース競合を回避
schedule_expression_timezone = "Asia/Tokyo" で JST ベースのスケジュール指定 (UTC 変換不要)
retry_policy.maximum_event_age_in_seconds = 3600 でスケジュール起動から1時間以内のリトライに制限

Fargate Task Target 統合 — TaskOverride でイベントペイロード受渡

EventBridge Rule から Fargate Task を起動する際、イベントの内容をコンテナに渡す唯一の手段が ECS Task Override の環境変数です。イベントペイロードを S3 や DynamoDB に書き込んで Fargate から参照するパターンも存在しますが、シンプルな構成では環境変数経由が推奨です。

resource "aws_cloudwatch_event_target" "fargate" {
  rule  = aws_cloudwatch_event_rule.order_fargate.name
  event_bus_name = aws_cloudwatch_event_bus.app.name
  target_id= "fargate-order-processor"
  arn= aws_ecs_cluster.app.arn
  role_arn = aws_iam_role.eventbridge_ecs.arn

  ecs_target {
 task_definition_arn = aws_ecs_task_definition.order_processor.arn
 launch_type= "FARGATE"
 task_count = 1

 network_configuration {
subnets = var.private_subnet_ids
security_groups  = [aws_security_group.fargate_task.id]
assign_public_ip = false
 }
  }

  input_transformer {
 input_paths = {
order_id= "$.detail.orderId"
amount  = "$.detail.amount"
event_id= "$.id"
event_time = "$.time"
 }

 input_template = jsonencode({
containerOverrides = [{
  name = "order-processor"
  environment = [
 { name = "ORDER_ID", value = "<order_id>" },
 { name = "AMOUNT",value = "<amount>" },
 { name = "EVENT_ID", value = "<event_id>" },
 { name = "EVENT_TIME",  value = "<event_time>" }
  ]
}]
 })
  }
}

resource "aws_iam_role" "eventbridge_ecs" {
  name = "eventbridge-ecs-execution"

  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = { Service = "events.amazonaws.com" }
Action = "sts:AssumeRole"
 }]
  })
}

resource "aws_iam_role_policy" "eventbridge_ecs" {
  name = "ecs-run-task"
  role = aws_iam_role.eventbridge_ecs.id

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect= "Allow"
  Action= ["ecs:RunTask"]
  Resource = aws_ecs_task_definition.order_processor.arn
},
{
  Effect= "Allow"
  Action= ["iam:PassRole"]
  Resource = [
 aws_iam_role.ecs_task_execution.arn,
 aws_iam_role.ecs_task_role.arn
  ]
}
 ]
  })
}
EventBridge → Fargate Target IAM 設計の落とし穴

  • ecs:RunTask だけでは不十分: EventBridge の IAM Role には ecs:RunTask に加えて iam:PassRole が必要。Task Execution Role と Task Role の両方に PassRole を付与しないと AccessDeniedException が発生する。
  • Resource は Task Definition ARN まで絞り込む: ecs:RunTask の Resource に * を使うと最小権限原則違反。Task Definition ARN (リビジョン指定 or :* で任意リビジョン) を指定する。
  • Fargate awsvpc モードで assign_public_ip = false 必須: Private Subnet に NAT Gateway + VPC Endpoint 構成が前提。Public IP を付与すると VPC Lattice Auth Policy の IP ベース制御が破綻する。

Dead Letter Queue + Retry Policy 設計

EventBridge の DLQ とリトライポリシーは、Rule レベルと Scheduler レベルで独立して設定が必要です。

# Rule レベルの DLQ
resource "aws_cloudwatch_event_target" "fargate_with_dlq" {
  rule  = aws_cloudwatch_event_rule.order_fargate.name
  event_bus_name = aws_cloudwatch_event_bus.app.name
  target_id= "fargate-order-processor"
  arn= aws_ecs_cluster.app.arn
  role_arn = aws_iam_role.eventbridge_ecs.arn

  retry_policy {
 maximum_retry_attempts = 3
 maximum_event_age_in_seconds = 3600
  }

  dead_letter_config {
 arn = aws_sqs_queue.eventbridge_dlq.arn
  }

  ecs_target {
 task_definition_arn = aws_ecs_task_definition.order_processor.arn
 launch_type= "FARGATE"
 task_count = 1

 network_configuration {
subnets = var.private_subnet_ids
security_groups  = [aws_security_group.fargate_task.id]
assign_public_ip = false
 }
  }
}

resource "aws_sqs_queue" "eventbridge_dlq" {
  name  = "eventbridge-fargate-dlq"
  message_retention_seconds  = 1209600
  visibility_timeout_seconds = 300

  redrive_allow_policy = jsonencode({
 redrivePermission = "allowAll"
  })

  tags = { Environment = "production" }
}

# DLQ メッセージ蓄積アラーム
resource "aws_cloudwatch_metric_alarm" "eventbridge_dlq_alarm" {
  alarm_name = "eventbridge-fargate-dlq-not-empty"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name= "ApproximateNumberOfMessagesVisible"
  namespace  = "AWS/SQS"
  period  = 300
  statistic  = "Sum"
  threshold  = 0
  alarm_description= "EventBridge Fargate DLQ にメッセージが蓄積しています"
  alarm_actions = [aws_sns_topic.alerts.arn]

  dimensions = {
 QueueName = aws_sqs_queue.eventbridge_dlq.name
  }
}

mermaid01: EventBridge Rule → Fargate Task 起動フロー

flowchart LR
 subgraph Sources["イベントソース"]
  APP["アプリケーション\n(PutEvents)"]
  DDB["DynamoDB Streams\n(変更データ)"]
  SCHED["Scheduler\n(cron / one-time)"]
 end

 subgraph EventBridge["EventBridge レイヤー"]
  BUS["カスタム EventBus\n(myapp-production)"]
  RULE["EventBridge Rule\n(Event Pattern Match)"]
  PIPES["EventBridge Pipes\n(Filter → Transform)"]
 end

 subgraph ECS["ECS / Fargate レイヤー"]
  CLUSTER["ECS Cluster\n(Fargate)"]
  TASKDEF["Task Definition\n(awsvpc モード)"]
  TASK["Fargate Task\n(コンテナ実行)"]
 end

 subgraph Result["処理結果"]
  SUCCESS["正常完了\n(Exit Code 0)"]
  DLQ["SQS DLQ\n(失敗イベント保持)"]
  ALARM["CloudWatch Alarm\n(DLQ 蓄積検知)"]
 end

 APP -->|PutEvents| BUS
 BUS -->|Route| RULE
 DDB -->|Stream| PIPES
 SCHED -->|RunTask| CLUSTER

 RULE -->|ECS RunTask\n+ TaskOverride| CLUSTER
 PIPES -->|ECS RunTask\n+ ContainerOverride| CLUSTER

 CLUSTER --> TASKDEF
 TASKDEF --> TASK

 TASK -->|Exit 0| SUCCESS
 TASK -->|Exit != 0\nmax retry 3| DLQ
 DLQ -->|ApproximateMessages > 0| ALARM
EventBridge → Fargate 統合 本番チェックリスト

  • ✅ カスタム EventBus でアプリケーションイベントを分離 (Default EventBus との混在なし)
  • ✅ Rule Event Pattern に source + detail-type を必ず指定 (過剰マッチ防止)
  • ✅ EventBridge IAM Role に ecs:RunTask + iam:PassRole (Task Execution Role + Task Role 両方)
  • ✅ Fargate Task は awsvpc ネットワークモード + Private Subnet + NAT Gateway 構成
  • ✅ Rule / Pipes / Scheduler すべてに DLQ + Retry Policy (maximum_retry_attempts=3) を設定
  • ✅ DLQ に CloudWatch Alarm (ApproximateNumberOfMessagesVisible > 0) を設定
  • ✅ TaskOverride の環境変数でイベントペイロードをコンテナに受渡
  • ✅ Scheduler は cron + JST タイムゾーン + flexible_time_window で競合回避