TCPステータスの深淵:なぜあなたのサーバーは「死んだ接続」に埋め尽くされるのか
ネットワーク運用において、ss や netstat の出力結果を単なる「羅列」として見ているうちは、まだエンジニアとしては二流だ。その一行一行は、Linuxカーネルという巨大なオーケストラが奏でる、パケットの生きた軌跡そのものだからだ。
深夜のNOCで、突如としてレスポンスが鈍化したアプリケーションのグラフを眺める時、我々が見るべきはCPU使用率よりも先に、TCPのステータス遷移だ。特に、TIME_WAIT や SYN_RECV が閾値を超えて積み上がる様子は、システムからの「悲鳴」に他ならない。今日は、この泥臭くも美しいTCPの深淵について、実戦的なチューニングの知見を紐解こう。
—
1. TCPステータスの「静かなる異常」を読み解く
ss -nt を叩いたとき、あるいは netstat -antp を眺める時、我々が注目すべきは各状態の「滞留理由」だ。
SYN_RECV: クライアントからのSYNを受け取り、SYN-ACKを返した後の状態。これが異常に多い場合、典型的なSYN Flood攻撃か、あるいはバックログが溢れてパケットがドロップされている予兆だ。ESTABLISHED: まさに今、セッションが確立されている状態。ここが枯渇するのは、単なるアプリの処理遅延か、コネクションプールが適切に開放されていない証拠だ。TIME_WAIT: 多くのエンジニアがここで躓く。接続を終了した側が、遅れて届くパケットの迷子を防ぐために保持する「猶予期間」。これが枯渇すると、新規接続ができなくなる。
なぜ TIME_WAIT を恐れるのか
多くのアーキテクトは TIME_WAIT を即座に「悪」と決めつけ、tcp_tw_recycle(現在はLinuxカーネルから削除された)のような禁じ手を使おうとする。だが、これはNAT環境下で致命的なパケット破棄を招く。我々がやるべきは、TCPの挙動を根本から理解し、カーネルパラメータを適切に追い込むことだ。
—
2. 極限のパフォーマンスを実現するカーネルチューニング
接続数が数万を超える高負荷環境では、デフォルトのsysctl設定はあまりに貧弱だ。以下の設定は、高負荷なWebサーバーやAPIゲートウェイを運用する際の「守りの要」となる。
# /etc/sysctl.conf に記述すべき最適化の例
# 1. TIME_WAIT ソケットの再利用を許可(安全な範囲で)
net.ipv4.tcp_tw_reuse = 1
# 2. 短期間に大量の接続が来る環境でのバックログ強化
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# 3. TCP バッファの自動調整(RTTと帯域幅の最大化)
# 読み取り/書き込みバッファを広げ、高レイテンシ環境でのスループットを維持
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# 4. FIN-WAIT-2 タイムアウトの短縮
# 接続切断待ちの時間を短くし、メモリ消費を抑える
net.ipv4.tcp_fin_timeout = 15
これらの設定を適用する際、sysctl -p を叩くだけで満足してはいけない。ss -nlt でリスニングポートのバックログが実際に拡大されているか、cat /proc/net/tcp で現在のキューの深さを確認するまでが、シニアエンジニアの仕事だ。
—
3. TLSハンドシェイクとRTTの削減:見えない負荷を削ぎ落とす
TCPステータス以前の問題として、アプリケーション層のセキュリティ、つまり TLS ハンドシェイクのコストを忘れてはならない。SYN が届いてから ESTABLISHED になり、その後の TLS ネゴシエーションで数往復(RTT)が発生する。
この「往復」こそが、ユーザー体験を損なう最大の要因だ。
- TLS 1.3 の採用:
TLS 1.3ではハンドシェイクが1往復に短縮された。また、0-RTT機能を使えば、過去に接続したクライアントはハンドシェイクなしでデータを送信できる。これを有効にしない手はない。 - TCP Fast Open (TFO):
SYNパケット自体にデータを乗せて送る手法。これにはクライアントとサーバーの両方のサポートが必要だが、モバイルネットワークのような不安定な環境では劇的な改善が見込める。
# TCP Fast Open を有効化する
# 3: クライアント/サーバー両方で有効
sysctl -w net.ipv4.tcp_fastopen=3
—
4. 現場の教訓:トラブルシューティングの作法
最後に、トラブルシューティングの現場で最も重要な心得を伝授する。
何か異常が起きたとき、netstat の出力だけを見て「接続数が足りない」と判断してはいけない。常に以下のフローを脳内で走らせるのだ。
1. パケットの現在地はどこか?: tcpdump で該当インターフェースをキャプチャし、SYN に対して SYN-ACK が返っているか、あるいは RST が送られていないかを確認する。
2. カーネルの統計を信じろ: netstat -s を叩き、TCPBacklogDrop や TCPRetransSegs が増えていないかチェックする。これは、アプリケーションではなく「カーネルがパケットを処理しきれていない」という明確な信号だ。
3. コンテキストスイッチの監視: 接続数が多いだけでは不十分だ。CPUがネットワークI/Oの処理(ソフトIRQ)に忙殺されていないか、mpstat -P ALL 1 で確認せよ。
ネットワークは生き物だ。教科書的な設定を盲信するのではなく、カーネルが吐き出す生データと対話せよ。その先にある「最適化」の領域こそが、インフラアーキテクトが目指すべき聖域なのだから。
コメント