メインコンテンツまでスキップ

AWS でのイベント駆動アーキテクチャ構築

スケーリングパターン で学んだ対策を、AWS のサービスを使って実現する構成パターンを学びます。


イベントデータのライフサイクル

1つのイベントがアプリケーションで発生してから、分析・アーカイブされるまでの全体像です。

┌─────────────────────────────────────────────────────────────────────┐
│ イベントデータのライフサイクル │
│ │
│ [1] 発生 ユーザー操作(ログイン、トークン発行等) │
│ │ アプリケーション内で同期的に処理 │
│ │ │
│ │ 同期 │
│ ▼ │
│ [2] 書き込み RDS (PostgreSQL) に INSERT │
│ │ トランザクション内で確実に記録 │
│ │ ここまでがリクエスト処理の範囲 │
│ │ │
│ │ 非同期 ─── ここからリクエスト処理とは独立 ────────────── │
│ │ │
│ ├──→ [3a] アプリ内通知(同一プロセス・非同期スレッド) │
│ │ Spring @EventListener / @Async │
│ │ イベント1件ごとに: │
│ │ ① フック設定を確認(テナントに設定があるか?) │
│ │ ② トリガー判定(このイベント種別が対象か?) │
│ │ ③ 対象なら実行(Slack, Webhook, Email) │
│ │ ④ 対象外ならスキップ │
│ │ レイテンシ: ミリ秒(スキップ時)〜 500ms(実行時) │
│ │ │
│ ├──→ [3b] CDC 伝搬(WAL → CDC ツール → 外部システム) │
│ │ PeerDB / Debezium / MSK Connect │
│ │ ・アプリ変更不要(DBレベルで自動) │
│ │ レイテンシ: 数秒 │
│ │ │
│ └──→ [3c] メッセージキュー(アプリ → Kafka → 購読者) │
│ アプリが明示的に publish │
│ ・複数の購読者に同時配信 │
│ レイテンシ: ミリ秒〜秒 │
│ │
│ ── 3a/3b/3c は併用可能 ── │
│ │
│ ▼ │
│ [4] 読み取りモデル構築(各購読者が非同期で処理) │
│ │ │
│ ├── ClickHouse: バッチINSERT → 統計・集計クエリ │
│ │ レイテンシ: 秒(バッチ間隔依存) │
│ │ │
│ ├── OpenSearch: インデクシング → 全文検索・ログ検索 │
│ │ レイテンシ: 秒 │
│ │ │
│ └── S3: Parquet 書き出し → 長期保存 │
│ レイテンシ: 分〜時間(バッチ) │
│ │
│ ▼ │
│ [5] 利用 │
│ │ │
│ ├── ダッシュボード: ClickHouse から集計クエリ │
│ ├── 監査ログ検索: OpenSearch でフィルタ・全文検索 │
│ ├── 月次レポート: ClickHouse で年次集計(数秒) │
│ └── アドホック分析: Athena で S3 上の過去データを分析 │
│ │
│ ▼ │
│ [6] アーカイブ・削除 │
│ │
│ ├── RDS: パーティション管理で90日保持 → archive へ移動 │
│ ├── ClickHouse: TTL で1年保持 → 自動削除 │
│ ├── OpenSearch: Index Lifecycle で30日保持 → 削除 │
│ └── S3: ライフサイクルポリシーで Glacier → 削除 │
│ │
└─────────────────────────────────────────────────────────────────────┘

同期・非同期の境界

イベントの記録(INSERT)は同期、その後の処理は全て非同期。これがイベント駆動アーキテクチャの最も重要な設計判断です。

  • 同期にする理由: イベントが記録されなかったら「起きたこと」自体が消える。これはデータ消失。だからリクエスト処理のトランザクション内で確実に INSERT する。
  • 非同期にする理由: 統計更新やメール通知が失敗しても、ログイン自体は成功している。後でリトライすればいい。同期にすると通知サービスの障害で認証が止まる。
┌─ リクエスト処理スレッド(同期)────────────────────────────┐
│ │
│ [1] イベント発生 → [2] RDS INSERT → レスポンス返却 │
│ │
│ ここまでが同期。ユーザーはここで応答を受け取る。 │
│ INSERT が失敗したらリクエスト全体がエラーになる。 │
│ INSERT が成功したらイベントは確実に記録されている。 │
│ │
└────────────────────────────────────────────────────────────┘

│ ← この境界が重要
│ リクエスト処理の成否と後続処理は独立
│ 後続処理が全部落ちてもイベントは残っている
│ → 復旧後にイベントから再処理できる

