はじめに
このシリーズは現実的な問題で終わります。電子商取引会社は、トランザクション フローを正常に実行し続けながら、4 時間で 1,000 万件のフラッシュ セール メールを送信する必要があります。この記事では、これまでのすべての要素を組み合わせて、運用可能な設計を作成します。
1. 問題と入力の仮定
ビジネス要件
- 最大 4 時間で 1,000 万件の電子メールを送信します。
- 名前、割引コード、ロケールによる基本的なパーソナライゼーションがあります。
- 購読解除、オープン追跡、クリック追跡があります。
- OTP および注文確認には影響しません。
容量目標
10,000,000 / 14,400 giây ≈ 694 emails/giây
Peak headroom x2 -> thiết kế cho ~1,400 emails/giây
運用アーキテクチャ
- 1 つの主要プロバイダー: Amazon SES。
- 1 つのバックアップ プロバイダー: SendGrid。
- 2 つの個別のワーカー プール: トランザクション マーケティングとバルク マーケティング。
- レート制限とスケジューリング用の 1 つの Redis クラスター。
- イベント駆動型バックボーンとしての 1 つの Kafka クラスター。
- メタデータと分析の取り込み用の 1 つの PostgreSQL プライマリ + リード レプリカ。
2. 全体的な運用アーキテクチャ
Admin UI / Campaign API
│
▼
Campaign Planner
│
├── Recipient Snapshot Service
├── Batch Planner
└── Kafka topics
│
▼
Bulk Worker Pool
│
┌───────┴────────┐
▼ ▼
Amazon SES SendGrid Fallback
│ │
└───────┬────────┘
▼
Webhook Ingestion
│
▼
Status Aggregator
│
▼
PostgreSQL + Grafana/Prometheus
主な成分
| 成分 | 役割 |
|---|---|
| キャンペーンプランナー | スナップショットとバッチ ジョブを作成する |
| カフカ | 生産者と消費者の分離 |
| レディス | リミッター、再試行スケジュール、分散ロック |
| バルクワーカー | キャンペーントラフィックをレンダリングして送信 |
| トランザクションワーカー | 重要なトラフィックを確保 |
| Webhook の取り込み | 配送/返送/苦情イベントを受け取る |
3. 推奨される Kubernetes インフラストラクチャ
ワークロードを名前空間とデプロイメントごとに分離する
apiVersion: apps/v1
kind: Deployment
metadata:
name: bulk-email-workers
spec:
replicas: 12
selector:
matchLabels:
app: bulk-email-workers
template:
metadata:
labels:
app: bulk-email-workers
spec:
containers:
- name: worker
image: ghcr.io/xdev/notification-workers:2026.04.01
env:
- name: WORKER_GROUP
value: bulk
- name: KAFKA_CONSUMER_GROUP
value: bulk-workers
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "2"
memory: "2Gi"
readinessProbe:
httpGet:
path: /health/ready
port: 8080
最初のサイズ提案
| サービス | 数量 | メモ |
|---|---|---|
| API / プランナー | 3 ポッド | 基本血圧 |
| バルクワーカー | 12~40ポッド | ラグによる自動スケール |
| トランザクションワーカー | 4~8ポッド | 予約容量 |
| Webhook プロセッサ | 3~6ポッド | コールバックバーストによるスケール |
| レディス | 3 ノード | センチネル/クラスター |
| カフカ | 3 ブローカー | レプリケーション係数 3 |
4. CI/CD とリリース戦略
パイプラインが存在するはずです
- テンプレート レンダリング、リミッター、プロバイダー アダプターの単体テスト。
- Kafka、Redis、PostgreSQL との統合テスト。
- スモーク テストはサンドボックス プロバイダー経由で電子メールを送信します。
- Canary を新しいワーカー バージョンにデプロイします。
- 送信失敗率が増加した場合は、すぐにロールバックします。
なぜ労働者にはカナリアが必要なのでしょうか?
レンダラーまたはプロバイダー アダプターの 1 つの小さなバグにより、1,000 万件の電子メールが 1,000 万件のエラーに変わる可能性があります。 Canary 1 ~ 5% のトラフィックは、キャンペーンが広範囲に影響を受ける前に回帰を検出するのに役立ちます。
5. k6 による負荷テスト
キャンペーン調整部分の負荷テストを行わずに運用トラフィックをテストすることは不可能です。
キャンペーン API の k6 の例
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
scenarios: {
create_campaigns: {
executor: 'ramping-vus',
startVUs: 1,
stages: [
{ duration: '1m', target: 20 },
{ duration: '3m', target: 100 },
{ duration: '2m', target: 0 },
],
},
},
};
export default function () {
const payload = JSON.stringify({
campaign_id: `camp-${__VU}-${__ITER}`,
template_id: 'flash_sale_v2',
segment_id: 'active_users_30d',
priority: 'normal',
});
const response = http.post('https://api.example.com/campaigns', payload, {
headers: { 'Content-Type': 'application/json' },
});
check(response, {
'campaign accepted': (r) => r.status === 202,
});
sleep(1);
}
API 以外に何をテストするか
- プロバイダーがスロットルするとキューのバックログが増加します。
- ラグが増加した場合のワーカーの自動スケーリング。
- Redis は、同時アクセスが多い場合の遅延を制限します。
- プロバイダーのフラッシュ イベント後の Webhook 取り込みバースト。
6. カオスシナリオをシミュレートする必要がある
| 状況 | 期待 |
|---|---|
| SES ペイ 429 は 15 分間続きます | 減速 + SendGrid への部分的なフェイルオーバー |
| Redis によりレイテンシが増加する | ワーカーは劣化しますが、大量に複製はされません。 |
| ワーカー ポッドの 20% が死亡 | バッチ リースが回収され、再開されます。 |
| Webhook プロセッサのダウンタイム | イベントはバッファリングされ、状態は失われません。 |
| キャンペーンのテンプレートのバグ | キャンペーンは一時停止されていますが、他のトラフィックはまだ安全です |
カオス テストの目標
システムが不滅であることを証明するためではなく、障害が発生したときに、制御され、観察可能で、回復可能な方法で障害が発生することを確認するためです。
7. 予備的なコスト分析
大きなコスト要素
| カテゴリー | 見積もり |
|---|---|
| Amazon SES は 1,000 万件のメールを送信 | 約1,000ドル |
| SendGrid フォールバック リザーブ | プランに応じて数百ドルから数千ドル |
| Kubernetes コンピューティング | クラウドと自動スケール ウィンドウに依存します |
| カフカ/Redis/PostgreSQL | 固定基本コスト |
| 可観測性 | Prometheus/Grafana 管理またはセルフホスト |
コストの最適化
- SES を大容量ワークロードのメインプロバイダーとして使用します。
- 災害シナリオに十分なレベルでのみフォールバック プロバイダーを有効にしてください。
- メインの PostgreSQL に負荷全体を負担させることなく、負荷の高い分析を非同期パイプラインに分離します。
- テンプレートのレンダリング キャッシュを最適化して、ワーカーの CPU を削減します。
8. 生産から学んだ教訓
正しい決断
- トランザクションワーカーとバルクワーカーを最初から分離します。
- メッセージを識別します
message_id冪等に対して安定。 - 大規模なキャンペーンを実行する前に、ダッシュボード キャンペーンの ETA と苦情率を作成します。
- ドメイン/IP を当初の予想よりも慎重にウォームアップします。
痛みはあるが貴重な教訓
- ワーカーの理論上のスループットは、プロバイダーを介した実際のスループットほど重要ではありません。
- 同じドメイン/IP を使用している場合、不適切なマーケティング キャンペーンによりトランザクション トラフィックの評判も損なわれる可能性があります。
- ジッターなしで再試行すると、すぐに自分自身による DDoS に変わります。
- どの電子メールが実際に配信されたかを知るには、Webhook 調整が必要です。
9. 1,000 万電子メールキャンペーンの稼働チェックリスト
- SPF、DKIM、DMARC ドメインが認証され、調整されています。
- セグメントがクリーンアップされ、抑制リストが適用されました。
- 設定されたプロバイダー/ドメイン/IP に応じたレート制限。
- ダッシュボード、アラート、ランブックが用意されています。
- フォールバックプロバイダーはテスト済みです。
- 小規模なカナリア キャンペーンは正常に実行されました。
- オンコールローテーションは、ページのしきい値とキャンペーンを一時停止する方法を正確に認識します。
概要
1,000 万件のメール送信の問題は、スケール ワーカーだけの問題ではありません。これは、イベント駆動型アーキテクチャ、配信可能性、レート制御、システム監視、および操作手順が同時に発生する問題です。これらのレイヤーを組み合わせて設計すると、大規模なキャンペーンはギャンブルではなく、予測可能で制御可能なワークロードになります。
大規模な電子メール通知プラットフォームを設計するための、高レベルの設計から運用展開まで、中核となるナレッジ チェーンを学習しました。