メインコンテンツまでスキップ

ロードバランシング

所要時間

約45分

学べること

  • ロードバランシングの基本概念とアルゴリズム
  • L4(Transport Layer)とL7(Application Layer)ロードバランサーの違い
  • ヘルスチェックとセッション維持の仕組み
  • ロードバランサーとDNSの連携
  • 一般的なロードバランシングパターン

前提知識


1. ロードバランシングの基礎

1.1 ロードバランシングとは

ロードバランシングは、複数のサーバーに対してトラフィックを分散させる技術です。

┌─────────────────────────────────────────────────────────────┐
│ ロードバランサーなし vs あり │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【ロードバランサーなし】 │
│ クライアント │
│ │ │
│ └───────► サーバー1(100%の負荷) │
│ │
│ 問題点: │
│ - 単一障害点(SPOF) │
│ - スケーラビリティの限界 │
│ - メンテナンス時のダウンタイム │
│ │
│ 【ロードバランサーあり】 │
│ クライアント │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ ロードバランサー │ │
│ └────────────────┘ │
│ │ │
│ ├───────► サーバー1(33%の負荷) │
│ ├───────► サーバー2(33%の負荷) │
│ └───────► サーバー3(33%の負荷) │
│ │
│ 利点: │
│ - 高可用性(1台故障しても継続) │
│ - 水平スケーリング(サーバー追加で対応) │
│ - ゼロダウンタイムデプロイ │
│ │
└─────────────────────────────────────────────────────────────┘

1.2 ロードバランシングの目的

1. 高可用性(High Availability)
- サーバー障害時の自動フェイルオーバー
- サービスの継続性確保

2. スケーラビリティ
- 水平スケーリング(サーバー台数追加)
- トラフィック増加への対応

3. パフォーマンス
- 負荷分散による応答速度向上
- リソースの効率的利用

4. メンテナンス性
- ローリングアップデート
- ゼロダウンタイムデプロイ

2. ロードバランシングアルゴリズム

2.1 主要なアルゴリズム

ラウンドロビン(Round Robin)

最もシンプルなアルゴリズム。順番にリクエストを振り分けます。

┌─────────────────────────────────────────────────────────────┐
│ ラウンドロビン │
├─────────────────────────────────────────────────────────────┤
│ │
│ リクエスト1 → サーバー1 │
│ リクエスト2 → サーバー2 │
│ リクエスト3 → サーバー3 │
│ リクエスト4 → サーバー1 ← 最初に戻る │
│ リクエスト5 → サーバー2 │
│ ... │
│ │
│ 利点: │
│ - 実装がシンプル │
│ - 均等に分散 │
│ │
│ 欠点: │
│ - サーバーの性能差を考慮しない │
│ - リクエストの重さを考慮しない │
│ │
└─────────────────────────────────────────────────────────────┘

重み付けラウンドロビン(Weighted Round Robin)

サーバーの性能に応じて重みを設定します。

サーバー1: 重み 3(性能高)
サーバー2: 重み 2(性能中)
サーバー3: 重み 1(性能低)

リクエスト1 → サーバー1
リクエスト2 → サーバー1
リクエスト3 → サーバー1
リクエスト4 → サーバー2
リクエスト5 → サーバー2
リクエスト6 → サーバー3
リクエスト7 → サーバー1 ← 最初に戻る

最小接続数(Least Connections)

現在の接続数が最も少ないサーバーにリクエストを振り分けます。

┌─────────────────────────────────────────────────────────────┐
│ 最小接続数 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 現在の状態: │
│ サーバー1: 5接続 │
│ サーバー2: 3接続 ← 最小 │
│ サーバー3: 7接続 │
│ │
│ 新しいリクエスト → サーバー2に振り分け │
│ │
│ 利点: │
│ - 長時間接続に適している(WebSocket等) │
│ - 動的に負荷を均等化 │
│ │
│ 欠点: │
│ - 接続数の追跡が必要(オーバーヘッド) │
│ │
└─────────────────────────────────────────────────────────────┘

IPハッシュ(IP Hash / Source IP Affinity)

クライアントのIPアドレスに基づいてサーバーを決定します。

hash(クライアントIP) % サーバー数 = サーバーインデックス

例:
hash(192.168.1.10) % 3 = 1 → サーバー2
hash(192.168.1.20) % 3 = 0 → サーバー1
hash(192.168.1.30) % 3 = 2 → サーバー3