┌─ 非同期処理 ──────────────────────────────────────────────┐
│ │
│ [3a] @Async スレッドプール │
│ フック実行(メール送信 450ms)が遅くても │
│ リクエスト処理には影響しない │
│ │
│ [3b] CDC(別プロセス) │
│ PeerDB が WAL を読んで ClickHouse に送信 │
│ アプリプロセスとは完全に独立 │
│ │
│ [3c] Kafka Consumer(別プロセス / 別Pod) │
│ 各 Consumer が独立して処理 │
│ 1つの Consumer が遅くても他に影響しない │
│ │
│ [4] 読み取りモデル構築 │
│ ClickHouse / OpenSearch へのINSERT │
│ バッチ集約で効率化 │
│ │
└────────────────────────────────────────────────────────────┘

障害時の影響範囲

各レイヤーが独立しているため、障害の影響が限定されます。

障害箇所認証処理統計ダッシュボードログ検索通知
RDS ダウン❌ 停止⚠️ 新データなし⚠️ 新データなし⚠️ 新データなし
CDC ツール ダウン✅ 影響なし⚠️ 同期遅延⚠️ 同期遅延✅ 影響なし
ClickHouse ダウン✅ 影響なし❌ 停止✅ 影響なし✅ 影響なし
OpenSearch ダウン✅ 影響なし✅ 影響なし❌ 停止✅ 影響なし
通知サービス ダウン✅ 影響なし✅ 影響なし✅ 影響なし❌ 停止

認証処理(コアビジネス)は RDS だけに依存。 分析・通知の障害で認証が止まることはない。


構成パターンの全体像

要件に応じて3段階の構成があります。

┌─────────────────────────────────────────────────────────────┐
│ │
│ Small: RDS + CDC + ClickHouse │
│ 秒間 数百〜数千イベント │
│ 月額 ~$200 │
│ │
│ Medium: RDS + MSK (Kafka) + ClickHouse + OpenSearch │
│ 秒間 数千〜数万イベント │
│ 月額 ~$1,000 │
│ │
│ Large: RDS + MSK + ClickHouse + OpenSearch + Kinesis │
│ 秒間 数万〜数十万イベント │
│ 月額 ~$3,000+ │
│ │
│ 問題が顕在化してから次の段階に進めばよい │
│ │
└─────────────────────────────────────────────────────────────┘

Small: RDS + CDC + ClickHouse

最小構成。CDC でイベントを ClickHouse に同期するだけ。

┌─ AWS ────────────────────────────────────────────────────────┐
│ │
│ ┌─ VPC ───────────────────────────────────────────────────┐│
│ │ ││
│ │ ┌─ EKS ──────────────────────────────────────────────┐││
│ │ │ │││
│ │ │ ┌───────────┐ ┌────────┐ ┌──────────────────┐ │││
│ │ │ │ App (Pod) │ │ PeerDB │ │ ClickHouse (Pod) │ │││
│ │ │ └─────┬─────┘ │ (Pod) │ │ + Keeper │ │││
│ │ │ │ └───┬────┘ └────────┬─────────┘ │││
│ │ │ │ │ │ │││
│ │ └────────┼─────────────┼─────────────────┼───────────┘││
│ │ │ │ │ ││
│ │ ▼ │ │ ││
│ │ ┌──────────────┐ │ │ ││
│ │ │ RDS │ WAL │ │ ││
│ │ │ PostgreSQL │──────┘ │ ││
│ │ └──────────────┘ ┌─────────────┘ ││
│ │ ▼ ││
│ │ ┌──────────────┐ ││
│ │ │ EBS gp3 │ ││
│ │ │ (100GB〜) │ ││
│ │ └──────────────┘ ││
│ └──────────────────────────────────────────────────────────┘│
│ │
└──────────────────────────────────────────────────────────────┘

データフロー:
App → RDS (INSERT) → WAL → PeerDB → ClickHouse

統計API / ダッシュボード
項目内容
スループット秒間 数百〜数千イベント
遅延数秒(CDC)
コストRDS ~$50 + EC2(CH) ~$60 + EC2(PeerDB) ~$15 + EBS $8 ≈ **$130/月**
運用PeerDB + ClickHouse の監視
適用中小規模、統計・分析の高速化が目的

Medium: RDS + MSK (Kafka) + ClickHouse + OpenSearch

Kafka を中心に据え、複数の読み取りモデルにイベントを配信。

