load-balancing について
load-balancing は、アプリケーションのトラフィックを複数サーバーに分散するためのスキルです。開発者や運用担当者向けに、nginx、HAProxy、AWS ロードバランサーの設定、アルゴリズム、トラブルシューティングを扱います。
主な機能
- 01nginx と HAProxy の設定
- 02AWS ALB と NLB の構築
- 03トラフィック分散アルゴリズムの選択
- 04ヘルスチェックとセッション維持の設定
- 05TLS、タイムアウト、バックエンド障害の調査
活用シーン
- 01HTTP/HTTPSトラフィックを複数サーバーに分散
- 02ヘルスチェックで不健全なインスタンスを回避
- 03ブルーグリーンやカナリアリリースで流量を調整
- 04ロードバランサーでTLSを終端
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ bagelhole/ devops-security-agent-skills --skill load-balancingインストール後、Claude Code で/load-balancingを入力して読み込まれたか確認します。
詳細
ロードバランシング
高可用性、スケーラビリティ、フォールトトレランスを実現するため、アプリケーションサーバー間でトラフィックを分散します。
使用する場面
- 複数のバックエンドサーバー間で HTTP/HTTPS トラフィックを分散する場合。
- ヘルスチェックを実装し、正常でないインスタンスを迂回してルーティングする場合。
- 証明書管理を簡素化するため、ロードバランサーで TLS を終端する場合。
- トラフィックを切り替えて、ブルー/グリーンデプロイやカナリアデプロイを有効にする場合。
- 単一のエントリーポイントの背後で水平スケーリングする場合。
前提条件
- 同じアプリケーションを実行するバックエンドサーバーが 2 台以上あること。
- HTTPS の終端に使用する TLS 証明書(ACM、Let's Encrypt、または内部向けの自己署名証明書)。
- AWS の場合:アベイラビリティーゾーンにまたがるパブリックサブネットとプライベートサブネットを含む VPC。
- nginx/HAProxy の場合:root アクセス権のある Linux サーバー。
nginx ロードバランサー
基本的なラウンドロビン
# /etc/nginx/conf.d/loadbalancer.conf
upstream app_backend {
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
server {
listen 80;
server_name app.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name app.example.com;
ssl_certificate /etc/ssl/certs/app.example.com.pem;
ssl_certificate_key /etc/ssl/private/app.example.com-key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://app_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;
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
}
location /health {
access_log off;
return 200 "OK";
}
}
重み付けサーバーとバックアップサーバー
upstream app_backend {
least_conn; # Route to server with fewest active connections
server 10.0.1.10:8080 weight=5; # Gets 5x traffic
server 10.0.1.11:8080 weight=3; # Gets 3x traffic
server 10.0.1.12:8080 weight=1; # Gets 1x traffic
server 10.0.1.20:8080 backup; # Only used when others are down
server 10.0.1.21:8080 down; # Temporarily removed from pool
}
ヘルスチェック(nginx Plus / OpenResty)
upstream app_backend {
zone backend 64k; # Shared memory zone for health data
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
# Health check (requires nginx Plus or third-party module)
# match healthy {
# status 200;
# body ~ "OK";
# }
# health_check interval=5s fails=3 passes=2 match=healthy;
スティッキーセッション(IP ハッシュ)
upstream app_backend {
ip_hash; # Same client IP always goes to the same server
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
HAProxy の設定
本番環境向けの完全な設定
# /etc/haproxy/haproxy.cfg
global
log /dev/log local0
maxconn 4096
daemon
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
tune.ssl.default-dh-param 2048
defaults
mode http
log global
option httplog
option dontlognull
option forwardfor
timeout connect 5s
timeout client 30s
timeout server 30s
timeout http-request 10s
timeout http-keep-alive 5s
retries 3
frontend http_front
bind *:80
redirect scheme https code 301 if !{ ssl_fc }
frontend https_front
bind *:443 ssl crt /etc/ssl/certs/app.example.com.pem
http-request set-header X-Forwarded-Proto https
# Route based on path
acl is_api path_beg /api/
acl is_ws path_beg /ws/
use_backend api_servers if is_api
use_backend ws_servers if is_ws
default_backend web_servers
backend web_servers
balance roundrobin
option httpchk GET /health HTTP/1.1\r\nHost:\ app.example.com
http-check expect status 200
cookie SERVERID insert indirect nocache
server web1 10.0.1.10:8080 check inter 5s fall 3 rise 2 cookie web1
server web2 10.0.1.11:8080 check inter 5s fall 3 rise 2 cookie web2
server web3 10.0.1.12:8080 check inter 5s fall 3 rise 2 cookie web3
backend api_servers
balance leastconn
option httpchk GET /api/health
http-check expect status 200
server api1 10.0.2.10:8080 check inter 5s fall 3 rise 2
server api2 10.0.2.11:8080 check inter 5s fall 3 rise 2
backend ws_servers
balance source
option httpchk GET /health
timeout tunnel 1h
server ws1 10.0.3.10:8080 check inter 5s fall 3 rise 2
server ws2 10.0.3.11:8080 check inter 5s fall 3 rise 2
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 10s
stats admin if LOCALHOST
HAProxy の管理
# Test config before reloading
haproxy -c -f /etc/haproxy/haproxy.cfg
# Reload without dropping connections
sudo systemctl reload haproxy
# View stats from CLI
echo "show stat" | sudo socat stdio /var/run/haproxy/admin.sock
# Drain a server (stop new connections, let existing finish)
echo "set server web_servers/web1 state drain" | sudo socat stdio /var/run/haproxy/admin.sock
# Set server to maintenance
echo "set server web_servers/web1 state maint" | sudo socat stdio /var/run/haproxy/admin.sock
# Re-enable server
echo "set server web_servers/web1 state ready" | sudo socat stdio /var/run/haproxy/admin.sock
AWS Application Load Balancer(ALB)
CLI を使用した ALB の作成
# Create the load balancer
aws elbv2 create-load-balancer \
--name my-app-alb \
--subnets subnet-aaa111 subnet-bbb222 \
--security-groups sg-xxx123 \
--type application \
--scheme internet-facing
# Create a target group
aws elbv2 create-target-group \
--name my-app-targets \
--protocol HTTP \
--port 8080 \
--vpc-id vpc-xxx123 \
--health-check-protocol HTTP \
--health-check-path /health \
--health-check-interval-seconds 15 \
--healthy-threshold-count 2 \
--unhealthy-threshold-count 3 \
--target-type instance
# Register targets
aws elbv2 register-targets \
--target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456:targetgroup/my-app-targets/abc123 \
--targets Id=i-0123456789abc Id=i-0987654321def
# Create HTTPS listener
aws elbv2 create-listener \
--load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-app-alb/abc123 \
--protocol HTTPS \
--port 443 \
--certificates CertificateArn=arn:aws:acm:us-east-1:123456:certificate/abc-123 \
--default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:us-east-1:123456:targetgroup/my-app-targets/abc123
# Create HTTP redirect listener
aws elbv2 create-listener \
--load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-app-alb/abc123 \
--protocol HTTP \
--port 80 \
--default-actions Type=redirect,RedirectConfig='{Protocol=HTTPS,Port=443,StatusCode=HTTP_301}'
ターゲットのヘルス状態を確認
# Check health of registered targets
aws elbv2 describe-target-health \
--target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456:targetgroup/my-app-targets/abc123
AWS Network Load Balancer(NLB)
# Create NLB (for TCP, UDP, or TLS traffic)
aws elbv2 create-load-balancer \
--name my-tcp-nlb \
--subnets subnet-aaa111 subnet-bbb222 \
--type network \
--scheme internet-facing
# Create TCP target group
aws elbv2 create-target-group \
--name my-tcp-targets \
--protocol TCP \
--port 5432 \
--vpc-id vpc-xxx123 \
--health-check-protocol TCP \
--target-type ip
Terraform を使用した ALB
resource "aws_lb" "app" {
name = "my-app-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = var.public_subnet_ids
enable_deletion_protection = true
}
resource "aws_lb_target_group" "app" {
name = "my-app-tg"
port = 8080
protocol = "HTTP"
vpc_id = var.vpc_id
health_check {
path = "/health"
port = "traffic-port"
healthy_threshold = 2
unhealthy_threshold = 3
timeout = 5
interval = 15
matcher = "200"
}
deregistration_delay = 30
stickiness {
type = "lb_cookie"
cookie_duration = 86400
enabled = true
}
}
resource "aws_lb_listener" "https" {
load_balancer_arn = aws_lb.app.arn
port = 443
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
certificate_arn = aws_acm_certificate.cert.arn
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.app.arn
}
}
resource "aws_lb_listener" "http_redirect" {
load_balancer_arn = aws_lb.app.arn
port = 80
protocol = "HTTP"
default_action {
type = "redirect"
redirect {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}
}
ロードバランシングのアルゴリズム
| アルゴリズム | 使用例 | nginx | HAProxy |
|---|---|---|---|
| ラウンドロビン | デフォルト、サーバー間で均等に分散 | (default) | balance roundrobin |
| 最少接続数 | リクエスト処理時間にばらつきがある場合 | least_conn | balance leastconn |
| IP ハッシュ | Cookie を使用せずにセッションを維持 | ip_hash | balance source |
| URI ハッシュ | URL ごとのキャッシュ局所性 | hash $request_uri | balance uri |
| 2 台をランダムに選択 | 大規模なサーバープール | random two least_conn | balance random(2) |
トラブルシューティング
| 症状 | 原因 | 対処方法 |
|---|---|---|
| すべてのバックエンドが「unhealthy」と表示される | ヘルスチェックのパスが 200 を返さない | /health エンドポイントが 200 を返すことを確認し、セキュリティグループを確認します |
| 502 Bad Gateway | バックエンドが稼働していない、またはポートが正しくない | バックエンドが設定されたポートでリッスンしていることを確認します |
| トラフィックの分散に偏りがある | スティッキーセッションまたは重み付け設定 | セッションアフィニティの設定を確認し、サーバーの重みを見直します |
| 接続がタイムアウトする | バックエンドの応答が遅い、またはタイムアウト値が低すぎる | proxy_read_timeout または HAProxy の timeout server を増やします |
| TLS ハンドシェイクに失敗する | 証明書が一致しない、または期限切れ | 証明書がドメインに一致することを確認し、期限切れの場合は更新します |
| ALB が 503 を返す | 正常なターゲットが登録されていない | ターゲットグループのヘルス状態を確認し、ターゲットが正しいサブネットにあることを確認します |
| WebSocket 接続が切断される | プロキシでアップグレードが設定されていない | proxy_set_header Upgrade と Connection "upgrade" を追加します |
関連スキル
- reverse-proxy - リバースプロキシの設定パターン
- dns-management - ロードバランサーを指す DNS レコード
- cdn-setup - ロードバランシングされたオリジンの前段に配置する CDN
- service-mesh - Kubernetes におけるサービスレベルのロードバランシング
コメント
Markdown 対応