ロードバランサ(LB)配下のシステムで、
「通信が突然切れる」「一定時間後にRSTが返る」「再接続が頻発する」
といった問題が発生する場合、LBのidle timeoutとアプリのKeepAlive設計不整合が原因であるケースが非常に多くあります。
本記事では、LB idle timeoutの仕組みから、よくある設計ミス、現場での切り分け方法、
“どう設計すれば事故らないか”まで実務目線で解説します。
LB idle timeoutとは何か
LBの idle timeout とは、
- TCP接続が確立された後
- 一定時間データ転送が行われない場合
その接続を LBが自動的に切断するまでの待ち時間 です。
代表的な例:
- AWS ALB:60秒(デフォルト)
- AWS NLB:350秒(TCP idle timeout)
- Azure Load Balancer:240秒
重要なのは、アプリ側が生きていてもLBは容赦なく切るという点です。
KeepAliveとは何か(アプリ側視点)
KeepAliveとは、既存のTCP接続を再利用する仕組みです。
- HTTP Keep-Alive
- DBコネクションプール
- APIクライアントの常時接続
アプリ側は、
「接続はまだ生きているはず」
という前提で通信を行います。
しかし、LBが先に切っていると、 アプリが次にデータを送った瞬間に RST を受け取ります。
よくある落とし穴①:LB idle timeout < アプリKeepAlive
最も多い事故パターンです。
- LB idle timeout:60秒
- アプリKeepAlive:300秒
この状態では、
- 通信が60秒止まる
- LBが接続を切断
- アプリは気付かず接続再利用
- RSTを受信してエラー
結果として、
- ランダムな通信エラー
- 再試行ループ
- ユーザー体感の不安定さ
が発生します。
よくある落とし穴②:LB側からのRSTをアプリ障害と誤認
ログ上では以下のように見えることがあります。
- Broken pipe
- Connection reset by peer
- EOFException
このため、
「アプリが落ちている」
「Javaが不安定」
「ネットワーク障害」
と誤診されやすいのが特徴です。
実際には、LBが仕様通りidle timeoutで切っているだけというケースが非常に多いです。
切り分け手順①:RSTの送信元を特定する
まず、RSTがどこから来ているかを確認します。
tcpdump -nn tcp and tcp[tcpflags] & tcp-rst != 0
確認ポイント:
- RSTの送信元IPがLBか
- サーバ自身か
- クライアント側か
LB IPからRSTが返っている場合、idle timeoutを強く疑います。
切り分け手順②:通信間隔と切断時間を測る
エラーが出るまでの時間が、
- ほぼ一定(60秒、120秒、240秒など)
であれば、LB idle timeoutと一致している可能性が高いです。
切り分け手順③:KeepAlive設定の確認
代表的な確認例:
# Java(例)
-Dhttp.keepAlive=true
# Apache
KeepAlive On
KeepAliveTimeout 300
# Nginx
keepalive_timeout 300;
LB idle timeoutより長く設定されていないか確認します。
正しい設計①:LB idle timeout > アプリKeepAlive
基本原則はこれです。
- LB idle timeout:300秒
- アプリKeepAlive:120秒
必ずLB側の方を長くします。
正しい設計②:アプリ側で定期的に通信を流す
どうしてもKeepAliveを長くしたい場合は、
- アプリレベルのPing
- HTTP/2 keepalive frame
などで、idle状態を作らない設計にします。
正しい設計③:短命接続に割り切る
API通信などでは、
- KeepAliveを使わない
- 接続は都度確立
という設計も有効です。
LB idle timeoutを気にしなくて済む反面、 接続数・ポート消費には注意が必要です。
まとめ
LB idle timeoutとKeepAliveの不整合は、
- 再現性が低い
- ログが分かりにくい
- アプリ障害に見える
という理由で、現場で非常にハマりやすいポイントです。
重要なのは、
LBとアプリを「別物」として設計しないこと
通信の主導権はLBが握っている、という前提で設計することが、 安定運用への近道です。



