深夜2時、監視アラートの鳴り響くNOCのコンソール前で、我々はしばしば「幽霊」と対峙することになる。
その幽霊の名は TIME_WAIT。Webサーバーの接続数が急増した際、netstat や ss を叩いて青ざめた経験はないだろうか。無数に並ぶ TIME_WAIT の行。これが何を意味し、なぜシステムを殺すのか。今回は、TCPの「お作法」と現場の「泥臭い対策」について、教科書には載っていない本音で語ろうと思う。
なぜ TIME_WAIT は消えてくれないのか
まず、TCPのハンドシェイクの「最後」を思い出してほしい。接続を終了する際、先に FIN パケットを送った側(通常はサーバー側)が TIME_WAIT 状態に入る。これは仕様だ。
なぜすぐに解放しないのか? それは「通信の残骸」がネットワーク上にまだ彷徨っているかもしれないからだ。もし即座にポートを再利用して別の通信を始めたら、前の接続の遅延パケットが新しい通信に混入する可能性がある。これを防ぐための「冷却期間」が TIME_WAIT だ。
RFC 793では、この期間は最大セグメント寿命(MSL)の2倍と定義されている。Linuxなら通常60秒。秒間数千のリクエストを捌くAPIサーバーで、60秒間もポートをロックし続けたらどうなるか。当然、利用可能なエフェメラルポートが枯渇し、新しいコネクションを張れなくなる。これが「ポート枯渇」の正体だ。
現場の武器:ss コマンドで現状を可視化する
まず、今何が起きているかを確認しよう。netstat もいいが、現代のエンジニアなら ss を使え。
# TIME_WAIT状態のソケット数をカウントする
ss -ant | grep TIME-WAIT | wc -l
# 特定のポートに対する接続状況を詳細に追う
ss -npt 'sport = :80'
この数値がマシンの制限値(/proc/sys/net/ipv4/ip_local_port_range)に近づいていたら、即座に対策が必要だ。
カーネルパラメータという名の「処方箋」
よくある誤解として、net.ipv4.tcp_tw_recycle を有効にするという古いWeb記事を見かけるが、絶対にやってはいけない。NAT環境下でパケットのタイムスタンプ整合性が取れず、通信が壊滅する。
我々が現場で使うのは、以下の設定だ。これらは /etc/sysctl.conf に追記して sysctl -p で反映させるのが定石だ。
# 1. TIME_WAIT状態のソケットを、新しい接続で再利用可能にする
# これが最も強力な即効薬だ
net.ipv4.tcp_tw_reuse = 1
# 2. TCPのFIN-WAIT-2状態のタイムアウト時間を短縮する
# 接続終了待ちの時間を減らすことで、リソース解放を早める
net.ipv4.tcp_fin_timeout = 15
# 3. エフェメラルポートの範囲を広げる
# これにより、一時的な枯渇を緩和する
net.ipv4.ip_local_port_range = 1024 65535
特に tcp_tw_reuse は、TIME_WAIT 状態のソケットを、別の新しい接続の起点として使い回すことを許可する。これにより、ポート枯渇問題は劇的に改善する。
根本治療:アプリケーション側の作法を見直す
OSの設定で延命はできるが、結局のところ、原因はアプリケーションにあることが多い。
1. HTTP Keep-Aliveの活用
頻繁にリクエストを投げるクライアント側(例えば、APIを叩くマイクロサービスなど)では、必ず Keep-Alive を有効にせよ。接続のたびに SYN/FIN を繰り返すのは、車を動かすたびにエンジンを切り、鍵を抜いて再始動するようなものだ。
Pythonの requests で言えば、Session オブジェクトを使うのが正解だ。
import requests
# Sessionを使うことで、コネクションプールが機能し、
# 接続の再利用が可能になる(Keep-Aliveの恩恵を受ける)
session = requests.Session()
for i in range(100):
# 毎回接続を確立せず、既存のコネクションを使い回す
response = session.get('https://api.example.com/data')
2. クライアント側の接続数制限
ロードバランサーとサーバーの間にコネクションプールを適切に設定し、上限を設けることも重要だ。際限なく接続を貼れば、当然 TIME_WAIT も際限なく増える。
シニアからの提言:メトリクスで監視せよ
最後に、運用担当者に伝えたいことがある。TIME_WAIT を「ただの数字」として見るな。
GrafanaやPrometheusで node_net_stat_Tcp_TW のようなメトリクスを可視化し、ベースラインを把握しろ。突発的なスパイク時に TIME_WAIT が急増するのか、それとも定常的に右肩上がりなのか。
障害対応の現場では、「何が起きているか」を正確に把握する力がすべてだ。TIME_WAIT はネットワークの健康診断の結果のようなもの。これを正しく読み解き、適切なカーネル調整とコードの改善を行えば、システムは必ず安定する。
さあ、コマンドを打とう。あなたのサーバーは、まだ悲鳴を上げているかもしれない。
コメント