┌─ AWS ────────────────────────────────────────────────────────┐
│ │
│ ┌─ VPC ───────────────────────────────────────────────────┐│
│ │ ││
│ │ ┌─ EKS ─────────────────────────────────────────┐ ││
│ │ │ │ ││
│ │ │ ┌───────────┐ ┌──────────────────┐ │ ││
│ │ │ │ App (Pod) │ │ ClickHouse (Pod) │ │ ││
│ │ │ └─────┬─────┘ └────────┬─────────┘ │ ││
│ │ │ │ │ │ ││
│ │ └────────┼───────────────────────┼──────────────┘ ││
│ │ │ ▲ ││
│ │ ▼ │ ││
│ │ ┌──────────────┐ ┌──────────┴───────────┐ ││
│ │ │ RDS │ │ Amazon MSK │ ││
│ │ │ PostgreSQL │────→│ (Managed Kafka) │ ││
│ │ └──────────────┘ CDC │ │ ││
│ │ (Debezium │ Topic: events │ ││
│ │ Connector) │ ├─ Partition 0 │ ││
│ │ │ ├─ Partition 1 │ ││
│ │ │ └─ Partition 2 │ ││
│ │ └──────────┬───────────┘ ││
│ │ │ ││
│ │ ┌─────────────┼─────────────┐ ││
│ │ ▼ ▼ ▼ ││
│ │ ┌────────────┐┌────────────┐┌──────────┐ ││
│ │ │ ClickHouse ││ OpenSearch ││ 通知 │ ││
│ │ │ (統計集計) ││ (ログ検索) ││ サービス │ ││
│ │ └────────────┘└────────────┘└──────────┘ ││
│ │ ││
│ └──────────────────────────────────────────────────────────┘│
│ │
└──────────────────────────────────────────────────────────────┘

データフロー:
App → RDS → CDC → MSK (Kafka) ──┬──→ ClickHouse → 統計
├──→ OpenSearch → ログ検索
└──→ 通知サービス → メール/Slack
項目内容
スループット秒間 数千〜数万イベント
遅延ミリ秒〜秒(Kafka)
コストRDS ~$100 + MSK ~$400 + EC2(CH) ~$120 + OpenSearch $200 ≈ **$800/月**
運用MSK はマネージド、ClickHouse + OpenSearch の監視
適用複数の読み取りモデル、マイクロサービス化

Amazon MSK(Managed Kafka)の特徴

┌──────────────────────────────────────────────────────┐
│ MSK を使う理由 │
│ │
│ ✅ Kafka クラスタの構築・運用が不要 │
│ ✅ ブローカーの自動パッチ、監視 │
│ ✅ EKS / Lambda から直接接続 │
│ ✅ MSK Connect で CDC コネクタをマネージド実行 │
│ │
│ MSK Connect + Debezium: │
│ ┌──────────┐ ┌─────────────────────┐ │
│ │ RDS │ CDC │ MSK Connect │ │
│ │PostgreSQL│ ──────→ │ (Debezium Connector)│ │
│ └──────────┘ │ → MSK Topic に配信 │ │
│ └─────────────────────┘ │
│ → CDC ツール (PeerDB) の自前運用も不要になる │
│ │
└──────────────────────────────────────────────────────┘

Large: フル構成

秒間数万〜数十万イベント。パーティション並列 + 優先度制御。

