ロードバランサ

ロードバランサとは

ロードバランサは、複数台のサーバーにリクエストを振り分ける装置・サービスです。1台に負荷が集中するのを防ぎ、1台が故障しても他のサーバーが応答を続けられるようにします。「負荷分散」と「可用性向上」の二つが主な目的です。

クライアント → ロードバランサ →┬→ サーバーA
                              ├→ サーバーB
                              └→ サーバーC

初学者向けポイント

  • [[リバースプロキシ]] の発展形と考えるとわかりやすい。1台への中継が「振り分け」になったもの
  • ヘルスチェック = 定期的にサーバーの生死を確認し、応答しないサーバーを自動で振り分け対象から外す。振り分け先が全滅すると利用者には [[HTTPステータスコード]] の 502 や 504 が返る
  • サーバーを増減させて負荷に対応する仕組み(オートスケーリング)は、ロードバランサとセットで機能する

振り分けアルゴリズムの例

方式内容
ラウンドロビン順番に均等に割り振る。最もシンプル
最少接続数今処理中の接続数が少ないサーバーを優先
IPハッシュ送信元IPごとに固定のサーバーへ送る(セッション維持に有効)

レイヤーによる違い

  • L4ロードバランサ: [[TCP/IP]] のレベル(IP・ポート)だけを見て振り分ける。速いが内容までは見ない
  • L7ロードバランサ: [[HTTP]] のパスやヘッダーまで見て振り分ける。柔軟だが処理コストは高い

初学者がつまずきやすい点

  • サーバーごとに [[セッション管理]] の情報を持たせると「毎回違うサーバーに振られて状態が消える」問題が起きる。セッションはDBやRedisなど外部に持たせるのが定石
  • クラウドでは AWSのELB、GCPのCloud Load Balancing のようにマネージドサービスとして提供され、自前構築は少なくなっている

ヘルスチェックの設計が可用性を左右する

壊れたサーバーを外すには、まず「壊れた」と判定できなければなりません。決めるのは何を見るか何回失敗したら外すかの2つです。

  • 浅いチェック — 指定ポートにTCP接続できるかだけを見る。軽いが、プロセスは生きていて中身が壊れている状態は見逃す
  • 深いチェック/health のようなURLへアクセスして応答内容まで確認する。アプリの初期化まで検知できる

判定は「5秒間隔で2回連続失敗したら切り離し、3回連続成功で復帰」のような閾値で行います。この緩急が難所です。間隔を30秒に広げれば、落ちてから外れるまでの間ずっと利用者にエラーが返り続けます。逆に厳しくしすぎると、一瞬の遅延で健全なサーバーまで外れ、残った台に負荷が集中してそこも遅くなり外される、という連鎖的な全滅を招きます。

典型的な失敗が、/health の中でデータベースへの問い合わせまで行う作りです。DBが一時的に遅くなると全台が同時にNG判定になり、振り分け先が1台も残りません。アプリ自身の生死を見る軽いチェックと、依存先まで含めた診断は分けておくのが安全です。なお、切り離しが起きたこと自体を [[監視とアラート]] で拾わないと、「残り1台で耐えていた」状態に気づけません。

切り離しても処理中のリクエストは残る

サーバーを止めるとき、振り分け対象から外すだけでは足りません。外した瞬間にも処理中のリクエストが残っているからです。即座にプロセスを落とすと、その利用者にはエラーが返ります。

そこで使うのがコネクションドレイン(接続の排出)です。新規の振り分けだけを止め、処理中のリクエストが終わるまで待ってから停止します。待ち時間の既定値は実装によって差が大きく、数十秒のものもあれば5分程度のものもあります(AWSのターゲットグループは既定300秒)。長い処理を抱えるなら伸ばし、デプロイを速くしたいなら実際の処理時間に合わせて縮めます。

これは [[デプロイ戦略]] のローリング更新の土台でもあります。1台ずつ入れ替えているのに停止が見えないのは、外す前にドレインが挟まっているからです。設定を忘れるとデプロイのたびにエラーが記録され、原因不明の「たまに出る500」として残り続けます。

ロードバランサ自身が単一障害点になる

入口を1つにまとめるということは、そこが壊れれば全部止まるということでもあります。サーバーを冗長化した達成感で見落としやすい点です。

