負荷分散とゲートウェイ
🎯 核心問題
1台のサーバーで負荷に耐えられなくなった時、どのようにトラフィックを「賢く」複数のサーバーインスタンスに分散させるか? 負荷分散は現代の分散システムにおける「交通整理係」です。本記事では、実際の事例(タピオカ店のレジ、宅配便の仕分け、交通整理)を通じて、負荷分散の設計思想とエンジニアリング実践を深く理解します。
1. なぜ「負荷分散」が必要なのか?
1.1 ある実例から考える:某Webサイトのアーキテクチャ進化
某スタートアップ企業は、ユーザー数が急増する中で深刻なパフォーマンス問題に直面しました:
シナリオ再現:
フェーズ1:シングルサーバー
ユーザー → サーバー (1コア2GB)
↓
DAU 1000 → ピーク時:1000人が同時アクセス
↓
問題:CPU 100%、応答遅延、頻繁なダウン⚠️ シングルサーバーの致命的な問題
- パフォーマンスのボトルネック: CPU 100%、応答時間 > 5秒
- 単一障害点: サーバーがダウンすると、サイト全体が利用不可に
- スケールアップの困難さ: 垂直スケーリング(CPU・メモリ増設)しかできず、高コストで限界がある
改善後のアーキテクチャ(負荷分散の導入):
フェーズ2:複数サーバー + 負荷分散
ユーザー → ロードバランサー (Nginx)
↓
├→ サーバー1 (1コア2GB)
├→ サーバー2 (1コア2GB)
└→ サーバー3 (1コア2GB)✨ 改善後の効果
- パフォーマンス向上: 3台のサーバーで並列処理、応答時間 < 1秒
- 高可用性: 1台のサーバーがダウンしても、他のサーバーがサービスを継続
- 水平スケーリング: より多くの性能が必要?サーバーを追加するだけ
1.2 負荷分散を日常に例えると
タピオカ店のレジ
あなたが人気タピオカ店を開いたと想像してください:
- レジ1台: お客様が列を作り、後ろの人は待ちきれず、悪評が広がる
- レジ3台: スタッフがお客様を各レジに振り分け、効率が3倍に向上
ロードバランサーは「レジの振り分け係」:
- ユーザー(お客様) → サービスをリクエスト
- ロードバランサー(振り分け係) → リクエストを各サーバーに分配
- サーバー(レジ) → リクエストを処理
基于传输层信息(IP地址+端口)进行流量分发。不关心应用层内容,只做"快递分拣",因此性能极高。
- 需要极高吞吐量的场景
- TCP/UDP流量分发
- 不需要内容识别的场景
- 微服务间通信
2. 負荷分散とは?
2.1 L4負荷分散(L4):宛先だけを見る
トランスポート層(TCP/UDP)で動作し、宅配業者があなたの住所(IPアドレス + ポート番号)だけを見て、中身が何かは気にしないのと同じです。
特徴:
- 超高速: シンプルなアドレス転送のみで、パケットの中身を解析しない
- 適用シーン: データベース接続、Redisキャッシュ、常時接続ゲームサーバー
- 代表製品: LVS(Linux Virtual Server)、AWS NLB、Azure Load Balancer
動作原理
クライアントリクエスト → L4ロードバランサー → バックエンドサーバー
↓
IP + Portのみ確認
↓
高速転送(中身は解析しない)2.2 L7負荷分散(L7):荷物の中身を確認する
アプリケーション層(HTTP/HTTPS)で動作し、宅配業者が住所だけでなく、荷物を開けて中身を確認し、中身に応じて配送方法を決めるようなものです。
特徴:
- インテリジェントルーティング: URLパス、HTTPヘッダー、Cookieなどに基づくきめ細かなルーティング
- 高度な機能: SSL終端、コンテンツキャッシュ、圧縮、WAFセキュリティ
- 適用シーン: Webアプリケーション、APIゲートウェイ、マイクロサービスアーキテクチャ
- 代表製品: Nginx、HAProxy、AWS ALB、Envoy
動作原理
クライアントリクエスト → L7ロードバランサー → HTTPコンテンツを解析
↓
URL、Header、Cookieを確認
↓
特定のサーバーへインテリジェントルーティング2.3 L4 vs L7 比較一覧
| 観点 | L4負荷分散(L4) | L7負荷分散(L7) |
|---|---|---|
| 動作階層 | トランスポート層(TCP/UDP) | アプリケーション層(HTTP/HTTPS) |
| 判断基準 | IPアドレス + ポート番号 | URL、Header、Cookie、Body |
| 処理速度 | 極めて高速(カーネル空間処理) | 高速(ユーザー空間での解析) |
| 機能の豊富さ | 基本的な転送 | SSL終端、キャッシュ、圧縮、WAF |
| 典型的な用途 | DB、ゲーム、持続的接続 | Webアプリ、APIゲートウェイ、マイクロサービス |
| 代表製品 | LVS、AWS NLB | Nginx、HAProxy、AWS ALB |
3. 核心問題①:「故障した」サーバーにリクエストを振り続けないようにするには?
3.1 ヘルスチェック:「病気」のサーバーがシステム全体を巻き込まないように
想像してみてください。あなたのレジの1台が突然故障したのに、振り分け係がそれに気づかず、お客様をどんどんそのレジに案内し続けています。その結果、列はどんどん長くなり、お客様の不満が爆発します。
ヘルスチェック(Health Check)は、このような事態を防ぐ「見張り役」です。定期的に各サーバーを「健診」し、「病気」を発見したら直ちにキューから外し、「回復」したら再び呼び戻します。
3.2 アクティブヘルスチェック vs パッシブヘルスチェック
アクティブヘルスチェック(Active Health Check): ロードバランサーが自ら「ノック」してサーバーに「まだ生きてる?」と尋ねる
- 定期的にプローブリクエストを送信(HTTP /health、TCP ping など)
- 応答タイムアウトまたはエラーコードが返った場合、不健康と判断
- メリット: 検出結果が正確で信頼性が高い
- デメリット: 追加のプローブトラフィックが発生する
パッシブヘルスチェック(Passive Health Check): ロードバランサーが実際のビジネストラフィックの応答状況を「観察」する
- 実際のリクエストの応答時間やエラー率を統計的に監視
- 連続して複数回失敗した場合、不健康と判断
- メリット: 追加トラフィックが発生しない
- デメリット: 判定には十分なトラフィックサンプルが必要
閾値設定表
| 指標 | 健康閾値 | 不健康閾値 | 説明 |
|---|---|---|---|
| HTTPステータスコード | 200-399 | 400+ またはタイムアウト | 4xx/5xxはすべて失敗と判断 |
| TCP接続 | 接続確立成功 | 接続タイムアウト | ポートの到達可能性をチェック |
| 応答時間 | < 500ms | > 2000ms | タイムアウトは通常2〜5秒に設定 |
| 連続失敗回数 | - | 3回 | 単発のブレによる誤判定を防ぐ |
| チェック間隔 | - | 5秒 | 頻度が高すぎると負荷が増加 |
💡 ありがちな落とし穴:閾値が「敏感すぎる」場合
某チームはヘルスチェックの応答時間閾値を100msに設定しましたが、アプリケーションの平均応答時間は80〜120msの間で変動していました。その結果、サーバーが頻繁に「不健康」とマークされ、トラフィックが健康と不健康の間を行ったり来たりし、システム全体の可用性がかえって低下しました。
正しいアプローチ: 閾値はP99応答時間の2〜3倍に設定し、正常な変動に十分なバッファを持たせるべきです。
4. 核心問題②:「常連客」が毎回同じ「レジ係」に接続できるようにするには?
4.1 セッション維持:「常連客」が毎回同じ「レジ係」に接続できるように
あなたがタピオカ店の常連だと想像してください。毎回同じ店員が接客してくれます。彼女はあなたの好み(甘さ控えめ、氷抜き)を知っているので、素早く気の利いたサービスが受けられます。しかし、毎回来るたびに新人が担当すると、同じ要望を何度も繰り返さなければならず、効率が大幅に下がります。
セッション維持(Session Persistence / Sticky Session) は、この問題を解決する手法です:同じユーザーのリクエストが常に同じバックエンドサーバーにルーティングされるようにします。
4.2 3つのセッション維持メカニズムの比較
| メカニズム | 実装原理 | メリット | デメリット | 適用シーン |
|---|---|---|---|---|
| Cookie挿入 | LBがレスポンスにCookieを挿入し、後続リクエストでそのCookieを送信 | IP変更の影響を受けず、初回リクエストから維持可能 | クライアントがCookieをサポートしている必要あり、無効化される可能性あり | ECショッピングカート、ログイン状態維持 |
| IPハッシュ | クライアントIPをハッシュ計算し、特定のサーバーにマッピング | クライアント側の対応不要、ステートレス | IP変更でセッション喪失、均等分散が困難 | Cookie非対応環境、WebSocket |
| スティッキーセッションテーブル | LBがセッションとサーバーのマッピングテーブルを維持 | セッションレプリケーションとフェイルオーバー対応 | LBのメモリ消費、追加の同期が必要 | 高可用性が厳しく求められるシーン |
💡 利用の推奨
- Cookie挿入: 優先推奨、互換性が高い
- IPハッシュ: WebSocketなどの特殊なシーンでのみ使用
- スティッキーセッションテーブル: Cookieと組み合わせてフェイルオーバー機能を提供
5. 核心問題③:ゼロダウンタイムデプロイを実現するには?
5.1 Blue-Greenデプロイメント:「ワンクリック切替」のゼロダウンタイムリリース
核心思想: 全く同じ本番環境を2セット(Blue環境とGreen環境)同時に維持し、そのうち1セットだけが外部にサービスを提供します。
- 零停机时间:流量切换在毫秒级完成,用户无感知
- 快速回滚:发现问题可立即切回原环境,风险可控
- 完整的预发布测试:新环境可完整测试后再接管流量
- 数据一致性:无需处理新旧版本同时运行时的兼容问题
- 资源成本高:需要同时维护两套完整环境,服务器成本翻倍
- 数据库兼容性挑战:如果涉及数据库Schema变更,需要特别处理兼容性
- 预热问题:新环境启动后可能需要时间预热缓存、连接池等
- 不适合有状态服务:对于长连接、会话保持要求高的场景处理复杂
ワークフロー:
- 初期状態: Blue環境でv1.0が稼働中(本番)、Green環境は待機。
- 新バージョンデプロイ: Green環境にv1.1をデプロイし、内部スモークテストを実施。
- トラフィック切替: ロードバランサーの向き先をGreen環境に切り替え、トラフィックが瞬時にv1.1へ。
- 監視・観察: Green環境の稼働状態を観察し、異常がないことを確認。
- 旧バージョン保持: Blue環境はv1.0のまま一定期間(例:24時間)保持し、迅速なロールバックの保険とする。
✨ メリット・デメリット分析
| メリット | デメリット |
|---|---|
| ✅ ゼロダウンタイム、切替はミリ秒単位で完了 | ❌ リソースコストが高い、2セットの環境を同時に維持する必要あり |
| ✅ 迅速なロールバック、問題発見時に即座に旧環境へ切戻し | ❌ データベーススキーマ変更時に互換性の特別な対応が必要 |
| ✅ 新環境で完全なテストを行ってからトラフィックを引き継げる | ❌ ステートフルなサービス(WebSocket持続的接続など)には不向き |
5.2 カナリアリリース:「小さく始めて徐々に広げる」グレースフルデプロイ戦略
カナリアリリースの名前は、歴史的な「炭鉱のカナリア」に由来します——炭鉱夫はカナリアを連れて坑道に入り、カナリアに異常が現れたら有毒ガスが漏れていると判断し、すぐに避難しました。ソフトウェアリリースにおいて、カナリアリリースとは、まずごく一部のユーザーに新バージョンを試してもらい、問題がないことを確認してから徐々に範囲を拡大する手法です。
- 1% → 5% → 10% → 25% → 50% → 100%
- 每个阶段观察至少15-30分钟
- 关键指标:错误率、延迟、吞吐量
- 内部员工/测试用户先行
- 按地域:选择特定区域用户
- 按用户属性:VIP用户或普通用户
- 按设备类型:iOS/Android/Web
- 错误率超过阈值自动回滚
- P99延迟异常触发告警
- 关键业务指标下降自动回滚
- 一键回滚:30秒内恢复旧版本
- 基础设施:CPU、内存、磁盘、网络
- 应用指标:QPS、错误率、延迟分布
- 业务指标:转化率、订单量、收入
- 用户体验:页面加载时间、交互延迟
核心思想:
- 少量トラフィックから開始: まず1%のトラフィックを新バージョンサーバーに振り向ける。
- 指標の観察: エラー率、レイテンシ、ビジネス重要指標を継続的に監視。
- 段階的な拡大: すべて正常であれば、割合を5%、10%、25%、50%、100%と段階的に引き上げる。
- 迅速なロールバック: 異常を検知したら、即座に全トラフィックを旧バージョンに戻す。
💡 カナリアリリースの優位性
| 優位性 | 説明 |
|---|---|
| 🎯 リスクコントロール | 新バージョンに重大なバグがあっても、影響はごく一部のユーザーのみ |
| 📊 実環境での検証 | 実際の本番環境で検証するため、テスト環境よりも信頼性が高い |
| 🚀 高速イテレーション | チームはより自信を持って頻繁に新機能をリリース可能 |
| 💰 リソース効率 | Blue-Greenデプロイのように2セットの完全な環境を用意する必要なし |
6. 核心問題④:システム自身が「呼吸」できるようにするには?
6.1 オートスケーリング:システムをレストランのように「柔軟にシフト調整」
あなたがレストランを経営していると想像してください:
- ランチピーク時: 10人のスタッフが必要だが、午後3時の閑散期には2人で十分
- ずっと10人を維持すると: 人件費が爆発
- ずっと2人だけだと: ピーク時にお客様が待ちきれず、全員離れてしまう
オートスケーリング(Auto Scaling) は、システムをレストランのように「柔軟にシフト調整」させるものです——忙しい時は自動でサーバーを追加し、暇な時は自動でサーバーを削減します。
6.2 スケーリング指標の選択
オートスケーリングの核心は、「いつサーバーを増やすべきか?いつサーバーを減らすべきか?」という問いに答えることです。
一般的な判断指標:
| 指標 | スケールアウト閾値 | スケールイン閾値 | 適用シーン |
|---|---|---|---|
| CPU使用率 | > 70% | < 30% | コンピュート集約型アプリ |
| メモリ使用率 | > 75% | < 40% | メモリ集約型アプリ |
| QPS(秒間リクエスト数) | > 1000/s | < 400/s | APIゲートウェイ、Webサービス |
| 接続数 | > 5000 | < 1000 | DB、メッセージキュー |
| カスタムビジネス指標 | ビジネス次第 | ビジネス次第 | 特定のビジネスシナリオ |
💡 スケーリング戦略の「落とし穴」と「解決策」
落とし穴1:スケールアウトの反応が遅すぎて、トラフィックの急増でシステムがダウン
某ECサイトのセール期間中、CPU > 80%でスケールアウトをトリガーする設定にしていましたが、監視データの収集に1分の遅延があり、新インスタンスの起動に3分かかりました。その結果、トラフィックが急増するスピードにスケールアウトが追いつかず、サーバーがダウンしました。
解決策:
- 事前スケールアウト: 過去データに基づいてトラフィックピークを予測し、30分前からスケールアウトを開始
- 多段階閾値: 60%で警告(新インスタンスのウォームアップ開始)、70%で正式スケールアウト、80%で緊急スケールアウト
- 高速スケールアウト: コンテナ化デプロイを使用し、新インスタンスを30秒以内に起動(VMの3〜5分と比較して)
落とし穴2:スケールアウトが過激すぎて、コストが爆発
某スタートアップは過激な自動スケールアウト戦略を設定しました:CPU > 50%でスケールアウト。その結果、通常のビジネス変動でスケールアウトがトリガーされ、サーバー数が5台から30台に膨れ上がり、月末のクラウド請求書を見てCTOが泣きました。
解決策:
- スケールアウトのクールダウン時間を設定: 1回のスケールアウト後、少なくとも5分間は次のスケールアウトを不可に
- 最大インスタンス数を設定: max = 現在のインスタンス数 × 2、無限の膨張を防止
- スパイクとトレンドを区別: 連続3周期すべてが閾値を超えた場合のみスケールアウト、単発スパイクによるトリガーを防止
落とし穴3:スケールインが速すぎて、スケールアウトしたばかりのサーバーがすぐに削除される
某チームはCPU < 30%でスケールインする設定にしていました。スケールアウト後、トラフィックがまだ処理中でCPUが一時的に25%に低下し、スケールインがトリガーされました。スケールインが完了した直後にCPUが80%に跳ね上がり、再びスケールアウトがトリガー——システムは「スケールアウト→スケールイン→スケールアウト」の激しい振動に陥りました。
解決策:
- スケールインはより保守的に: スケールアウト閾値70%、スケールイン閾値25%、中間に十分なバッファゾーンを確保
- スケールインのクールダウン時間をより長く: スケールアウト後、少なくとも10分間はスケールイン不可に
- 段階的スケールイン: 一度に1台のみ削除し、様子を見てから継続するか判断
7. 実践:ロードバランサーをどう選ぶか?
7.1 主要ロードバランサーの比較
| 特性 | Nginx | HAProxy | Envoy | クラウドベンダーLB |
|---|---|---|---|---|
| ポジショニング | 高性能リバースプロキシ/LB | オープンソースLB | クラウドネイティブプロキシ | マネージドLB |
| パフォーマンス | 極めて高い(C言語、イベント駆動) | 高い(イベント駆動) | 高い(C++/Rust) | 非常に高い |
| 機能の豊富さ | 基本LB、静的ファイル、キャッシュ | 豊富なLBアルゴリズム | 高度なルーティング、可観測性 | 機能全面 |
| 設定 | 設定ファイル(nginx.conf) | 設定ファイル(haproxy.cfg) | API/設定ファイル | UIコンソール |
| 拡張 | Cモジュール/Luaスクリプト | Luaスクリプト | WASM/Filter | プラグイン |
| 適用シーン | 静的リソース、L7負荷分散、SSL終端 | L7負荷分散、高可用性 | サービスメッシュ、マルチクラウド | クイックスタート |
💡 選定のアドバイス
決定木:
ロードバランサーの選択:
│
├─ 基本的なL4負荷分散だけで十分?
│ ├─ はい → LVS(OSS/無料)または クラウドベンダーNLB
│ └─ いいえ → 次へ
│
├─ サービスメッシュやマルチクラウドデプロイが必要?
│ ├─ はい → Envoy
│ └─ いいえ → 次へ
│
├─ 非常に複雑な設定やプラグインが必要?
│ ├─ はい → HAProxy
│ └─ いいえ → 次へ
│
├─ 高性能 + シンプルな設定が必要?
│ ├─ はい → Nginx(第一選択)
│ └─ 次へ
│
├─ マネージド運用がしたい?
│ ├─ はい → クラウドベンダーLB(AWS ALB、Alibaba SLB)
│ └─ Nginxを自前構築8. まとめ:負荷分散の核心的思考
8.1 核心原則の振り返り
| 原則 | 意味 | 実践のポイント |
|---|---|---|
| 階層化 | L4は「宅配便の仕分け」(速いがシンプル) | L4はDB・ゲーム用;L7はWeb・API用 |
| 冗長化 | 単一障害点はアーキテクチャの敵 | マルチインスタンス・マルチリージョンで可用性向上 |
| 段階的 | 新バージョンリリースは「一括」でやらない | Blue-Greenでゼロダウンタイム;カナリアでリスク管理 |
| 弾力性 | システムは生命体のように「呼吸」すべき | ピーク時に自動スケールアウト、アイドル時に自動スケールイン |
8.2 設計チェックリスト
負荷分散を導入する前に、以下の質問を自問してください:
- [ ] 本当に負荷分散が必要か?(単一サーバーの性能が本当に不足しているか)
- [ ] L4かL7か?(ビジネスシナリオに応じて選択)
- [ ] セッション維持をどう処理するか?(Cookie、IPハッシュ、セッションテーブル)
- [ ] ヘルスチェックをどう実装するか?(アクティブ、パッシブ、閾値設定)
- [ ] ゼロダウンタイムをどう実現するか?(Blue-Green、カナリア)
- [ ] 弾力性をどう実現するか?(スケーリング指標、クールダウン時間、最大インスタンス数)
9. 用語早見表
| 用語 | 英語 | 説明 |
|---|---|---|
| ロードバランサー | Load Balancer | トラフィックを複数のバックエンドサーバーに分散する機器またはソフトウェア |
| L4負荷分散 | L4 Load Balancing | トランスポート層(TCP/UDP)ベースの負荷分散 |
| L7負荷分散 | L7 Load Balancing | アプリケーション層(HTTP/HTTPS)ベースの負荷分散 |
| ヘルスチェック | Health Check | バックエンドサーバーの健全性を定期的にチェックするメカニズム |
| セッション維持 | Session Persistence | 同一ユーザーのリクエストを常に同じサーバーにルーティングする仕組み |
| スティッキーセッション | Sticky Session | Session Persistenceの別称 |
| Blue-Greenデプロイメント | Blue-Green Deployment | 2セットの環境を切り替えるゼロダウンタイムリリース戦略 |
| カナリアリリース | Canary Release | 少量トラフィックから先行検証するグレースフルデプロイ戦略 |
| オートスケーリング | Auto Scaling | 負荷に応じてサーバー数を自動的に増減する仕組み |
| 水平スケーリング | Horizontal Scaling | サーバー台数を増やして処理能力を向上させる |
| 垂直スケーリング | Vertical Scaling | 単一サーバーのスペック(CPU・メモリ)を上げて処理能力を向上させる |
| マルチリージョン | Multi-Region | 複数の地理的リージョンにサービスをデプロイ |
| アクティブ-アクティブ | Active-Active | 複数リージョンが同時にサービスを提供 |
| アクティブ-スタンバイ | Active-Standby | 1つのリージョンのみがサービスを提供し、他は待機 |
| データ同期 | Data Replication | リージョン間のデータ複製メカニズム |
| RTO | Recovery Time Objective (RTO) | 目標復旧時間、システム障害後にどのくらいの時間で復旧すべきか |
| RPO | Recovery Point Objective (RPO) | 目標復旧時点、システム障害後に許容できるデータ損失量 |