┌─ AWS ────────────────────────────────────────────────────────────┐
│ │
│ ┌─ VPC ───────────────────────────────────────────────────────┐│
│ │ ││
│ │ ┌─ EKS ──────────────────────────────────────────────┐ ││
│ │ │ App (Pod × N) │ ││
│ │ │ ├── 認証処理 │ ││
│ │ │ └── イベント publish │ ││
│ │ └────────┬────────────────────────────────────────────┘ ││
│ │ │ ││
│ │ ▼ ││
│ │ ┌──────────────┐ ││
│ │ │ RDS Multi-AZ │ ← 書き込み(Command 側) ││
│ │ │ PostgreSQL │ ││
│ │ └──────┬───────┘ ││
│ │ │ CDC ││
│ │ ▼ ││
│ │ ┌─────────────────────────────────────────────────────┐ ││
│ │ │ Amazon MSK │ ││
│ │ │ │ ││
│ │ │ ┌─ Topic: events-high-priority ──────────────────┐ │ ││
│ │ │ │ LoginFailed, AccountLocked │ │ ││
│ │ │ │ → 即時処理 │ │ ││
│ │ │ └────────────────────────────────────────────────┘ │ ││
│ │ │ ┌─ Topic: events-normal ─────────────────────────┐ │ ││
│ │ │ │ LoginSucceeded, TokenIssued │ │ ││
│ │ │ │ → バッチ処理 │ │ ││
│ │ │ │ Partitions: 12 (tenant_id で分散) │ │ ││
│ │ │ └────────────────────────────────────────────────┘ │ ││
│ │ │ ┌─ Topic: events-low-priority ──────────────────┐ │ ││
│ │ │ │ InspectTokenSuccess │ │ ││
│ │ │ │ → 余力がある時に処理 │ │ ││
│ │ │ └────────────────────────────────────────────────┘ │ ││
│ │ └───────────────┬──────────────┬──────────────┬───────┘ ││
│ │ │ │ │ ││
│ │ ▼ ▼ ▼ ││
│ │ ┌─ EKS Consumer Group ─────────────────────────────────┐ ││
│ │ │ │ ││
│ │ │ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │ ││
│ │ │ │ ClickHouse │ │ OpenSearch │ │ 通知 │ │ ││
│ │ │ │ Consumer × 4 │ │ Consumer × 2 │ │ Consumer │ │ ││
│ │ │ │ (バッチ集約) │ │ │ │ (即時) │ │ ││
│ │ │ └──────────────┘ └──────────────┘ └───────────┘ │ ││
│ │ └───────────────────────────────────────────────────────┘ ││
│ │ ││
│ │ ┌─ 監視 ─────────────────────────────────────────────┐ ││
│ │ │ CloudWatch: Consumer Lag, スループット │ ││
│ │ │ → Lag 増加時に Consumer Auto Scaling │ ││
│ │ └─────────────────────────────────────────────────────┘ ││
│ └──────────────────────────────────────────────────────────────┘│
│ │
│ ┌─ バックアップ / コールドストレージ ──────────────────────────┐│
│ │ S3: イベントの長期保存(Parquet 形式) ││
│ │ → Athena で過去データのアドホック分析 ││
│ │ → ClickHouse の S3 テーブルエンジンでも直接クエリ可 ││
│ └──────────────────────────────────────────────────────────────┘│
│ │
└──────────────────────────────────────────────────────────────────┘
項目内容
スループット秒間 数万〜数十万イベント
遅延高優先: ミリ秒 / 通常: 秒 / 低優先: 分
コストRDS ~$200 + MSK ~$800 + EKS ~$500 + OpenSearch ~$400 + S3 $50 ≈ **$2,000/月**
運用Consumer Lag 監視 + Auto Scaling

AWS サービスの選択ガイド

役割AWS サービス代替(セルフホスト)
イベントストア(書き込み)RDS (PostgreSQL)EC2 上の PostgreSQL
イベント伝搬(CDC)MSK Connect + DebeziumPeerDB (EKS Pod)
メッセージキューAmazon MSK (Kafka)Kafka (EKS)
統計・集計(OLAP)ClickHouse (EKS)ClickHouse Cloud
ログ検索Amazon OpenSearchElasticsearch (EKS)
通知Amazon SES / SNSメールサーバー
コールドストレージS3-
アドホック分析Athena (S3 上)-
監視CloudWatchPrometheus + Grafana

Amazon OpenSearch Service は Elasticsearch をベースとした AWS のマネージドサービスです。全文検索・ログ分析・ダッシュボード可視化が得意で、イベントデータの検索・フィルタリング用途に使います。詳細は AWS 大規模アーキテクチャ で触れています。

マネージド vs セルフホスト

┌──────────────────────────────────────────────────────┐
│ 判断基準 │
│ │
│ マネージド (MSK, OpenSearch, RDS): │
│ ├── 運用チームが小さい │
│ ├── 可用性・パッチ適用を任せたい │
│ └── コストよりも運用負荷を下げたい │
│ │
│ セルフホスト (EKS 上に構築): │
│ ├── コスト最小化 │
│ ├── 細かいチューニングが必要 │
│ └── Kubernetes 運用に慣れている │
│ │
│ ClickHouse は AWS マネージドがないため │
│ EKS 上にセルフホスト or ClickHouse Cloud │
│ │
└──────────────────────────────────────────────────────┘

どこがスケールするポイントか

Small → Medium → Large への移行判断は「どこが詰まったか」で決まります。