利点:
- 同一クライアントは常に同一サーバーに接続
- セッション維持(Session Affinity / Sticky Session)

欠点:
- サーバー数変更時に再分散が発生
- 負荷が偏る可能性

最小レスポンスタイム(Least Response Time)

応答時間が最も短いサーバーにリクエストを振り分けます。

サーバー1: 平均応答時間 50ms
サーバー2: 平均応答時間 30ms ← 最速
サーバー3: 平均応答時間 80ms

新しいリクエスト → サーバー2に振り分け

利点:
- パフォーマンス最適化
- 動的に最速サーバーを選択

欠点:
- 応答時間の計測が必要(複雑)

3. L4 vs L7 ロードバランサー

3.1 OSI参照モデルとロードバランサー

┌─────────────────────────────────────────────────────────────┐
│ L4 vs L7 ロードバランサー │
├─────────────────────────────────────────────────────────────┤
│ │
│ OSI参照モデル │
│ ┌─────────────────────┐ │
│ │ 7. アプリケーション │ ← L7ロードバランサー │
│ ├─────────────────────┤ (HTTP/HTTPS、URLパス、ヘッダー) │
│ │ 6. プレゼンテーション│ │
│ ├─────────────────────┤ │
│ │ 5. セッション │ │
│ ├─────────────────────┤ │
│ │ 4. トランスポート │ ← L4ロードバランサー │
│ ├─────────────────────┤ (TCP/UDP、IPアドレス、ポート) │
│ │ 3. ネットワーク │ │
│ ├─────────────────────┤ │
│ │ 2. データリンク │ │
│ ├─────────────────────┤ │
│ │ 1. 物理 │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

3.2 L4ロードバランサー(Transport Layer)

L4ロードバランサーは、IPアドレスとポート番号に基づいてトラフィックを分散します。

┌─────────────────────────────────────────────────────────────┐
│ L4ロードバランサーの動作 │
├─────────────────────────────────────────────────────────────┤
│ │
│ クライアント (192.168.1.10:54321) │
│ │ │
│ │ TCP SYN → lb.example.com:443 │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ L4ロードバランサー │ │
│ │ (lb.example.com) │ │
│ └──────────────────────┘ │
│ │ │
│ │ 判断基準: │
│ │ - 送信元IP: 192.168.1.10 │
│ │ - 送信元ポート: 54321 │
│ │ - 宛先IP: lb.example.com │
│ │ - 宛先ポート: 443 │
│ │ - プロトコル: TCP │
│ │ │
│ ├───► Server1 (10.0.1.10:443) │
│ ├───► Server2 (10.0.1.11:443) │
│ └───► Server3 (10.0.1.12:443) │
│ │
│ ※ HTTPヘッダーやペイロードは見ない │
│ │
└─────────────────────────────────────────────────────────────┘

L4の特徴:

利点:
- 高速(パケット検査が少ない)
- 低レイテンシー
- あらゆるTCP/UDPトラフィックに対応
- SSL終端不要(パススルー可能)

欠点:
- URLパスベースのルーティング不可
- HTTP/HTTPSヘッダーの利用不可
- コンテンツベースのルーティング不可

用途:
- 非HTTPトラフィック(DB、SMTP、DNS等)
- 超高速処理が必要な場合
- SSL/TLSをバックエンドで処理したい場合

3.3 L7ロードバランサー(Application Layer)

L7ロードバランサーは、HTTPリクエストの内容に基づいてトラフィックを分散します。

