NLBのヘルスチェックはなぜ「噛み合わない」のか? L4の冷徹さとL7の気遣いを解き明かす
クラウドインフラの現場で、AWSのNetwork Load Balancer(NLB)を触るたびに思うことがあります。「こいつは、ALBほどお節介ではないが、一度拗ねると原因特定がとてつもなく面倒だ」と。
ALBはHTTPヘッダーを解釈してよしなに振る舞う「賢いお兄さん」ですが、NLBはあくまでレイヤー4(L4)の「寡黙な門番」です。今回は、多くのエンジニアが運用現場で頭を抱える「NLBのヘルスチェック」の深淵に潜り込み、パケットレベルの挙動からトラブルシューティングの勘所までを叩き込みます。
—
1. NLBのヘルスチェック:TCPとHTTPの決定的な違い
NLBのヘルスチェック設定には、大きく分けて TCP と HTTP/HTTPS の2種類が存在します。ここを誤解していると、アプリケーション層で障害が起きているのに、NLBが「健康だ」と判断してトラフィックを送り続け、ユーザーにエラーを撒き散らす事態を招きます。
TCPヘルスチェック:レイヤー4の冷徹な握手
TCPヘルスチェックは、NLBがターゲットに対して SYN パケットを送り、SYN/ACK が返ってくるかを待ちます。
- 成功:
SYN/ACKが返れば成功。その後、NLBはRSTを投げて接続を終了します。 - 失敗:
SYNを投げても返事がない、あるいはターゲットがRSTを返してきた場合に「異常」とみなします。
これは「ポートが開いているか」を確認するだけで、アプリケーションが正しく処理を行っているか(例えばデータベース接続が詰まっていないか)までは関知しません。
HTTP/HTTPSヘルスチェック:L4の殻を被ったL7の気遣い
NLBのヘルスチェックで HTTP を選ぶと、NLBの各ノードがターゲットに対して GET リクエストを投げます。
- 成功:
200 OKが返ってくれば健康。 - 失敗:
200以外、あるいはタイムアウトすると異常。
「え、NLBはL4なのにHTTPができるの?」と疑問に思うかもしれません。これは、NLBの各ノードがターゲットに対して、ユーザーからのリクエストとは別に、ヘルスチェック専用のHTTPクライアントとして振る舞っているからです。
—
2. 現場で役立つヘルスチェックのパラメーター設計
運用の現場では、以下の3つのパラメーターをチューニングするだけで、過敏な再起動(フラッピング)を防ぐことができます。
- Interval (間隔): 何秒おきにチェックするか(デフォルト30秒)。
- Healthy threshold (正常しきい値): 何回連続で成功したら健康とみなすか。
- Unhealthy threshold (異常しきい値): 何回連続で失敗したら異常とみなすか。
【シニアの教訓】
本番環境では、Interval を短くしすぎないでください。例えば 10秒 に設定し、Unhealthy threshold を 2 にすると、わずか20秒でターゲットが切り離されます。一時的なネットワークの揺らぎで全台が落ちる、いわゆる「自爆」を防ぐため、この値はアプリケーションの許容度に合わせて慎重に決めるべきです。
—
3. トラブルシューティング:パケットはどこで死んでいるのか
「ヘルスチェックが通らない」と連絡が来たら、まずはターゲット内で以下のコマンドを叩き、自分自身に対してヘルスチェックリクエストを模倣してみましょう。
# Pythonで簡易HTTPサーバーを立ち上げ、NLBの代わりに自分を叩く(ポート8080の例)
python3 -m http.server 8080
# 別ターミナルからcurlで擬似リクエストを送る
# -v をつけてヘッダーやレスポンスを確認するのが鉄則
curl -v http://localhost:8080/health-check
ここで 200 OK が返るのにNLBで失敗する場合、原因はほぼ間違いなく 「セキュリティグループ」か「ルーティング」 です。
よくある地雷ポイント
1. セキュリティグループの穴: NLBの各AZにはIPアドレスがあります。ターゲットのセキュリティグループが 0.0.0.0/0 を許可していない場合、NLBからのヘルスチェックパケットが遮断されます。
2. ターゲットのレスポンス速度: アプリケーションが重すぎて、Timeout 期間内に 200 OK を返せていないケース。
3. ALBと混同しない: NLBのヘルスチェックは「ターゲットのポート」に対して行われます。ターゲットが複数ある場合、各ターゲットが個別にヘルスチェックに応答できる必要があります。
—
4. Terraformでの推奨設定例
インフラをコードで管理する際、以下のように設定を記述するのがベストプラクティスです。
resource "aws_lb_target_group" "api_tg" {
name = "api-target-group"
port = 8080
protocol = "TCP" # L4で負荷分散する場合
vpc_id = var.vpc_id
target_type = "instance"
health_check {
enabled = true
protocol = "HTTP" # ヘルスチェックはL7で行うのが安全
port = 8080
path = "/health" # アプリ側でこのエンドポイントを実装しておくこと
interval = 30
healthy_threshold = 3
unhealthy_threshold = 3
matcher = "200" # 明示的に200以外を異常と定義
}
}
【実務Tips】
アプリケーション側の /health エンドポイントでは、単に 200 を返すだけでなく、「DBへの接続確認」や「キャッシュサーバーの生存確認」 を含めることを強く推奨します。これが真の「サービスが生きているか」を判定する指標になります。
—
最後に:ネットワークは裏切らない
NLBは、設定さえ正しく行えば非常に堅牢で高速なロードバランサーです。ALBとの最大の違いは、「HTTPの複雑さを排除した、純粋なTCP/UDPの高速な運び屋」であるという点です。
ヘルスチェックで躓いたときは、curl で叩く、セキュリティグループを確認する、そしてアプリケーションのログを信じる。この地道なプロセスの先に、安定したインフラが待っています。皆さんの設計が、今日もパケットの如く軽快に動くことを願っています。
コメント