┌─────────────────────────────────────────────────────────────────────┐
│ データの流れとスケールポイント │
│ │
│ App │
│ │ ① 書き込み │
│ ▼ │
│ RDS (PostgreSQL) │
│ │ ② 伝搬 │
│ ▼ │
│ CDC / Kafka │
│ │ ③ 分配 │
│ ├──→ ClickHouse ④ 集計 │
│ ├──→ OpenSearch ⑤ 検索 │
│ └──→ 通知 ⑥ 配信 │
│ │
│ 番号が大きいほど下流。下流から先に詰まる。 │
└─────────────────────────────────────────────────────────────────────┘

各ポイントの症状と対策

#ポイント詰まったときの症状Small での対策Medium/Large での対策
書き込みINSERT レイテンシ増加、コネクション枯渇RDS インスタンスサイズアップRead Replica で読み取り分離
伝搬Consumer Lag 増加、同期遅延拡大PeerDB のリソース増強MSK (Kafka) に移行
分配1つの購読者の遅延が他に波及- (Small は1購読者)Kafka Topic 分割、優先度キュー
集計ClickHouse クエリ遅延、INSERT 詰まりノードスペック増強、EBS 拡張シャード追加、バッチサイズ調整
検索インデクシング遅延、検索レイテンシ増加- (Small は OpenSearch なし)ノード追加、シャード分割
配信通知遅延、SES スロットリングSES 送信レート引き上げ優先度キュー、非同期バッチ送信

詰まる順番(典型的なパターン)

┌─────────────────────────────────────────────────────────────┐
│ │
│ 最初に詰まる: ④ ClickHouse の INSERT │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 症状: バッチ INSERT が追いつかない │ │
│ │ 原因: 1件ずつ INSERT している or バッチサイズが小さい │ │
│ │ 対策: バッチサイズを 1万件以上に、flush 間隔を調整 │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ 解決 │
│ │
│ 次に詰まる: ② CDC の伝搬 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 症状: PeerDB の Consumer Lag が増加し続ける │ │
│ │ 原因: 単一プロセスで WAL を読んでいるため並列化できない│ │
│ │ 対策: MSK (Kafka) に移行、パーティション並列化 │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ 解決 │
│ │
│ その次に詰まる: ③ 購読者間の干渉 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 症状: 通知サービスの遅延が統計集計にも影響 │ │
│ │ 原因: 同じ Topic を全購読者が共有している │ │
│ │ 対策: Topic / Consumer Group 分割、優先度キュー │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ 解決 │
│ │
│ 最後に詰まる: ① RDS の書き込み │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 症状: INSERT レイテンシ増加(認証フロー全体に影響) │ │
│ │ 原因: 秒間数万 INSERT で IOPS 上限 │ │
│ │ 対策: RDS インスタンスサイズアップ、Provisioned IOPS │ │
│ │ または書き込みのシャーディング │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ① が詰まるのは相当な規模(秒間数万以上) │
│ ほとんどのシステムは ④ → ② の対策で十分 │
│ │
└─────────────────────────────────────────────────────────────┘

Small → Medium への移行判断

以下のいずれかに該当したら Medium を検討:

□ PeerDB の Consumer Lag が常時増加している
□ 読み取りモデルが2つ以上必要になった(統計 + 検索 等)
□ 通知送信の遅延が統計集計に影響している
□ イベントの優先度制御が必要になった

Medium → Large への移行判断

以下のいずれかに該当したら Large を検討:

□ Kafka パーティション数を増やしても Consumer が追いつかない
□ 秒間数万イベントを超えた
□ Consumer Lag の Auto Scaling が必要
□ S3 コールドストレージで長期保存が必要

段階的な構築

┌─────────────────────────────────────────────────────────────┐
│ │
│ Step 1: Small で始める │
│ ├── RDS + PeerDB + ClickHouse (全部 EKS 内) │
│ ├── ~$130/月 │
│ └── 統計APIの読み取り先を ClickHouse に切り替え │
│ │
│ Step 2: Kafka を追加(Medium) │
│ ├── 複数の購読者が必要になったら MSK を導入 │
│ ├── CDC を MSK Connect に移行 │
│ └── OpenSearch でログ検索を追加 │
│ │
│ Step 3: スケールアウト(Large) │
│ ├── パーティション + Consumer 並列化 │
│ ├── 優先度キュー分割 │
│ ├── Consumer Lag 監視 + Auto Scaling │
│ └── S3 コールドストレージ + Athena │
│ │
│ 「必要になってから拡張する」が鉄則 │
│ │
└─────────────────────────────────────────────────────────────┘

関連ドキュメント