ロードバランサ
リクエストを複数のサーバへ分散する装置。可用性とスケーラビリティの要。
概要
ロードバランサ(負荷分散装置)は、外から届くリクエストを複数のサーバへ振り分ける仕組みです。クライアントから見える接続先はロードバランサ1つだけで、その後ろに並ぶサーバ群(バックエンド)の存在は隠されています。「窓口は1つ、中の担当者は大勢」という構図です。
役割は2つあります。1つはスケーラビリティ——1台で捌けない量のトラフィックを、台数を増やすことで受け止められるようにすること。もう1つは可用性——ヘルスチェックで故障したサーバを検知して自動的に振り分け対象から外し、残りのサーバでサービスを継続することです。
かつては専用のハードウェア製品(F5のBIG-IPなど)が主流でしたが、現在はソフトウェア(nginx、HAProxy)やクラウドのマネージドサービス(AWSのALB/NLBなど)として使うのが一般的です。ある程度の規模のWebサービスで、入口にロードバランサがいない構成はまず見かけません。
なぜ生まれたか
Webの利用が拡大した1990年代半ば、人気サイトのトラフィックは1台のサーバの処理能力をあっさり超えるようになりました。対策として「もっと強い1台に買い替える」垂直スケーリングには、価格が性能に対して急激に高くなるうえ、どんなに強くても限界がある、そしてその1台が壊れれば全部止まる、という壁があります。
「普通のサーバを並べて全体で捌く」水平スケーリングのほうが筋が良いのは明らかでしたが、問題は入口です。クライアントに「空いているサーバを選んで接続してください」とは頼めません。初期にはDNSで複数のIPアドレスを順繰りに返すDNSラウンドロビンが使われましたが、壊れたサーバのIPも返し続けてしまい、故障に対応できませんでした。そこで、トラフィックの入口に立って賢く振り分け、死んだサーバを即座に外せる専用の装置——ロードバランサが生まれました。水平スケーリングと高可用性は、ロードバランサが入口にいて初めて成立します。
詳細
基本の動きとヘルスチェック
ロードバランサは仮想的な接続先(VIPやDNS名)でリクエストを受け、登録されたバックエンド群のうち健全な1台を選んで転送します。応答は同じ経路を戻り、クライアントは分散の存在に気づきません。
要となるのがヘルスチェックです。ロードバランサは各サーバに定期的に確認リクエスト(例: /healthzへのHTTP GET)を送り、失敗が続いたサーバを振り分け対象から自動的に外します。復活すれば自動で戻します。この仕組みがあるおかげで、サーバの障害時にもサービス全体は止まらず、デプロイ時に「1台ずつ外して更新して戻す」ローリングアップデートも可能になります。ヘルスチェックのエンドポイントが「プロセスは生きているがDBに繋がらない」状態を正しく検知できるかどうかは、実務でよく問われる設計ポイントです。
図で見た分散とヘルスチェックを、実際に手で動かして確かめられます。サーバをクリックすると停止・復帰します。
バランサ
リクエストを送って、分散のされ方を観察してみてください。
分散アルゴリズム
どのサーバに割り当てるかの戦略には定番がいくつかあります。最も単純なのがラウンドロビンで、順繰りに割り当てます。処理時間にばらつきがある場合は、その時点で接続数が最も少ないサーバを選ぶ「最小接続数(Least Connections)」が有効です。サーバの性能差を係数で反映する重み付け方式や、送信元IPのハッシュで同じクライアントを同じサーバに寄せる方式もあります。均等に配ること自体より、「遅い1台に仕事を積みすぎない」「性能差を吸収する」ことが目的だと理解しておくと選択を誤りません。
L4とL7 — どの層で振り分けるか
ロードバランサは動作する層で大きく2種類に分かれます。L4(トランスポート層)ロードバランサはTCP/IPのレベルで動き、IPアドレスとポート番号だけを見て接続単位で振り分けます。中身を解釈しないぶん高速で、HTTP以外のプロトコルにも使えます。一方L7(アプリケーション層)ロードバランサはHTTPを解釈し、「/apiはAPIサーバ群へ、/imagesは画像サーバ群へ」といったパスやヘッダに基づくルーティングができます。あわせてTLSの終端(証明書の一元管理)を担うことも多く、機能的にはリバースプロキシとほぼ重なります。実際、nginxのようなソフトウェアは両方の役割を一枚で担い、AWSでもNLB(L4)とALB(L7)が使い分けられています。
ステートの罠 — セッションと設計のトレードオフ
水平分散の最大の落とし穴が「状態」です。あるサーバのメモリ上にセッション情報を保存していると、次のリクエストが別のサーバに振り分けられた瞬間にログイン状態が消えます。対処は2方向あります。1つはスティッキーセッション——Cookieなどで同じクライアントを毎回同じサーバに固定する方法で、手軽ですが、そのサーバが落ちればセッションは失われ、負荷も偏ります。もう1つが本命で、セッションを外部ストア(Redisなど)やJWTのようなトークンに追い出し、サーバ自体をステートレスにする方法です。どのサーバが受けても同じ結果になるなら、振り分けは完全に自由になり、スケーリングもサーバの入れ替えも容易になります。「ロードバランサを活かすにはアプリをステートレスに作る」は、分散システム設計の基本原則です。
最後に、ロードバランサ自体が単一障害点にならないかという問いも残ります。オンプレミスでは2台を冗長化して仮想IPを引き継ぐ構成が定番で、クラウドではマネージドサービスがこの冗長化ごと面倒を見てくれます。入口の可用性を自分で作り込まなくてよいことは、クラウド移行の大きな利点のひとつです。