なぜあなたのアプリケーションは「突然死」するのか?― netstat で読み解くTCP状態遷移の深淵
深夜2時、鳴り響くアラート通知。ダッシュボードを見ると、Web APIのレスポンスタイムが急激に跳ね上がり、あちこちで 504 Gateway Timeout や Connection refused が発生している。
多くのエンジニアはここで慌ててログを漁りますが、熟練のNOCエンジニアはまず ss や netstat を叩きます。「今、メモリ上で何が起きているのか?」を可視化するためです。今回は、教科書的な説明はすっ飛ばして、現場の泥臭いトラブルシューティングで必須となるTCPステータスを深掘りします。
—
1. TCPステータスの「現場的」解釈
TCPのステータスは単なる記号ではありません。それは通信という「交渉」の記録です。
ESTABLISHED:正常な「交信中」
通信が確立され、パケットが双方向に流れている状態です。ここが異常に多い場合、アプリケーションの処理が詰まっているか、あるいはバックエンドのDBクエリが重すぎて、コネクションを掴んだまま離せない状況を疑います。
SYN_SENT:接続の「片思い」
クライアントが SYN を送ったものの、サーバーから SYN/ACK が返ってこない状態です。
- 現場の勘所: サーバーのポートが閉まっているか、途中のファイアウォール(iptables/nftablesやAWSのSecurity Group)でパケットがドロップされています。
telnetやncでの疎通確認だけでなく、tcpdumpでパケットがどこで消えたか追うのが鉄則です。
CLOSE_WAIT:終わらせられない「後始末」
ここがトラブルの温床です。CLOSE_WAIT は「相手から切断要求(FIN)が来たが、自アプリがまだ close() を呼んでいない」状態です。
- なぜ起きるか: アプリのコードでソケットをクローズし忘れている、あるいは重い処理の最中でスレッドがブロックされており、アプリケーション層の切断ロジックまで制御が回っていないことがほとんどです。
TIME_WAIT:消えない「残照」
通信終了後、誤ったパケットの迷子を防ぐためにOSが一定時間(通常60秒〜240秒)接続情報を保持する状態です。
- 現場の勘所:
TIME_WAITが大量にあるからといって、即座に「バグ」とは限りません。しかし、ephemeral port(一時ポート)が枯渇して新規接続できなくなる「ポート枯渇」を引き起こすことがあります。
—
2. 現場で役立つ実践的デバッグ術
ss コマンドによる高速診断
今は netstat より高速な ss を使うのが定石です。特定のポート(例えば8080)の状態を一発で確認します。
# 8080ポートのコネクション一覧をステータス付きで表示
ss -antp 'sport = :8080'
Pythonでコネクションリークを再現する(実験用)
コネクションを閉じずに放置すると、CLOSE_WAIT がどう溜まるか。以下のスクリプトを動かして ss を監視してみてください。
import socket
# サーバーに接続するが、わざとclose()を呼ばない
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 80))
# ここで処理を止めて放置する(netstatで確認可能)
input("Press Enter to close...")
s.close()
—
3. 「ポート枯渇」と TIME_WAIT への対処法
もし TIME_WAIT が原因でサービスが停止した場合、OSのカーネルパラメータを調整するのも一手ですが、根本解決は「コネクションプール」の適正化です。
カーネルパラメータのチューニング(sysctl)
どうしても接続数が捌ききれない場合の緊急避難処置として、/etc/sysctl.conf に以下を追記します。
# TIME_WAIT状態のソケットを再利用可能にする
net.ipv4.tcp_tw_reuse = 1
# 一時ポートの範囲を広げる(デフォルトは狭いことが多い)
net.ipv4.ip_local_port_range = 1024 65535
※注意:net.ipv4.tcp_tw_reuse はNAT環境下で副作用が出る場合もあるため、慎重に適用してください。
—
4. シニアからのアドバイス:ログを見るな、パケットを見ろ
障害発生時、多くのエンジニアがアプリケーションログばかりを見ますが、アプリケーションは「OSが何をしているか」を正しく把握できていないことが多々あります。
1. まずは ss -s で要約を見る: 今、システム全体で各ステータスがどれだけあるか。
2. CLOSE_WAIT が増え続けているなら: アプリのソースコードを grep で close() の呼び出し箇所を検索してください。
3. SYN_SENT が多いなら: 宛先IPへのネットワーク経路(ルーター、ロードバランサー)を確認してください。
ネットワークは嘘をつきません。TCPの状態遷移を理解することは、システムという巨大な生命体の「脈拍」を診ることに等しいのです。画面上のステータスが何を示唆しているか、その裏側に想像力を働かせてみてください。
さあ、次はあなたの番です。ss コマンドを叩いて、そのサーバーの「本音」を聞いてやりましょう。
コメント