Chuyển đến nội dung chính

第5課:ロードバランシング

Nginxにおけるロードバランシングの解説 — アルゴリズム(round-robin、least_conn、ip_hash、hash)、 upstreamブロックの詳細設定、バックアップサーバー、重み、スティッキーセッション、ヘルスチェック。 ハイアベイラビリティとパフォーマンス最適化のためのロードバランサー設定ガイド(実例付き)。

🔒 DevSecOps — 第5課 第5課:ロードバランシング Nginxの基礎から応用まで 第2部:リバースプロキシ & ロードバランシング xdev.asia

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

ロードバランシングは、リソース使用率の最適化、スループット向上、レイテンシ削減、高可用性確保のために、複数のサーバーにトラフィックを分散させる技術です。

1.1. Round-Robin(デフォルト)

Round-robinは、各サーバーへのリクエストをラウンドロビン形式で順番に振り分けます。

設定:

upstream backend {
    # Round-robinはデフォルト、宣言不要
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

server { listen 80; server_name example.com;

location / {
    proxy_pass http://backend;
}

}

動作の仕組み:

リクエスト1 → backend1
リクエスト2 → backend2
リクエスト3 → backend3
リクエスト4 → backend1(繰り返し)
リクエスト5 → backend2
リクエスト6 → backend3
...

メリット:

  • シンプルで実装が簡単
  • リクエストを均等に分散
  • 状態/セッション追跡が不要

デメリット:

  • サーバーの現在の負荷を考慮しない
  • サーバーの処理能力が異なる場合に不向き
  • セッションアフィニティを維持しない

ユースケース:

  • ステートレスアプリケーション
  • 同一構成のサーバー群
  • シンプルな負荷分散

詳細な例:

upstream web_backend {
server web1.example.com:8080;
server web2.example.com:8080;
server web3.example.com:8080;
server web4.example.com:8080;
}

server { listen 80; server_name www.example.com;

access_log /var/log/nginx/loadbalancer.log;

location / {
    proxy_pass http://web_backend;
    
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    
    add_header X-Upstream-Server $upstream_addr always;
}

}

1.2. 最小コネクション(least_conn)

最小コネクションは、アクティブな接続数が最も少ないサーバーにリクエストをルーティングします。

設定:

upstream backend {
least_conn;  # 最小コネクションアルゴリズムを有効化

server backend1.example.com;
server backend2.example.com;
server backend3.example.com;

}

server { listen 80;

location / {
    proxy_pass http://backend;
}

}

動作の仕組み:

初期状態:
backend1: 0接続
backend2: 0接続
backend3: 0接続

リクエスト1 → backend1(0接続)→ backend1: 1接続 リクエスト2 → backend2(0接続)→ backend2: 1接続 リクエスト3 → backend3(0接続)→ backend3: 1接続

backend1完了 → backend1: 0接続 リクエスト4 → backend1(0接続、最小)

メリット:

  • リクエスト処理時間が異なる場合に優れた負荷分散
  • ビジー/アイドルサーバーに自動調整
  • 長時間接続に適している

デメリット:

  • Round-robinより追跡オーバーヘッドが大きい
  • セッションアフィニティを維持しない

ユースケース:

  • リクエスト処理時間が変動するアプリケーション
  • 長時間接続(ストリーミング、ダウンロード)
  • 処理能力の異なるサーバー群

監視付きの例:

upstream api_backend {
least_conn;

server api1.example.com:3000 max_fails=3 fail_timeout=30s;
server api2.example.com:3000 max_fails=3 fail_timeout=30s;
server api3.example.com:3000 max_fails=3 fail_timeout=30s;

keepalive 32;
keepalive_timeout 60s;

}

server { listen 80; server_name api.example.com;

location / {
    proxy_pass http://api_backend;
    
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

}

1.3. IP ハッシュ(ip_hash)

IP ハッシュは、同じクライアント IP からのリクエストを常に同じサーバーにルーティングします(スティッキーセッション)。

設定:

upstream backend {
ip_hash;  # IPベースのスティッキーセッションを有効化

server backend1.example.com;
server backend2.example.com;
server backend3.example.com;

}

server { listen 80;

location / {
    proxy_pass http://backend;
}

}

動作の仕組み:

クライアントIP: 192.168.1.100
Hash(192.168.1.100) → backend2
→ 192.168.1.100からのリクエストはすべてbackend2へ

クライアントIP: 192.168.1.101 Hash(192.168.1.101) → backend1 → 192.168.1.101からのリクエストはすべてbackend1へ

メリット:

  • セッションアフィニティ(同じクライアント → 同じサーバー)
  • 共有セッションストレージが不要
  • シンプルで効率的

デメリット:

  • クライアント数が少ない場合に分散が不均一
  • NAT/プロキシ配下では機能しにくい
  • サーバー障害時にセッションが失われる

ユースケース:

  • セッションベースのアプリケーション
  • 状態維持が必要なアプリケーション
  • ショッピングカート、ユーザーセッション

ip_hashの重要な注意点:

upstream backend {
ip_hash;

server backend1.example.com;
server backend2.example.com;
server backend3.example.com;

# ip_hashではweightを使用しない
# server backend1.example.com weight=3;  # ← 誤り!

# backupは使用可能
server backend4.example.com backup;

# downマークも使用可能
server backend5.example.com down;

}

1.4. ジェネリックハッシュ(hash)

ジェネリックハッシュは、任意の変数に基づいてハッシュを計算できます。

基本設定:

upstream backend {
hash $request_uri consistent;

server backend1.example.com;
server backend2.example.com;
server backend3.example.com;

}

同じURI → 同じサーバー

/api/users → 常にbackend2

/api/products → 常にbackend1

さまざまな変数でのハッシュ:

# 1. URIでハッシュ(キャッシュフレンドリー)
upstream cache_backend {
hash $request_uri consistent;

server cache1.example.com;
server cache2.example.com;
server cache3.example.com;

}

2. Cookieでハッシュ

upstream cookie_backend { hash $cookie_sessionid consistent;

server app1.example.com;
server app2.example.com;

}

3. カスタムヘッダーでハッシュ

upstream custom_backend { hash $http_x_tenant_id consistent;

server tenant1.example.com;
server tenant2.example.com;

}

4. クエリパラメータでハッシュ

upstream param_backend { hash $arg_user_id consistent;

server user1.example.com;
server user2.example.com;

}

メリット:

  • 柔軟性が高い(任意の変数でハッシュ可能)
  • コンシステントハッシュによりキャッシュ無効化を最小化
  • キャッシング戦略に最適

デメリット:

  • 他の方法より複雑
  • ハッシュキーの分散を理解する必要がある

ユースケース:

  • キャッシュレイヤー(CDN、プロキシキャッシュ)
  • マルチテナントアプリケーション
  • シャーディング戦略

1.5. ランダム

ランダムは、サーバーをランダムに選択します(オプションで重み付き)。

設定:

upstream backend {
random;  # または: random two least_conn;

server backend1.example.com;
server backend2.example.com;
server backend3.example.com;

}

メリット:

  • シンプル
  • 多数のサーバーでの良好な分散
  • 低オーバーヘッド

デメリット:

  • 予測不可能
  • セッションアフィニティを維持しない

2. upstreamブロックの詳細設定

2.1. 基本的なupstream設定

upstream backend {
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
}

server { listen 80;

location / {
    proxy_pass http://backend;
}

}

2.2. 複数ポートのサーバー

upstream multi_port_backend {
server backend.example.com:8080;
server backend.example.com:8081;
server backend.example.com:8082;
}

2.3. Unixソケット接続

upstream socket_backend {
server unix:/var/run/app1.sock;
server unix:/var/run/app2.sock;
server unix:/var/run/app3.sock;
}

server { listen 80;

location / {
    proxy_pass http://socket_backend;
}

}

2.4. IPv6サポート

upstream ipv6_backend {
server [2001:db8::1]:8080;
server [2001:db8::2]:8080;
server [2001:db8::3]:8080;
}

2.5. 混在設定

upstream mixed_backend {
# TCPサーバー
server backend1.example.com:8080;
server 192.168.1.100:8080;

# Unixソケット
server unix:/var/run/app.sock;

# IPv6
server [2001:db8::1]:8080;

}


3. バックアップサーバーと重み

3.1. 重み(Weight)

重みは、各サーバーが受け取るリクエストの割合を決定します。

基本設定:

upstream backend {
server backend1.example.com weight=3;  # トラフィックの60%
server backend2.example.com weight=1;  # トラフィックの20%
server backend3.example.com weight=1;  # トラフィックの20%
}

合計重み = 3 + 1 + 1 = 5

backend1: 3/5 = 60%

backend2: 1/5 = 20%

backend3: 1/5 = 20%

ユースケース1:処理能力の異なるサーバー

upstream capacity_backend {
# 大容量サーバー - 50%のトラフィック
server large.example.com weight=5;

# 中容量サーバー - それぞれ25%
server medium1.example.com weight=2.5;
server medium2.example.com weight=2.5;

}

ユースケース2:カナリーデプロイ

upstream canary_backend {
# 本番サーバー - 90%のトラフィック
server prod1.example.com weight=45;
server prod2.example.com weight=45;

# カナリーサーバー - 10%のトラフィック
server canary.example.com weight=10;

}

server { listen 80; server_name app.example.com;

location / {
    proxy_pass http://canary_backend;
    
    add_header X-Upstream-Server $upstream_addr always;
}

}

ユースケース3:ブルー/グリーンデプロイ

upstream bluegreen_backend {
# Blue(現行)- 最初は100%
server blue.example.com weight=10;

# Green(新規)- 最初は0%
server green.example.com weight=0;

}

段階的な移行:

ステップ1: weight=10 / weight=0 (100% blue)

ステップ2: weight=9 / weight=1 (90% blue, 10% green)

ステップ3: weight=5 / weight=5 (それぞれ50%)

ステップ4: weight=1 / weight=9 (10% blue, 90% green)

ステップ5: weight=0 / weight=10 (100% green)

重要:ip_hashではweightが機能しない

upstream bad_config {
ip_hash;

# ip_hashではweightは無視される!
server backend1.example.com weight=3;  # ← 効果なし!
server backend2.example.com weight=1;

}

3.2. バックアップサーバー

バックアップサーバーは、すべてのプライマリサーバーがダウンした場合のみトラフィックを受け取ります。

基本設定:

upstream backend {
server backend1.example.com;
server backend2.example.com;
server backend3.example.com;

# バックアップサーバー
server backup1.example.com backup;
server backup2.example.com backup;

}

動作の仕組み:

通常運用時:

  • backend1、backend2、backend3がトラフィックを処理
  • backup1、backup2はアイドル状態

backend1が障害:

  • backend2、backend3がトラフィックを処理
  • backup1、backup2はまだアイドル

すべてのプライマリが障害:

  • backup1、backup2がトラフィックを処理

backend1が回復:

  • トラフィックがbackend1に戻る

  • backup1、backup2は再びアイドルへ

メンテナンスモードの例:

upstream maintenance_backend {
server prod1.example.com max_fails=3 fail_timeout=30s;
server prod2.example.com max_fails=3 fail_timeout=30s;
server prod3.example.com max_fails=3 fail_timeout=30s;

メンテナンスページサーバー

server maintenance.example.com:8080 backup; }

server { listen 80; server_name app.example.com;

location / {
    proxy_pass http://maintenance_backend;
    
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

}

3.3. 重みとバックアップの組み合わせ

シナリオ:本番 + ステージング + 緊急

upstream prod_staging_emergency {
# 本番サーバー - メインのトラフィック
server prod1.example.com weight=5 max_fails=3 fail_timeout=30s;
server prod2.example.com weight=5 max_fails=3 fail_timeout=30s;

# ステージングサーバー - バックアップ
server staging.example.com weight=2 backup max_fails=5;

# 緊急静的サーバー - 最後の手段
server emergency.example.com backup;

}


4. スティッキーセッション

4.1. IPハッシュ方式(組み込み)

upstream backend {
ip_hash;

server backend1.example.com;
server backend2.example.com;
server backend3.example.com;

}

ip_hashの制限:

  • NAT配下では機能しにくい
  • 分散が不均一
  • 柔軟性が低い
upstream backend {
hash $cookie_sessionid consistent;

server backend1.example.com;
server backend2.example.com;
server backend3.example.com;

}

server { listen 80;

location / {
    proxy_pass http://backend;
    
    proxy_set_header Cookie $http_cookie;
}

}

4.3. カスタムセッション管理

バックエンドでセッションCookieを作成:

// Node.js の例
app.use((req, res, next) => {
if (!req.cookies.server_id) {
// サーバー識別子でCookieを設定
res.cookie('server_id', process.env.SERVER_ID, {
maxAge: 3600000,
httpOnly: true
});
}
next();
});

CookieベースのNginxルーティング:

map $cookie_server_id $backend_server {
"server1" "backend1.example.com:8080";
"server2" "backend2.example.com:8080";
"server3" "backend3.example.com:8080";
default   "backend1.example.com:8080";
}

server { listen 80;

location / {
    proxy_pass http://$backend_server;
    
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Cookie $http_cookie;
}

}

4.4. ヘッダーによるセッションアフィニティ

upstream backend {
hash $http_x_session_id consistent;

server backend1.example.com;
server backend2.example.com;
server backend3.example.com;

}

server { listen 80;

location / {
    proxy_pass http://backend;
    
    proxy_set_header X-Session-ID $http_x_session_id;
}

}


5. ヘルスチェック

5.1. パッシブヘルスチェック(オープンソース)

upstream backend {
server backend1.example.com max_fails=3 fail_timeout=30s;
server backend2.example.com max_fails=3 fail_timeout=30s;
server backend3.example.com max_fails=3 fail_timeout=30s;
}

max_fails=3: 3回失敗でダウンとマーク

fail_timeout=30s: 30秒後に再試行

詳細なパラメータ:

upstream detailed_health {
server backend1.example.com
max_fails=5          # ダウンとマークするまでの失敗回数
fail_timeout=60s     # 再試行までの待機時間
max_conns=1000;      # 最大同時接続数

server backend2.example.com
    max_fails=3
    fail_timeout=30s;

server backend3.example.com backup;

}

5.2. ヘルスチェックエンドポイント

バックエンドのヘルスエンドポイント(Node.js):

const express = require('express');
const app = express();

app.get('/health', (req, res) => { const dbOk = checkDatabase(); const memUsage = process.memoryUsage(); const memOk = memUsage.heapUsed < 500 * 1024 * 1024; const depsOk = checkDependencies();

if (dbOk && memOk && depsOk) {
    res.status(200).json({
        status: 'healthy',
        timestamp: new Date().toISOString(),
        uptime: process.uptime()
    });
} else {
    res.status(503).json({
        status: 'unhealthy',
        database: dbOk,
        memory: memOk,
        dependencies: depsOk
    });
}

});

app.listen(3000);

Nginxのヘルスチェックルーティング:

upstream backend {
server backend1.example.com:3000 max_fails=3 fail_timeout=30s;
server backend2.example.com:3000 max_fails=3 fail_timeout=30s;
server backend3.example.com:3000 max_fails=3 fail_timeout=30s;
}

server { listen 80; server_name example.com;

location / {
    proxy_pass http://backend;
}

# ヘルスチェックエンドポイント(内部のみ)
location /health {
    access_log off;
    proxy_pass http://backend/health;
    
    allow 127.0.0.1;
    allow 10.0.0.0/8;
    deny all;
}

}

5.3. 外部ヘルスチェックスクリプト

監視スクリプト(Python):

#!/usr/bin/env python3

health_monitor.py

import requests import time from datetime import datetime

BACKENDS = [ 'http://backend1.example.com:3000/health', 'http://backend2.example.com:3000/health', 'http://backend3.example.com:3000/health', ]

CHECK_INTERVAL = 10 # 秒 UNHEALTHY_THRESHOLD = 3

backend_fail_counts = {url: 0 for url in BACKENDS}

def check_health(url): try: response = requests.get(url, timeout=5) if response.status_code == 200: data = response.json() return data.get('status') == 'healthy' return False except Exception as e: print(f"エラー {url}: {e}") return False

def main(): while True: for url in BACKENDS: healthy = check_health(url)

        if healthy:
            if backend_fail_counts[url] > 0:
                print(f"{datetime.now()} - {url} 回復")
            backend_fail_counts[url] = 0
        else:
            backend_fail_counts[url] += 1
            print(f"{datetime.now()} - {url} 不健全 "
                  f"({backend_fail_counts[url]}回連続失敗)")
            
            if backend_fail_counts[url] >= UNHEALTHY_THRESHOLD:
                print(f"{url} をダウンとしてマーク")
    
    time.sleep(CHECK_INTERVAL)

if name == 'main': main()


6. 実際の例

6.1. 高トラフィックWebアプリケーション

upstream web_app {
least_conn;

server app1.example.com:8080 weight=5 max_fails=3 fail_timeout=30s max_conns=1000;
server app2.example.com:8080 weight=5 max_fails=3 fail_timeout=30s max_conns=1000;
server app3.example.com:8080 weight=3 max_fails=3 fail_timeout=30s max_conns=800;

server backup.example.com:8080 backup;

keepalive 128;
keepalive_timeout 90s;
keepalive_requests 1000;

}

server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name www.example.com;

ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;

location / {
    proxy_pass http://web_app;
    
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;
    
    proxy_buffering on;
    proxy_buffer_size 8k;
    proxy_buffers 16 8k;
    
    add_header X-Upstream-Server $upstream_addr always;
    add_header X-Upstream-Response-Time $upstream_response_time always;
}

location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2)$ {
    root /var/www/static;
    expires 1y;
    add_header Cache-Control "public, immutable";
    access_log off;
}

}

6.2. マイクロサービスのAPIゲートウェイ

# ユーザーサービス
upstream user_service {
least_conn;
server user1.internal:8001 max_fails=2 fail_timeout=20s;
server user2.internal:8001 max_fails=2 fail_timeout=20s;
keepalive 32;
}

プロダクトサービス

upstream product_service { least_conn; server product1.internal:8002 max_fails=2 fail_timeout=20s; server product2.internal:8002 max_fails=2 fail_timeout=20s; keepalive 32; }

オーダーサービス(カートのためにスティッキーセッション)

upstream order_service { ip_hash; server order1.internal:8003 max_fails=2 fail_timeout=20s; server order2.internal:8003 max_fails=2 fail_timeout=20s; keepalive 32; }

決済サービス(重要 - 高い冗長性)

upstream payment_service { least_conn; server payment1.internal:8004 weight=3 max_fails=1 fail_timeout=10s; server payment2.internal:8004 weight=3 max_fails=1 fail_timeout=10s; server payment3.internal:8004 weight=2 max_fails=1 fail_timeout=10s; server payment_backup.internal:8004 backup; keepalive 64; }

limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s; limit_req_zone $binary_remote_addr zone=payment_limit:10m rate=10r/s;

server { listen 443 ssl http2; server_name api.example.com;

proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

location /api/v1/users {
    limit_req zone=api_limit burst=50 nodelay;
    proxy_pass http://user_service;
    proxy_read_timeout 30s;
}

location /api/v1/products {
    limit_req zone=api_limit burst=50 nodelay;
    proxy_pass http://product_service;
    proxy_read_timeout 30s;
}

location /api/v1/orders {
    limit_req zone=api_limit burst=30 nodelay;
    proxy_pass http://order_service;
    proxy_read_timeout 60s;
}

location /api/v1/payments {
    limit_req zone=payment_limit burst=5 nodelay;
    proxy_pass http://payment_service;
    proxy_read_timeout 90s;
    proxy_connect_timeout 10s;
}

}


7. 練習課題

課題1:基本的なロードバランシング

  1. ポート3000、3001、3002でアプリケーションの3インスタンスを起動
  2. ラウンドロビンでNginxを設定
  3. トラフィックを生成して分散を確認(ログをチェック)

課題2:重み付きロードバランシング

  1. 重み5、3、2で3つのバックエンドサーバーを設定
  2. 100リクエストを生成
  3. サーバーごとのリクエスト数を数えて比率を確認(約50%、30%、20%)

課題3:スティッキーセッション

  1. ip_hashロードバランシングを設定
  2. 同じクライアントから複数リクエストを送信
  3. すべてのリクエストが同じバックエンドに届くことを確認

課題4:バックアップサーバー

  1. 2つのプライマリサーバーと1つのバックアップを設定
  2. 両方のプライマリを停止
  3. トラフィックがバックアップに切り替わることを確認
  4. プライマリを再起動してトラフィックが戻ることを確認

課題5:ヘルスチェック

  1. max_fails=2 fail_timeout=30sでバックエンドを設定
  2. /healthエンドポイントを設定
  3. バックエンド障害をシミュレート(サービス停止)
  4. ログを監視してフェイルオーバーを確認
  5. サービスを再起動して回復を確認

課題6:カナリーデプロイ

  1. 本番サーバーを設定(各weight=45)
  2. カナリーサーバーを追加(weight=10)
  3. 10%のトラフィックがカナリーに行くことを確認
  4. カナリーの重みを徐々に増加
  5. カナリーへの完全移行を完了

8. トラブルシューティング

8.1. 不均一な分散

問題:

サーバー1: 1000リクエスト
サーバー2: 500リクエスト
サーバー3: 300リクエスト

診断:

# 重みを確認
grep -A10 "upstream" /etc/nginx/nginx.conf

少数のクライアントでip_hashを使用していないか確認

サーバーの状態/パフォーマンスを確認

解決策:

# round-robinの代わりにleast_connを使用
upstream backend {
least_conn;
server backend1.example.com;
server backend2.example.com;
server backend3.example.com;
}

8.2. スティッキーセッションが機能しない

解決策:

# ip_hashの代わりにCookieでハッシュを使用
upstream backend {
hash $cookie_sessionid consistent;
server backend1.example.com;
server backend2.example.com;
}

8.3. ヘルスチェックが障害を検知しない

解決策:

# しきい値を下げる
upstream backend {
server backend1.example.com max_fails=2 fail_timeout=10s;
server backend2.example.com max_fails=2 fail_timeout=10s;
}

8.4. バックエンドタイムアウトの問題

問題: 504 Gateway Timeout エラー

解決策:

location / {
proxy_pass http://backend;

proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 120s;

}

8.5. 接続プールの枯渇

問題: バックエンドへの接続が多すぎる、高負荷時の502エラー

解決策:

upstream backend {
server backend1.example.com max_conns=1000;
server backend2.example.com max_conns=1000;

keepalive 128;
keepalive_timeout 75s;
keepalive_requests 1000;

}

server { location / { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ""; } }


9. ベストプラクティス

9.1. 設定

# 1. 分かりやすいupstream名を使用
upstream user_api_backend {  # 良い例
# ...
}

2. 設定をドキュメント化

upstream payment_service { # 処理時間が変動するためleast_connを使用 # 重要なパスには厳格なヘルスチェックを設定 least_conn;

server payment1.internal:8080 weight=3 max_fails=1 fail_timeout=10s;
server payment2.internal:8080 weight=3 max_fails=1 fail_timeout=10s;
server payment_backup.internal:8080 backup;

keepalive 32;

}

3. 懸念事項を分離

include /etc/nginx/conf.d/upstreams/.conf; include /etc/nginx/conf.d/servers/.conf;

9.2. パフォーマンス

upstream optimized_backend {
# 1. 良好な分散のためleast_connを使用
least_conn;

# 2. keepaliveを有効化
keepalive 128;
keepalive_timeout 75s;
keepalive_requests 1000;

# 3. 適切なmax_connsを設定
server backend1.example.com max_conns=1000;
server backend2.example.com max_conns=1000;

}

server { location / { proxy_pass http://optimized_backend;

    # 4. keepaliveでHTTP/1.1を使用
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    
    # 5. バッファリングを有効化
    proxy_buffering on;
    proxy_buffer_size 8k;
    proxy_buffers 16 8k;
}

}

9.3. 監視

# upstream情報のログ記録
log_format upstream_log '$remote_addr - [$time_local] '
'"$request" $status '
'upstream: $upstream_addr '
'response_time: $upstream_response_time '
'connect_time: $upstream_connect_time';

access_log /var/log/nginx/upstream.log upstream_log;

デバッグヘッダーの追加(本番環境以外)

add_header X-Upstream-Server $upstream_addr always; add_header X-Upstream-Response-Time $upstream_response_time always;

stub_statusの有効化

server { listen 8080; location /nginx_status { stub_status; allow 127.0.0.1; deny all; } }

9.4. セキュリティ

# 1. サーバーごとの最大接続数を制限
upstream backend {
server backend1.example.com max_conns=1000;
}

2. タイムアウトを設定

proxy_connect_timeout 10s; proxy_send_timeout 60s; proxy_read_timeout 60s;

3. upstreamエラーを隠す

proxy_intercept_errors on; error_page 502 503 504 /50x.html;

4. レートリミット

limit_req_zone $binary_remote_addr zone=backend_limit:10m rate=100r/s;

location / { limit_req zone=backend_limit burst=50 nodelay; proxy_pass http://backend; }


まとめ

この課では以下を学びました:

  • ✅ ロードバランシングアルゴリズム(round-robin、least_conn、ip_hash、hash、random)
  • ✅ upstreamブロックの詳細設定
  • ✅ バックアップサーバーと重み分散
  • ✅ スティッキーセッション戦略
  • ✅ アクティブ/パッシブヘルスチェック
  • ✅ 実際のユースケースとベストプラクティス

次の課: キャッシング — 静的コンテンツ、APIレスポンスのキャッシュ方法と、プロキシキャッシュによるパフォーマンス最適化を解説します。