┌─────────────────────────────────────────────────────────────┐
│ L7ロードバランサーの動作 │
├─────────────────────────────────────────────────────────────┤
│ │
│ クライアント │
│ │ │
│ │ GET /api/users HTTP/1.1 │
│ │ Host: example.com │
│ │ User-Agent: Mobile │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ L7ロードバランサー │ │
│ │ (Nginx/HAProxy) │ │
│ └──────────────────────┘ │
│ │ │
│ │ 判断基準: │
│ │ - URLパス: /api/users │
│ │ - ホストヘッダー: example.com │
│ │ - HTTPメソッド: GET │
│ │ - User-Agentヘッダー: Mobile │
│ │ - Cookieの有無 │
│ │ │
│ ├───► /api/* → API Server (10.0.1.10) │
│ ├───► /static/* → Static Server (10.0.1.20) │
│ └───► /* → App Server (10.0.1.30) │
│ │
└─────────────────────────────────────────────────────────────┘

L7の特徴:

利点:
- URLパスベースのルーティング
- ホストヘッダーベースのルーティング
- HTTPヘッダーの読み書き
- WebSocketサポート
- SSL/TLS終端(証明書管理の一元化)
- コンテンツベースのルーティング

欠点:
- L4より低速(HTTP解析が必要)
- HTTP/HTTPS専用

用途:
- Webアプリケーション
- マイクロサービス(パスベースルーティング)
- コンテナ環境(複数サービスの統合)

4. 実践的なロードバランサー設定

4.1 ロードバランサーの選択基準

ロードバランサーを選択する際の一般的な考慮事項:

┌─────────────────────────────────────────────────────────────┐
│ ロードバランサー選択基準 │
├─────────────────────────────────────────────────────────────┤
│ │
│ L7ロードバランサー(アプリケーション層) │
│ 推奨ケース: │
│ - HTTP/HTTPSトラフィック │
│ - URLパスベースルーティングが必要 │
│ - SSL/TLS終端が必要 │
│ - WebSocketサポートが必要 │
│ 例: Nginx, HAProxy, Apache mod_proxy │
│ │
│ L4ロードバランサー(トランスポート層) │
│ 推奨ケース: │
│ - 非HTTPプロトコル(TCP/UDP) │
│ - 超高速処理が必要 │
│ - SSL/TLSをバックエンドで処理 │
│ - データベース接続の負荷分散 │
│ 例: HAProxy (TCPモード), LVS, Nginx Stream │
│ │
└─────────────────────────────────────────────────────────────┘

4.2 L7ロードバランサーの一般的な機能

L7ロードバランサー(Nginx、HAProxy等)で実現できる一般的な機能:

1. パスベースルーティング
/api/* → APIサーバーグループ
/images/* → 静的コンテンツサーバー
/* → デフォルトサーバーグループ

2. ホストベースルーティング
api.example.com → APIサーバー
www.example.com → Webサーバー

3. HTTPヘッダーベースルーティング
User-Agent: Mobile → モバイル専用サーバー
User-Agent: Desktop → デスクトップ専用サーバー

4. プロトコルサポート
- HTTP/1.1、HTTP/2
- WebSocket
- gRPC

5. SSL/TLS終端
- 証明書の一元管理
- SNI(複数証明書)サポート

Nginx設定例(L7ロードバランサー)

# /etc/nginx/nginx.conf

http {
# バックエンドサーバーグループ定義
upstream api_servers {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}

upstream web_servers {
server 192.168.1.20:80;
server 192.168.1.21:80;
}

# ロードバランサー設定
server {
listen 80;
server_name example.com;

# パスベースルーティング
location /api/ {
proxy_pass http://api_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

location / {
proxy_pass http://web_servers;
proxy_set_header Host $host;
}
}
}

4.3 L4ロードバランサーの設定

L4ロードバランサーは、TCPレベルでの負荷分散を提供します。

HAProxy設定例(L4モード)

# /etc/haproxy/haproxy.cfg

global
maxconn 4096
log /dev/log local0

defaults
mode tcp
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms

# データベース負荷分散(PostgreSQL)
frontend postgres_frontend
bind *:5432
default_backend postgres_servers

backend postgres_servers
balance roundrobin
server pg1 192.168.1.10:5432 check
server pg2 192.168.1.11:5432 check
server pg3 192.168.1.12:5432 check

Nginx Stream設定例(L4モード)

# /etc/nginx/nginx.conf

stream {
upstream postgres_backend {
server 192.168.1.10:5432;
server 192.168.1.11:5432;
server 192.168.1.12:5432;
}

server {
listen 5432;
proxy_pass postgres_backend;
proxy_connect_timeout 1s;
}
}

5. ヘルスチェック

5.1 ヘルスチェックの仕組み

┌─────────────────────────────────────────────────────────────┐
│ ヘルスチェックの動作 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ロードバランサー │
│ │ │
│ ├───► Server1: GET /health → 200 OK ✅ 正常 │
│ │ │
│ ├───► Server2: GET /health → 200 OK ✅ 正常 │
│ │ │
│ └───► Server3: GET /health → 500 Error ❌ 異常 │
│ ↓ │
│ トラフィック停止 │
│ ↓ │
│ 連続2回正常になるまで待機 │
│ ↓ │
│ トラフィック再開 │
│ │
└─────────────────────────────────────────────────────────────┘

5.2 ヘルスチェック設定パラメータ

パラメータ               デフォルト  説明
────────────────────────────────────────────────────────
Protocol HTTP ヘルスチェックプロトコル
Port Traffic ヘルスチェックポート
Path / ヘルスチェックパス
Interval 30秒 チェック間隔
Timeout 5秒 タイムアウト
Healthy Threshold 2回 正常判定までの成功回数
Unhealthy Threshold 3回 異常判定までの失敗回数
Success Codes 200 正常とみなすHTTPステータス

5.3 ヘルスチェックエンドポイントの実装

ヘルスチェックエンドポイントは、アプリケーションの正常性を確認するためのシンプルなHTTPエンドポイントです。

一般的なヘルスチェックエンドポイント設計:

1. シンプルなヘルスチェック(/health)
- HTTPステータス: 200 OK(正常時)
- レスポンス: "OK" または {"status": "UP"}
- 用途: 基本的な生存確認

2. 詳細なヘルスチェック(/health/detailed)
- データベース接続確認
- 外部API接続確認
- キャッシュサーバー接続確認
- HTTPステータス: 200(全て正常)、503(異常あり)
- レスポンス例:
{
"status": "UP",
"database": "UP",
"cache": "UP"
}

3. Readiness Probe(/ready)
- アプリケーション初期化完了確認
- トラフィック受付準備完了を示す
- コンテナ環境で重要

4. Liveness Probe(/alive)
- プロセスの生存確認
- 最も軽量なチェック

シンプルな実装例(疑似コード)

GET /health
if (application_is_running) {
return 200 OK, body: "OK"
} else {
return 503 Service Unavailable
}

GET /health/detailed
status = {}

# データベースチェック
try {
execute_query("SELECT 1")
status["database"] = "UP"
} catch (error) {
status["database"] = "DOWN"
return 503, body: status
}

# 全て正常
status["status"] = "UP"
return 200, body: status

5.4 ヘルスチェックのベストプラクティス

1. 軽量なエンドポイント
- 重い処理は避ける
- 数秒以内に応答

2. 依存サービスのチェック
- データベース接続
- 外部API接続
- キャッシュサーバー接続

3. エンドポイントの使い分け
/health : 基本的な生存確認
/ready : トラフィック受付準備完了
/alive : プロセス生存確認

4. ログ出力の抑制
- ヘルスチェックのログは大量になる
- 必要に応じてフィルタリング

5. 適切なタイムアウト設定
- アプリケーションの起動時間を考慮
- 初期遅延(Initial Delay)の設定

6. セッション維持(Sticky Session / Session Affinity)

6.1 セッション維持の必要性

┌─────────────────────────────────────────────────────────────┐
│ セッション維持なし vs あり │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【セッション維持なし】 │
│ ユーザーA │
│ ├─ リクエスト1 → Server1 (セッション作成) │
│ └─ リクエスト2 → Server2 (セッション不明 ❌) │
│ │
│ 問題: ログイン状態が失われる │
│ │
│ 【セッション維持あり】 │
│ ユーザーA │
│ ├─ リクエスト1 → Server1 (セッション作成) │
│ └─ リクエスト2 → Server1 (セッション維持 ✅) │
│ │
└─────────────────────────────────────────────────────────────┘

6.2 セッション維持の方式

Cookieベース

┌─────────────────────────────────────────────────────────────┐
│ Cookie ベースセッション維持 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 初回リクエスト │
│ クライアント → ロードバランサー → Server1 │
│ │
│ 2. ロードバランサーがCookieを追加 │
│ ロードバランサー → クライアント │
│ Set-Cookie: LB_SESSION=...; Path=/; Expires=... │
│ │
│ 3. 2回目以降のリクエスト │
│ クライアント → ロードバランサー (Cookieを送信) │
│ Cookie: LB_SESSION=... │
│ ↓ │
│ ロードバランサーがCookieを読み取り、Server1に転送 │
│ │
└─────────────────────────────────────────────────────────────┘

IPアドレスベース

クライアントIPアドレスに基づいて同じサーバーに転送

hash(クライアントIP) % サーバー数 = サーバーインデックス

利点: Cookieなしでセッション維持
欠点: NATやプロキシ経由で同一IPに見える場合、偏りが発生

6.3 ロードバランサーでのSticky Session設定

Nginx設定例

# /etc/nginx/nginx.conf

upstream backend_servers {
# IPハッシュによるセッション維持
ip_hash;

server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}

server {
listen 80;
server_name example.com;

location / {
proxy_pass http://backend_servers;
}
}

HAProxy設定例

# /etc/haproxy/haproxy.cfg

backend web_servers
balance roundrobin
# Cookieベースのセッション維持
cookie SERVERID insert indirect nocache

server web1 192.168.1.10:80 check cookie web1
server web2 192.168.1.11:80 check cookie web2
server web3 192.168.1.12:80 check cookie web3

6.4 ステートレス設計(推奨)

セッション維持に依存しない設計が推奨されます。

アンチパターン: サーバー側セッション
- サーバーのメモリにセッション情報を保存
- スケールアウトが困難
- ロードバランサーでSticky Session必須

推奨パターン1: トークンベース認証(JWT等)
- セッション情報をトークンに含める
- ステートレス(サーバー側でセッション保持不要)
- どのサーバーでもトークン検証可能
- スケールアウトが容易

推奨パターン2: 外部セッションストア
- Redis、Memcached等にセッション保存
- 全サーバーから共有セッションストアにアクセス
- Sticky Session不要
- 高可用性設計が必要

比較:
┌──────────────────┬─────────┬──────────┬─────────────┐
│ 方式 │ 複雑度 │ スケール │ Sticky必要 │
├──────────────────┼─────────┼──────────┼─────────────┤
│ サーバーセッション│ 低 │ 困難 │ 必須 │
│ トークンベース │ 中 │ 容易 │ 不要 │
│ 外部ストア │ 高 │ 容易 │ 不要 │
└──────────────────┴─────────┴──────────┴─────────────┘

7. コンテナ環境でのロードバランシング

7.1 コンテナ環境の特徴

コンテナオーケストレーション(Kubernetes、Docker Swarm等)では、動的にコンテナが作成・削除されるため、ロードバランサーとの統合が重要です。

┌─────────────────────────────────────────────────────────────┐
│ コンテナロードバランシングの課題 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 従来の環境: │
│ - サーバーのIPアドレスは固定 │
│ - 手動でロードバランサーに登録 │
│ │
│ コンテナ環境: │
│ - コンテナのIPアドレスは動的に変化 │
│ - コンテナの数が自動的に増減(オートスケール) │
│ - ロードバランサーへの自動登録/解除が必要 │
│ │
│ 解決策: │
│ - サービスディスカバリー │
│ - 動的ターゲット登録 │
│ - ヘルスチェックによる自動除外 │
│ │
└─────────────────────────────────────────────────────────────┘

7.2 Kubernetes Service(LoadBalancer type)

Kubernetesは、Serviceリソースを使用してロードバランシングを提供します。

# Kubernetes Service定義
apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
type: LoadBalancer # 外部ロードバランサーを自動作成
selector:
app: api
ports:
- port: 80
targetPort: 8080
protocol: TCP

---
# Deployment定義(バックエンドPod)
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: myapp/api:latest
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5

7.3 Kubernetes Ingress

Ingressは、L7レベルのロードバランシングとルーティングを提供します。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: api.example.com
http:
paths:
# パスベースルーティング
- path: /api/v1
pathType: Prefix
backend:
service:
name: api-v1-service
port:
number: 80
- path: /api/v2
pathType: Prefix
backend:
service:
name: api-v2-service
port:
number: 80
tls:
- hosts:
- api.example.com
secretName: api-tls-secret

まとめ

学んだこと

本章では、ロードバランシングの実践的な知識を学びました:

  • ロードバランシングの基本概念とアルゴリズム(ラウンドロビン、最小接続数、IPハッシュ等)
  • L4とL7ロードバランサーの違いと使い分け
  • Nginx、HAProxyを使用したロードバランサー設定
  • ヘルスチェックの仕組みとベストプラクティス
  • セッション維持とステートレス設計
  • コンテナ環境(Kubernetes)でのロードバランシング

重要なポイント

1. L4 vs L7 の使い分け
- HTTP/HTTPS → L7ロードバランサー(Nginx、HAProxy)
- TCP/UDP、非HTTP → L4ロードバランサー
- 超高速処理 → L4が有利

2. ヘルスチェックの設計
- 軽量なエンドポイント(/health)
- 依存サービスのチェック(データベース、キャッシュ等)
- 適切なタイムアウト設定

3. ステートレス設計(推奨)
- セッション維持に依存しない
- トークンベース認証(JWT等)
- 外部セッションストア(Redis、Memcached等)

4. パスベースルーティングの活用
- マイクロサービス統合
- バージョニング(/api/v1、/api/v2)
- サービス分離

次のステップ

参考リンク