#はじめに
「1台のサーバーでは処理しきれない」——アクセスが増えたとき、どうすればいいのでしょうか?
ロードバランサーは、複数のサーバーにリクエストを分散させる仕組みです。可用性の向上、スケーラビリティの確保、障害耐性の向上を実現します。
この記事を読むと、以下のことができるようになります:
- ロードバランサーの役割を理解できる
- 主要な分散アルゴリズムを把握できる
- セッション維持の方法がわかる
#ロードバランサーとは
ロードバランサーとは、複数のサーバーにトラフィックを分散させる仕組みです。
┌→ [サーバー1]
[クライアント] → [LB] ────→ [サーバー2]
└→ [サーバー3]
1台のサーバーに負荷が集中することを防ぎ、システム全体の処理能力を向上させます。
#ロードバランサーの種類
#L4ロードバランサー(トランスポート層)
TCP/UDPレベルで動作します。
[クライアント] ──TCP──> [L4 LB] ──TCP──> [サーバー]
特徴:
- IPアドレスとポート番号で振り分け
- 高速、低レイテンシー
- HTTPの内容は見られない
#L7ロードバランサー(アプリケーション層)
HTTP/HTTPSレベルで動作します。
[クライアント] ──HTTP──> [L7 LB] ──HTTP──> [サーバー]
↓
リクエスト内容を解析
特徴:
- URL、ヘッダー、Cookieで振り分け可能
- パスベースルーティング
- SSLターミネーション
- L4より処理コストが高い
#L4 vs L7
| 項目 | L4 | L7 |
|---|---|---|
| 動作レイヤー | TCP/UDP | HTTP/HTTPS |
| 振り分け基準 | IP、ポート | URL、ヘッダー、Cookie |
| 速度 | 高速 | やや遅い |
| 機能 | シンプル | 高機能 |
| 用途 | 汎用 | Webアプリケーション |
#分散アルゴリズム
#ラウンドロビン(Round Robin)
順番にリクエストを振り分けます。
リクエスト1 → サーバー1
リクエスト2 → サーバー2
リクエスト3 → サーバー3
リクエスト4 → サーバー1 ← 一巡して最初に戻る
upstream backend {
server 192.168.1.10:3000;
server 192.168.1.11:3000;
server 192.168.1.12:3000;
# デフォルトでラウンドロビン
}
メリット: シンプル、均等に分散 デメリット: サーバーの処理能力差を考慮しない
#重み付きラウンドロビン(Weighted Round Robin)
サーバーの処理能力に応じて重みを設定します。
サーバー1(weight=3): 3リクエスト
サーバー2(weight=2): 2リクエスト
サーバー3(weight=1): 1リクエスト
upstream backend {
server 192.168.1.10:3000 weight=3;
server 192.168.1.11:3000 weight=2;
server 192.168.1.12:3000 weight=1;
}
用途: 異なるスペックのサーバーが混在する場合
#最小接続数(Least Connections)
現在の接続数が最も少ないサーバーに振り分けます。
サーバー1: 10接続
サーバー2: 5接続 ← 次のリクエストはここへ
サーバー3: 8接続
upstream backend {
least_conn;
server 192.168.1.10:3000;
server 192.168.1.11:3000;
server 192.168.1.12:3000;
}
メリット: 負荷が不均一な場合に効果的 用途: 処理時間が一定でないリクエスト
#IPハッシュ
クライアントIPをハッシュ化して振り分け先を決定します。
IP: 192.168.1.100 → hash → サーバー2
IP: 192.168.1.101 → hash → サーバー1
IP: 192.168.1.100 → hash → サーバー2 ← 同じIPは同じサーバーへ
upstream backend {
ip_hash;
server 192.168.1.10:3000;
server 192.168.1.11:3000;
server 192.168.1.12:3000;
}
メリット: セッション維持が容易 デメリット: NAT環境で偏りが発生する可能性
#ランダム
ランダムにサーバーを選択します。
upstream backend {
random;
server 192.168.1.10:3000;
server 192.168.1.11:3000;
}
#ヘルスチェック
ヘルスチェックとは、サーバーが正常に動作しているか定期的に確認する仕組みです。
[LB] ──ヘルスチェック──> [サーバー1] ✅ 正常
[LB] ──ヘルスチェック──> [サーバー2] ❌ 異常 → 振り分け対象から除外
[LB] ──ヘルスチェック──> [サーバー3] ✅ 正常
#パッシブヘルスチェック
実際のリクエストの結果からサーバーの状態を判断します。
upstream backend {
server 192.168.1.10:3000 max_fails=3 fail_timeout=30s;
server 192.168.1.11:3000 max_fails=3 fail_timeout=30s;
}
max_fails: 失敗回数の閾値fail_timeout: 失敗カウントのリセット時間、および異常判定後の除外時間
#アクティブヘルスチェック
定期的にヘルスチェック用のリクエストを送信します。
# nginx Plus(商用版)の場合
upstream backend {
zone backend 64k;
server 192.168.1.10:3000;
server 192.168.1.11:3000;
health_check interval=5s fails=3 passes=2;
}
#ヘルスチェックエンドポイント
アプリケーション側でヘルスチェック用のエンドポイントを用意します。
// Express.js の例
app.get('/health', (req, res) => {
// データベース接続など依存サービスの確認
const isHealthy = checkDependencies();
if (isHealthy) {
res.status(200).json({ status: 'healthy' });
} else {
res.status(503).json({ status: 'unhealthy' });
}
});
#セッション維持(スティッキーセッション)
同じユーザーのリクエストを同じサーバーに振り分ける仕組みです。
#必要な理由
セッション情報がサーバーのメモリに保存されている場合、別のサーバーにリクエストが振り分けられると、セッションが失われます。
リクエスト1 → サーバー1(ログイン、セッション作成)
リクエスト2 → サーバー2(セッションがない!ログアウト状態に)
#方法1: Cookieベース
ロードバランサーがCookieを設定し、振り分け先を記録します。
1. 初回リクエスト
[LB] → サーバー1を選択
[LB] ← Set-Cookie: SERVERID=server1
2. 以降のリクエスト
[クライアント] → Cookie: SERVERID=server1
[LB] → サーバー1へ転送
#方法2: IPハッシュ
前述のIPハッシュアルゴリズムを使用します。
#方法3: セッションの外部化
セッションデータを外部ストレージ(Redis等)に保存し、どのサーバーからでもアクセスできるようにします。
[サーバー1] ─┐
[サーバー2] ─┼→ [Redis(セッションストア)]
[サーバー3] ─┘
// Express.js + Redis の例
const session = require('express-session');
const RedisStore = require('connect-redis').default;
const { createClient } = require('redis');
const redisClient = createClient({ url: 'redis://localhost:6379' });
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: 'your-secret',
resave: false,
saveUninitialized: false,
}));
推奨: セッションの外部化が最もスケーラブル
#クラウドサービスのロードバランサー
#AWS
| サービス | 種類 | 用途 |
|---|---|---|
| ALB | L7 | Webアプリケーション |
| NLB | L4 | 高パフォーマンス、固定IP |
| CLB | L4/L7 | レガシー(非推奨) |
#GCP
| サービス | 種類 | 用途 |
|---|---|---|
| HTTP(S) Load Balancing | L7 | グローバル分散 |
| TCP/UDP Load Balancing | L4 | リージョン内分散 |
#Azure
| サービス | 種類 | 用途 |
|---|---|---|
| Application Gateway | L7 | Webアプリケーション |
| Load Balancer | L4 | 汎用 |
#高可用性の実現
#ロードバランサーの冗長化
ロードバランサー自体が単一障害点にならないよう、冗長化します。
┌→ [LB1 (Active)]
[DNS/VIP] ────────┤
└→ [LB2 (Standby)]
↓
障害時に自動切り替え
#グレースフルシャットダウン
サーバーを停止する際、処理中のリクエストを完了させてから停止します。
1. ヘルスチェックを失敗させる
2. 新規リクエストの振り分けを停止
3. 処理中のリクエストが完了するまで待機
4. サーバーを停止
upstream backend {
server 192.168.1.10:3000;
server 192.168.1.11:3000 down; # 振り分け対象から除外
}
#DevToolsでの確認
#レスポンスヘッダー
ロードバランサーが追加するヘッダーを確認できます。
# AWSのALB
X-Amzn-Trace-Id: Root=1-xxxxx
# Cloudflare
CF-Ray: xxxxxxxxx
#接続先サーバーの確認
アプリケーションでサーバー情報をヘッダーに含めると、デバッグに便利です。
app.use((req, res, next) => {
res.set('X-Server-Id', process.env.SERVER_ID);
next();
});
#まとめ
- ロードバランサーは複数サーバーにトラフィックを分散
- L4(TCP)とL7(HTTP)の2種類がある
- 分散アルゴリズム: ラウンドロビン、最小接続数、IPハッシュなど
- ヘルスチェックで異常サーバーを自動除外
- セッション維持: Cookieベース、IPハッシュ、外部セッションストア
- セッションの外部化がスケーラブルな解決策
#次のステップ
ロードバランサーの基礎を理解したところで、次はエッジコンピューティングについて学びましょう。ユーザーに近い場所でコードを実行し、レイテンシーを削減する方法を紹介します。