load-balancing について
バックエンド間のリクエスト分散を設計する、システム設計者向けのロードバランシングスキルです。L4・L7、分散アルゴリズム、ヘルスチェック、高可用性構成を扱い、復旧ノードの過負荷も考慮します。
主な機能
- 01L4またはL7の選択
- 02リクエスト分散アルゴリズムの比較
- 03アクティブ・アクティブ/パッシブ構成の計画
- 04ヘルスチェックと接続ドレインの定義
- 05スロースタートとリトライ増幅への対処
活用シーン
- 01複数バックエンドへのリクエスト分散
- 02バックエンドプールのヘルスゲーティング設計
- 03ロードバランサーの可用性とフェイルオーバー計画
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ proyecto26/ system-design-skills --skill load-balancingインストール後、Claude Code で/load-balancingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/deep-dive.mdreferences/providers/aws.mdazure.mdgcp.mdgeneric.md詳細
負荷分散
同一構成のバックエンド群に受信リクエストを分散し、単一のサーバーがボトルネックや単一障害点にならないようにします。設定を誤ると、排除するはずだった SPOF がロードバランサーになったり、停止中のサーバーにトラフィックを送ったり、すでに限界に達しているバックエンドを叩いて障害を 増幅 させたりします。
導入を検討する場面
複数のバックエンドが同じ役割を担い、トラフィックを分散する必要がある場合、排除すべき SPOF になっている単一の入口がある場合、クライアントに気づかれずにサーバーを追加・削除したい場合(ステートレス層とオートスケーリングを可能にする仕組みです)、あるいは TLS 終端、ヘルスチェックによる振り分け、ルーティングを一か所にまとめて、プライベートなサーバー群の前に単一のパブリックアドレスを置きたい場合に、ロードバランサーを導入します。
導入しない場面
ピーク負荷を余裕をもって処理できるバックエンドが1台だけなら、まだロードバランサーは必要ありません。追加すれば、新たなコンポーネント、障害モード、運用対象が増えます(YAGNI)。本当のボトルネックがデータベースや単一のホットシャードなら、Web 層の前にロードバランサーを置いても解決しません。まず実際の制約を診断してください(→ back-of-the-envelope)。リージョンをまたぐトラフィックの振り分けには、通常、L4/L7 ロードバランサーではなく、エッジでの DNS/エニーキャストを使います(→ content-delivery)。クライアントごとのリクエスト制限は、負荷分散アルゴリズムではなく、resilience-failure が担うポリシー上の課題です。
まず確認すること
- プロトコルと必要なレイヤー — 生の TCP/UDP スループット(L4)が必要ですか? それともパス/ホスト/ヘッダー/Cookie に応じた HTTP 対応ルーティング(L7)が必要ですか? これによってロードバランサーの種類が決まります。
- 状態 — バックエンドはステートレスですか? それともセッションが特定のサーバーに保持されますか(アフィニティが必要になります)? 状態を外部に移すほうが、ほとんどの場合は優れた解決策です。
- ピーク時の QPS と接続数 — 長時間維持される接続が少数ですか? それとも短いリクエストが多数ですか? これらがアルゴリズムとロードバランサーのサイジングを左右します(→
back-of-the-envelope)。 - ヘルスシグナル — 「正常」とは何を意味しますか(TCP 接続の受付?
/healthzの 200? 依存先まで含む詳細なチェック?)。停止したノードをどれくらいの速さでプールから外す必要がありますか? - TLS — ロードバランサーで終端しますか(バックエンドの負荷を軽減し、L7 の内容を検査します)? それともエンドツーエンドでパススルーしますか(コンプライアンス/mTLS)?
選択肢
検査レイヤー
- L4(トランスポート): IP アドレス/ポートでルーティングします。ペイロードを読み取らず、NAT または DSR でパケットを転送します。生のスループット、HTTP 以外のプロトコル、または追加レイテンシの最小化が必要な場合に使います。
- L7(アプリケーション): 接続を終端して HTTP(ホスト、パス、ヘッダー、Cookie)を読み取り、その後にルーティングします。コンテンツに基づくルーティング、ルート別のプール、TLS 終端、またはリクエストの書き換えが必要な場合に使います。
分散アルゴリズム
- ラウンドロビン/重み付きラウンドロビン: 均等に順番に振り分け、容量に応じて重み付けします。負荷が均一でステートレスなバックエンドに使います。
- 最小接続数/最短応答時間: 最も負荷の低いノードに送ります。リクエストのコストに大きな差がある場合(長時間維持される接続や混在するワークロード)に使います。
- IP ハッシュ/コンシステントハッシュ: クライアント(またはキー)を固定のバックエンドに割り当てます。サーバー側にセッションを保存せずにアフィニティを実現する場合や、キャッシュの局所性を保つ場合に使います。
- ランダム(二択): ランダムに2台を選び、負荷の軽いほうに送ります。低コストで、規模を拡大しても驚くほど均等に分散できます。
トポロジー
- アクティブ・パッシブ: 1台のロードバランサーが稼働し、障害時には待機系が VIP を引き継ぎます。シンプルな HA 構成です。
- アクティブ・アクティブ: 複数のロードバランサーで負荷を分担します(DNS またはエニーキャストを使用)。ロードバランサー自体の SPOF をなくし、余裕のある処理能力を確保できます。
リバースプロキシの役割: L7 ロードバランサーはリバースプロキシでもあります。バックエンドを隠し、TLS を終端し、圧縮、キャッシュ、ルーティングの一元管理を行う、単一のパブリックな窓口です。これらの利点があれば、バックエンドが1台だけでもリバースプロキシを導入する価値があります。負荷分散は、同じコンポーネントを複数のバックエンドに対して使うケースです。
トレードオフ
| 選択肢 | 解決できること | 悪化すること | 切り替えるタイミング |
|---|---|---|---|
| L4 負荷分散 | 最大スループット、低レイテンシ、あらゆるプロトコルへの対応 | コンテンツを認識できない。パス/ヘッダーによるルーティングや TLS 検査ができない | コンテンツベースのルーティングや TLS 終端が必要な場合 → L7 |
| L7 負荷分散 | コンテンツルーティング、TLS オフロード、書き換え、可観測性 | レイテンシ/CPU 使用率が高くなる。接続を終端するため、状態が増える | 生のスループットが最優先で、ルーティングが単純な場合 → L4 |
| ラウンドロビン | 非常にシンプルで、均一な処理なら効果的 | 実際の負荷を考慮しない。遅いノードにも同じ割合で割り当てる | リクエストの処理コストに大きな差がある場合 → 最小接続数 |
| 最小接続数 | リクエストの処理コストの偏りに対応できる | 接続状態をリアルタイムで把握する必要がある。復旧直後のノードに負荷が集中することがある | バックエンドが均一な場合 → ラウンドロビンで十分 |
| IP/一貫性ハッシュ | セッションストアなしでアフィニティとキャッシュ局所性を実現 | 分散に偏りが生じる。プールの変更時に再分散される | 状態を外部に保存できる場合 → ラウンドロビン+共有ストア |
| スティッキーセッション | 現在のステートフルなバックエンドで機能する | 均等な分散が崩れ、スケールインが複雑になり、ノード障害時にセッションが失われる | 状態を Redis/DB に移行できる場合 → アフィニティを解除 |
| アクティブ/パッシブ | シンプルなロードバランサーの HA | 待機系がアイドル状態になる。フェイルオーバーに数秒の空白が生じる | 空白のない余力が必要な場合 → アクティブ/アクティブ |
負荷時の挙動
ロードバランサーは すべての リクエスト経路にあるため、その障害モードは システム全体の障害モードになります。
- ヘルスチェックの集中砲火(典型的な落とし穴): バックエンドが復旧すると、
ロードバランサーは正常と判定し、冷え切った単一ノードに大量のリクエストを送ります。キャッシュが空で、
JIT もウォームアップされておらず、接続キューも満杯になるため、次のヘルスチェックに失敗して
対象から外れ、状態が不安定に変動します(フラッピング)。過剰に頻繁なチェックは、実質的に 復旧中のサービスへの DDoS になり得ます。
スロースタート/接続の段階的増加、十分に余裕のある
失敗しきい値(除外までに N 回、再登録までに M 回の失敗)、チェックのジッター、
依存先経路のサーキットブレーカー(→
resilience-failure)で緩和します。 - リトライの増幅: ロードバランサー(またはクライアント)が、失敗したリクエストを
残りの正常なノードに再試行するため、1 台のノードを失うと、残存ノードがその負荷と
リトライを引き受けて連鎖的に障害が拡大する可能性があります。リトライ回数を制限し、
ジッターを伴うバックオフを使用してください(担当は
resilience-failure)。 - 負荷の偏り/過負荷のバックエンド: 長時間接続やスティッキーセッションによって、 負荷が一部のノードに固定されます。ラウンドロビンではこれを把握できません。全体の集計値だけでなく、 バックエンドごとの使用率を監視してください。
- SPOF としてのロードバランサー: 1 台のロードバランサーに障害が発生すると、すべてが停止します。 高速な VIP/エニーキャストのフェイルオーバーを備えたアクティブ/アクティブまたはアクティブ/パッシブで運用してください。
- 再接続の集中: ロードバランサーが再起動すると、すべてのクライアントが一斉に再接続します。 ドレインの開始をずらし、接続数を制限してください。
監視項目: バックエンドごとのリクエストレートとレイテンシ(p99)、正常なホスト数、 ヘルスチェックの成功/失敗とフラッピング率、接続数、ロードバランサー側と バックエンド側の 5xx 率(差異から障害箇所を特定)、およびアクティブ接続の 分布。
適用方法
- 入力を明確にする — まず確認することの質問に答えます。プロトコル/レイヤー、バックエンドがステートレスかどうか、ピーク QPS と接続数、「正常」の定義、TLS の方針を確認します。これらによって、後続のすべての選択が決まります。
- トレードオフ表からレイヤーとアルゴリズムを選ぶ — 生のスループットや非 HTTP なら L4、コンテンツルーティングや TLS オフロードなら L7 を選びます。均一なステートレスバックエンドにはラウンドロビンを標準とし、リクエストのコストにばらつきがある場合に限り最少接続数を選び、状態をまだ移行できない場合に限りハッシュベースのアフィニティを使います。
- 重要な設定値を決める — ヘルスチェックを具体的に記述します(メソッド、パス、期待するコード、間隔、失敗/復帰のしきい値)。プールのドレイン遅延も設定し、スロースタートを有効にして、復旧したノードが一気に大量のトラフィックを受けるのではなく、徐々に負荷を増やせるようにします。
- 選択をストレステストする — ヘルスチェックの集中、リトライの増幅、特定バックエンドへの負荷集中、ロードバランサー自体が単一障害点(SPOF)になるケースなど、障害モードを検討します。ロードバランサーが新たな単一障害点にならないよう、アクティブ/アクティブ(または高速な VIP/エニーキャストフェイルオーバーを伴うアクティブ/パッシブ)を追加します。
- 数値を使って規模を決める — ロードバランサーがピーク QPS と同時接続数に対応できることを確認し、制約となるのがロードバランサーではなく バックエンドのサーバー群であることを検証します。サーバーあたりの QPS と接続メモリの数値は
back-of-the-envelopeから取得します。 - プロバイダーを選ぶ — 汎用のレシピを標準とします。クラウドが指定されている場合は、そのプロバイダーのファイルを参照して、マネージドサービスへの対応関係と制限を確認します。
- 回答でトレードオフを示す — ここで行った選択ごとに 1 行を設けた
Decision | Solves | Worsens | Change it when…テーブルを提示します(例:選択肢、そのストア、障害時の方針)。最後の列には具体的な切り替え条件(数値または観測可能な兆候)を記載します。その後、最初に何が破綻するかを明示します。
すべきこと、すべきでないこと
すべきこと
- セッション状態を共有ストアに移し、シンプルなラウンドロビンとオートスケーリングが使えるようにします。
- ヘルスチェックを明示的に定義し、浅いチェックにするか深いチェックにするかを意図的に選びます。
- ロードバランサーをアクティブ/アクティブまたはアクティブ/パッシブで運用し、SPOF にならないようにします。
- スロースタートと余裕のある失敗/復帰のしきい値を有効にして、状態の不安定な切り替わりを防ぎます。
- バックエンドごとの使用率を監視し、全体の集計値だけでなく、特定ノードへの負荷集中を検出します。
すべきでないこと
- ピーク負荷に対応できるバックエンドが 1 つしかない場合に、ロードバランサーを追加しません(YAGNI)。リバースプロキシを導入する価値はあるかもしれませんが、複数バックエンドの負荷分散は不要です。
- 遅い依存先に対する深いヘルスチェックによって、サーバー群全体が一度に切り離される状態にしません。
- 状態を簡単に外部へ移せる場合に、スティッキーセッションを安易に使いません。
- 生き残ったノードへ無制限にリトライしません。リトライ回数を制限し、バックオフ/ジッターを使います。
- バックエンドのサーバー群を確認する前に、ロードバランサーがボトルネックだと決めつけません。
重要な数値
最新のソフトウェアロードバランサー(HAProxy/Nginx/Envoy)は、インスタンスあたり毎秒数万件のリクエストと数千件を超える同時接続を処理できます。通常は単一の Web/アプリケーションノードを大きく上回るため、最初のボトルネックになることはまれです。ピーク QPS と同時接続数に基づいて規模を決め、制約となるのがロードバランサーではなく バックエンドのサーバー群であることを確認してください。サーバーあたりの QPS、接続あたりのメモリコスト、ピーク係数の計算は back-of-the-envelope に記載されています。ここでは再掲せず、プールとロードバランサーの規模を決める際にその数値を参照してください。
インターフェースの概要
ロードバランシング自体にはリクエスト/レスポンスの契約はありませんが、重要な設定が 2 つあるため、具体的に記録しておく価値があります。
- ヘルスチェック: メソッド + パス + 期待するコード + 間隔 + しきい値。例:
GET /healthz → 200, every 5s, unhealthy after 3 fails, healthy after 2 passes。/healthzを浅くする(プロセスが稼働中か確認する)か、深くする(DB を確認する)かを慎重に決めます。遅い依存先で失敗する深いチェックは、サーバー群全体を一度に切り離す可能性があります。 - バックエンドプール/ターゲットグループ:
host:portのターゲット群、重み、スケールイン時に処理中のリクエストを完了させるためのドレイン/登録解除の遅延。
ロードバランシングでステートレス階層を実現する方法
リクエストを、相互に置き換え可能なサーバー間で自由に振り分けられるのは、どのサーバーでもあらゆるリクエストを処理できる場合、つまりサーバー上にセッション状態が存在しない場合に限られます。セッション状態を共有ストア(Redis/DB)に移すと、ロードバランサーは通常のラウンドロビンを使い、ノードを自由に追加・削除できるようになり、オートスケーリンググループでプールを拡大・縮小できます。状態をまだ移せない場合は、スティッキーセッションが代替手段になりますが、均等な分散と容易なスケールインは犠牲になります。水平スケーリングとステートレス階層の説明は scaling-evolution が担当し、このスキルはルーティング可能にする仕組みです。
プロバイダーの選択
まずは上記の汎用的な手順を使ってください。ユーザーがクラウドを指定した場合は、references/providers/<provider>.md を読み、マネージドサービスとの対応、クォータ/制限、プロバイダー固有のトレードオフを確認してください。そのプロバイダー向けのファイルがない場合は、汎用的な手順が回答になります。
図
リクエスト経路(クライアント → ロードバランサー → バックエンドプール)や、ヘルスチェック/フェイルオーバーの流れを視覚化するには、プラグイン内の architecture-diagram スキルを使ってください。簡単なインライン図 — client → [LB] → {web1, web2, web3} → shared state — は考察用として問題ありませんが、Mermaid を埋め込まないでください。
関連する構成要素
scaling-evolution— このスキルへの入力となる: ステートレス階層と水平/垂直スケーリングを担当し、このスキルはそのサーバー群をアドレス指定可能にするルーティングの仕組みです。resilience-failure— ロードバランサーが障害を増幅させる可能性がある場合は、これと組み合わせます。また、関連する概念の担当先でもあります。リトライ/バックオフ/ジッター、サーキットブレーカー、フェイルオーバー、レート制限は、ヘルスチェックの集中やリトライストームへの対策です。content-delivery— クロスリージョンのトラフィックでは、このスキルの代替となります。「負荷分散」が実際にはユーザーを最寄りのリージョンへ誘導することを意味する場合は、L4/L7 ロードバランサーではなく、エッジでのジオルーティング/DNS/エニーキャスト(このスキルが担当)が適切なツールです。back-of-the-envelope— プールとロードバランサーのサイズを決める QPS、接続数、ピーク係数の値について、このスキルに依存します。system-design— 設計が単一サーバーを超える規模になったときに、こちらへ処理を振り分けるオーケストレーターです。
参考資料
references/deep-dive.md— L4 NAT と DSR、L7 終端の仕組み、アルゴリズムの内部動作(アフィニティのためのコンシステントハッシュを含む)、ヘルスチェックの調整とスロースタート、接続のドレイン、TLS 終端とパススルー。ロードバランサーを詳細に設定する場合に読んでください。references/providers/{generic,aws,azure,gcp}.md— 各環境におけるサービスの対応関係、制限、落とし穴。