スケーリングを見据えた技術選定: モノリスからマイクロサービスへの段階的移行
はじめに
プロダクトの成長に伴い、システムのスケーリングは避けて通れない課題です。しかし、最初からマイクロサービスで設計すべきでもなく、モノリスで限界を迎えてから慌てるのも問題です。本記事では、段階的なスケーリング戦略と、各段階での技術選定の判断基準を解説します。
身近な例えで理解するスケーリング
スケーリングは「お店の拡大」に似ています。
- フェーズ1(個人経営) = モノリス。1人で全部やる。効率は良いが、キャパシティに限界がある
- フェーズ2(従業員を雇う) = 垂直スケーリング。より大きなサーバーを使う
- フェーズ3(支店を出す) = 水平スケーリング。同じ構成のサーバーを増やす
- フェーズ4(専門店に分割) = マイクロサービス化。機能ごとに独立したサービスに分割
- フェーズ5(フランチャイズ展開) = グローバル分散。世界中にサービスを展開
スケーリングの段階
段階一覧
| 段階 | ユーザー規模 | アーキテクチャ | 主な課題 |
|---|
| Stage 0 | 0-100 | シンプルモノリス | 開発速度を最大化 |
| Stage 1 | 100-10,000 | モノリス + キャッシュ | DB負荷の軽減 |
| Stage 2 | 10,000-100,000 | モノリス + Read Replica + CDN | 読み取り性能の向上 |
| Stage 3 | 100,000-1,000,000 | モジュラーモノリス / サービス分離開始 | 開発チームの拡大 |
| Stage 4 | 1,000,000-10,000,000 | マイクロサービス | システム全体の複雑性管理 |
| Stage 5 | 10,000,000+ | グローバル分散マイクロサービス | グローバル展開、データ整合性 |
Stage 0: シンプルモノリス(0-100ユーザー)
アーキテクチャ
[ユーザー]
|
v
[Next.js / Rails / Django]
|
v
[PostgreSQL]
|
v
[S3 (ファイル保存)]
技術選定のポイント
| 項目 | 推奨 | 理由 |
|---|
| フレームワーク | Next.js, Rails, Django | フルスタックで開発が速い |
| データベース | PostgreSQL | 万能で将来のスケーリングにも対応 |
| ホスティング | Vercel, Heroku, Railway | ゼロ設定デプロイ |
| ストレージ | S3互換 (Supabase Storage等) | スケーラブル |
この段階でやるべきこと
- コア機能の開発に集中する
- ユーザーフィードバックを集める
- パフォーマンスモニタリングの基盤を作る(Sentry、PostHog等)
この段階でやるべきでないこと
- マイクロサービス化
- 複数DBの導入
- Kubernetes
- カスタムインフラの構築
Stage 1: モノリス + キャッシュ(100-10,000ユーザー)
アーキテクチャ
[ユーザー]
|
v
[Next.js / Rails / Django]
|
+-- [Redis (キャッシュ)]
| |-- セッション管理
| |-- 頻繁にアクセスされるデータのキャッシュ
| +-- レートリミット
|
v
[PostgreSQL]
|
v
[S3 + CloudFront (CDN)]
キャッシュ戦略
| キャッシュ対象 | 方法 | TTL |
|---|
| ユーザーセッション | Redis Strings | 24時間 |
| 商品一覧/記事一覧 | Redis Strings (JSON) | 5分 |
| ユーザープロフィール | Redis Hashes | 1時間 |
| API レスポンス | Redis Strings | 1-5分 |
| 静的アセット | CDN (CloudFront) | 1日 |
キャッシュ導入の効果
[キャッシュなし]
DBクエリ: 1,000回/秒
レスポンスタイム: 200ms
[キャッシュあり(ヒット率80%)]
DBクエリ: 200回/秒(80%削減)
レスポンスタイム: 20ms(90%短縮)
この段階での判断基準
| 指標 | 閾値 | 対策 |
|---|
| DBクエリ数 | 500回/秒以上 | Redisキャッシュ導入 |
| レスポンスタイム | 200ms以上 | クエリ最適化 + キャッシュ |
| 静的アセット配信 | ページロード3秒以上 | CDN導入 |
Stage 2: Read Replica + CDN(10,000-100,000ユーザー)
アーキテクチャ
[ユーザー]
|
v
[CloudFront (CDN)]
|
v
[ALB (ロードバランサー)]
|
+--------+--------+
| | |
[App 1] [App 2] [App 3] (水平スケーリング)
| | |
+--------+--------+
|
+--------+--------+
| |
[Redis [PostgreSQL
Cluster] Primary]
|
+-------+-------+
| |
[Read Replica 1] [Read Replica 2]
読み書き分離
| 操作 | 接続先 | 例 |
|---|
| 書き込み(INSERT/UPDATE/DELETE) | Primary | ユーザー登録、投稿作成 |
| 読み取り(SELECT) | Read Replica | 一覧表示、検索 |
注意: レプリケーション遅延
Read Replicaにはミリ秒〜数秒の遅延がある。ユーザーが投稿した直後に「自分の投稿が見えない」という問題が発生し得る。対策として、自分自身のデータはPrimaryから読み取る(Read Your Own Writes)パターンを採用する。
水平スケーリングの条件
アプリケーションを水平スケーリングするには、アプリケーションがステートレスである必要がある。
| 条件 | 対策 |
|---|
| セッションをサーバーに保存しない | Redis等の外部ストアを使う |
| ファイルをローカルに保存しない | S3等の外部ストレージを使う |
| サーバー間で共有が必要なデータ | Redis、DBを使う |
Stage 3: モジュラーモノリス / サービス分離開始(100,000-1,000,000ユーザー)
アーキテクチャ
[ユーザー]
|
v
[CloudFront + WAF]
|
v
[ALB]
|
+-- [API サーバー群 (モジュラーモノリス)]
| |
| +-- [ユーザーモジュール]
| +-- [商品モジュール]
| +-- [注文モジュール]
| +-- [通知モジュール] --> 分離候補
| +-- [検索モジュール] --> 分離候補
|
+-- [非同期処理ワーカー]
| |
| +-- [SQS] --> [Lambda / ECS Task]
|
v
[データストア]
+-- [Aurora PostgreSQL (Primary + Replica x 2)]
+-- [ElastiCache Redis Cluster]
+-- [OpenSearch (検索)]
+-- [S3 + CloudFront]
モジュラーモノリスとは
モジュラーモノリスは、単一のアプリケーション内で機能をモジュールに分割し、モジュール間の依存関係を厳密に管理するアーキテクチャ。
[モノリス vs モジュラーモノリス vs マイクロサービス]
モノリス:
+---------------------------+
| すべてが密結合 |
| ユーザー←→商品←→注文←→通知 |
+---------------------------+
モジュラーモノリス:
+---------------------------+
| [ユーザー] | [商品] |
| ↓ API | ↓ API |
| [注文] | [通知] |
| モジュール間はAPIで通信 |
+---------------------------+
同一プロセス内だが、モジュール間の境界が明確
マイクロサービス:
[ユーザー] [商品] [注文] [通知]
各サービスが独立したプロセス/コンテナ
サービス分離の判断基準
| 基準 | 分離すべきサイン |
|---|
| スケーリング要件の違い | 検索サービスだけCPU負荷が高い |
| デプロイ頻度の違い | 通知サービスは頻繁に変更、決済は安定 |
| チームの境界 | 別チームが開発/運用する |
| 技術スタックの違い | Python (ML)で書きたいサービスがある |
| 障害の分離 | 通知の障害が本体に影響してはいけない |
最初に分離する候補
| サービス | 分離理由 |
|---|
| 通知サービス | 非同期処理、障害分離 |
| 画像処理サービス | CPU負荷が高い、非同期 |
| 検索サービス | 別の技術(OpenSearch)を使用 |
| バッチ処理 | 長時間実行、リソース消費が大きい |
Stage 4: マイクロサービス(1,000,000-10,000,000ユーザー)
アーキテクチャ
[ユーザー]
|
v
[CloudFront + WAF]
|
v
[API Gateway / BFF]
|
+-- [ユーザーサービス] --> [Aurora (Users DB)]
|
+-- [商品サービス] --> [Aurora (Products DB)]
|
+-- [注文サービス] --> [Aurora (Orders DB)]
| |
| +-- [SQS] --> [在庫サービス]
|
+-- [検索サービス] --> [OpenSearch]
|
+-- [通知サービス] --> [SQS] --> [SES/SNS]
|
+-- [分析サービス] --> [Redshift/BigQuery]
|
v
[共通基盤]
+-- [Redis Cluster (キャッシュ)]
+-- [Kafka (イベントストリーミング)]
+-- [Jaeger (分散トレーシング)]
+-- [Prometheus + Grafana (監視)]
マイクロサービスで必要になるインフラ
| インフラ | 用途 | ツール例 |
|---|
| サービスディスカバリ | サービス間の位置解決 | AWS Cloud Map, Consul |
| API Gateway | リクエストルーティング | AWS API Gateway, Kong |
| メッセージキュー | 非同期通信 | SQS, Kafka |
| 分散トレーシング | リクエストの追跡 | AWS X-Ray, Jaeger |
| サービスメッシュ | サービス間通信の管理 | AWS App Mesh, Istio |
| 設定管理 | 環境設定の一元管理 | AWS Systems Manager |
| シークレット管理 | 秘匿情報の管理 | Secrets Manager, Vault |
データベースの分割(Database per Service)
| パターン | 説明 | トレードオフ |
|---|
| 共有DB | 全サービスが同一DBを使用 | 簡単だがサービス間の結合が強い |
| DB per Service | サービスごとに独立したDB | 独立性が高いが、クロスサービスクエリが困難 |
| Saga パターン | 分散トランザクション | 複雑だが整合性を保てる |
| CQRS | 書き込みと読み取りを分離 | 高パフォーマンスだが複雑 |
Stage 5: グローバル分散(10,000,000+ユーザー)
アーキテクチャ
[グローバルユーザー]
|
v
[Route 53 (Geo DNS)]
|
+-- [東京リージョン]
| +-- [CloudFront]
| +-- [ECS/EKS クラスター]
| +-- [Aurora Global Database (Primary)]
| +-- [ElastiCache]
|
+-- [北米リージョン (us-east-1)]
| +-- [CloudFront]
| +-- [ECS/EKS クラスター]
| +-- [Aurora Global Database (Replica)]
| +-- [ElastiCache]
|
+-- [欧州リージョン (eu-west-1)]
+-- [CloudFront]
+-- [ECS/EKS クラスター]
+-- [Aurora Global Database (Replica)]
+-- [ElastiCache]
グローバル分散の課題
| 課題 | 対策 |
|---|
| データの整合性 | 結果整合性を受け入れる設計 |
| レプリケーション遅延 | 最大1秒程度の遅延を前提に |
| データのローカライゼーション | GDPR等の規制に対応したデータ配置 |
| 障害のリージョン間波及 | リージョン間の依存を最小限に |
| コスト | クロスリージョンデータ転送費用 |
データベースシャーディング
シャーディングとは
データベースのデータを複数のDB(シャード)に分散して格納する技術。1つのDBでは処理しきれないデータ量/リクエスト量に対応する。
シャーディング戦略
| 戦略 | 説明 | メリット | デメリット |
|---|
| ハッシュベース | キーのハッシュ値でシャードを決定 | 均等な分散 | 範囲クエリが困難 |
| レンジベース | キーの範囲でシャードを決定 | 範囲クエリが容易 | ホットスポットのリスク |
| ジオベース | 地域でシャードを決定 | 低レイテンシ | 地域間のデータ共有が困難 |
| テナントベース | テナント(顧客)ごとにシャード | テナント分離が完全 | 大テナントの偏り |
シャーディング導入のタイミング
[シャーディング導入の判断]
以下の全てを試した後に検討する:
1. クエリの最適化(インデックス、クエリ改善)
2. Read Replicaの追加
3. キャッシュの強化
4. 接続プーリングの最適化
5. パーティショニング(テーブル分割)
それでもDBがボトルネックなら → シャーディング検討
CDNとキャッシュ戦略
CDN活用のレベル
| レベル | キャッシュ対象 | 効果 |
|---|
| Level 1 | 静的ファイル(CSS、JS、画像) | ページロード高速化 |
| Level 2 | API レスポンス(短いTTL) | サーバー負荷軽減 |
| Level 3 | HTMLページ(ISR/SSG) | オリジンサーバーへのリクエスト大幅削減 |
| Level 4 | Edge Computing | ユーザーに最も近い場所で処理 |
キャッシュ戦略の全体像
[リクエストの流れ]
[ブラウザキャッシュ] (TTL: 5分)
↓ miss
[CDN (CloudFront)] (TTL: 1-5分)
↓ miss
[アプリケーションキャッシュ (Redis)] (TTL: 5-60分)
↓ miss
[データベース]
各段階でのコスト概算
| Stage | ユーザー規模 | 月額インフラコスト | チーム規模 |
|---|
| Stage 0 | 0-100 | $0-20 | 1-3人 |
| Stage 1 | 100-10,000 | $50-200 | 3-5人 |
| Stage 2 | 10,000-100,000 | $300-1,000 | 5-10人 |
| Stage 3 | 100,000-1,000,000 | $2,000-10,000 | 10-30人 |
| Stage 4 | 1,000,000-10,000,000 | $10,000-100,000 | 30-100人 |
| Stage 5 | 10,000,000+ | $100,000+ | 100人+ |
スケーリングのアンチパターン
| アンチパターン | 問題 | 対策 |
|---|
| 早すぎる最適化 | ユーザーが100人なのにシャーディング | ユーザーが増えてから対応 |
| 早すぎるマイクロサービス化 | チーム3人でマイクロサービス | まずモノリスで始める |
| 垂直スケーリングへの依存 | サーバーをどんどん大きくする | 水平スケーリングを前提に設計 |
| キャッシュの乱用 | 全てをキャッシュで解決しようとする | 根本原因(遅いクエリ等)を修正 |
| 技術的負債の放置 | スケーリング時に一気にツケが来る | 段階的に負債を返済 |
判断フローチャート
[スケーリング判断]
|
v
[現在のボトルネックは?]
|
+-- DBの読み取り
| → Redis キャッシュ、Read Replica
|
+-- DBの書き込み
| → 非同期処理(SQS)、最終的にはシャーディング
|
+-- アプリサーバーのCPU/メモリ
| → 水平スケーリング(インスタンス追加)
|
+-- 静的コンテンツの配信
| → CDN(CloudFront)
|
+-- 開発チームの生産性
| → モジュラーモノリス、サービス分離
|
+-- グローバルなレイテンシ
→ マルチリージョン展開
まとめ
スケーリングの鉄則
| 鉄則 | 詳細 |
|---|
| 早すぎない | ユーザーが少ないうちは最適化しない |
| 測定してから最適化 | 推測ではなくデータに基づく |
| 段階的に進める | Big Bangの移行は避ける |
| シンプルさを保つ | 複雑さはスケーリングの敵 |
| 自動化する | スケーリングは自動化前提で設計 |
各段階の推奨アクション
| 段階 | 最初にやること |
|---|
| Stage 0→1 | Redis導入、CDN導入 |
| Stage 1→2 | Read Replica、水平スケーリング |
| Stage 2→3 | モジュラーモノリス化、非同期処理 |
| Stage 3→4 | ボトルネックとなるサービスから分離 |
| Stage 4→5 | マルチリージョン展開 |
大半のプロダクトはStage 2-3で十分。Stage 4以上が必要になるのは、グローバル展開する大規模サービスのみ。過度なスケーリング設計は避け、プロダクトの成長に合わせて段階的に進化させることが最も効率的なアプローチである。
参考リンク