マネージドサービスは1つに見えても内部で冗長化されています。自前で構築するなら2台を組にして仮想IPを共有し、片方が落ちたらもう片方が引き継ぐ形にします([[ネットワーク冗長化]])。

[[DNS]] のラウンドロビン(1つのホスト名に複数のIPを登録する)でも分散はできますが、故障時の切り替えには向きません。DNSの応答はキャッシュされるため、レコードから壊れたIPを消しても、キャッシュが切れるまで利用者はそこへ行き続けます。

次に読む記事の地図

ロードバランサは入口に立つため、話題が多方向へ伸びます。目的別に整理します。

入口に立つ仲間を知る — [[リバースプロキシ]] は中継の原型、[[Nginx]] はそれを実際に担う代表的なソフトウェアです。振り分けた先で何が動いているかは [[Webサーバーとアプリケーションサーバー]]、認証やレート制限まで入口で担う発展形は [[APIゲートウェイ]] にまとまっています。

台数を増やし、落ちても止めない — 導入の動機そのものです。[[スケーリング]] が「なぜ台数を増やすのか」、[[高可用性設計]] が「落ちても止めないための冗長化」、[[ネットワーク冗長化]] が手前のルーターやスイッチまで含めた多重化を扱います。

止めずに入れ替える — ヘルスチェックとドレインが効く場面です。[[デプロイ戦略]] は無停止で新バージョンへ切り替える方式の比較、[[Kubernetes]] は振り分けと入れ替えを自動化するプラットフォームです。

入口を守る — 全リクエストが通る場所は攻撃の的です。[[ファイアウォール]] は手前で不要な通信を遮断し、[[DDoS攻撃と対策]] は処理能力を超える通信の受け流し方を扱います。

どの層で振り分けているのか — L4・L7という呼び名の出どころは [[OSI参照モデル]] の層番号です。L4が見る情報は [[TCP/IP]]、L7が見る情報は [[HTTP]] に整理されています。

関連技術とのつながり

  • [[リバースプロキシ]] — 単一サーバーへの中継から発展した振り分けの仕組み
  • [[クラウドコンピューティング]] — マネージドロードバランサとして提供される代表的サービス
  • [[Kubernetes]] — Service や Ingress が内部的にロードバランシングを担う
  • [[HTTP]] — L7ロードバランサが振り分け判断に使うプロトコル
  • [[VPCとクラウドネットワーク]] — LBは公開側、後段のサーバーは非公開側に置く
  • [[CDN]] — 同じ「振り分け」でも、CDNはDNSなどで利用者に近い拠点へ誘導し距離を縮める仕組み。単一の入口の背後で複数サーバーへ負荷を配るロードバランサとは目的も動作する位置も異なる
Q: ヘルスチェックの `/health` でデータベースへの問い合わせまで行う作りにすると起きうる問題はどれ?
- [ ] チェック対象のサーバーが永久に切り離されなくなる
- [x] DBが一時的に遅くなると全台が同時にNG判定になり、振り分け先が1台も残らなくなる
- [ ] ロードバランサがL4からL7へ自動的に切り替わってしまう
解説: 依存先まで見る深いチェックは、その依存先の不調で全台が同時に落ちます。アプリ自身の生死を見る軽いチェックと診断は分けておくのが安全です。

Q: コネクションドレインの説明として正しいのはどれ?
- [x] 新規の振り分けだけを止め、処理中のリクエストが終わるまで待ってからサーバーを停止する
- [ ] 接続数が多いサーバーを優先して振り分ける方式のこと
- [ ] 使われていないTCP接続をまとめて強制切断する機能のこと
解説: 振り分け対象から外した瞬間にも処理中のリクエストは残っています。一定時間待ってから停止することで、ローリング更新中もエラーを出さずに済みます。

Q: DNSのラウンドロビンが故障時の切り替えに向かない理由はどれ?
- [ ] 1つのホスト名に複数のIPアドレスを登録できないから
- [ ] 振り分けがIPハッシュ方式に固定されてしまうから
- [x] DNSの応答はキャッシュされるため、レコードから消してもキャッシュが切れるまで壊れたIPへ行き続けるから
解説: DNSラウンドロビンで分散はできますが、切り替えの速さはキャッシュの有効期限に縛られます。障害時の切り離しにはヘルスチェックを持つロードバランサが必要です。