気ままにインフラエンジニア

渋谷で働くインフラエンジニアの備忘録。 TwitterID: @nakashii_

kernel: TCP: time wait bucket table overflow の解消とTIME_WAITを減らすチューニング

整理がてら。
httpdが動いているあるホスト上で、 /var/log/messages に以下のようなメッセージが出ていた。

kernel: TCP: time wait bucket table overflow
kernel: printk: 50078 messages suppressed.

"netstat -tna |grep TIME_WAIT"すると、10万以上の数でTIME_WAITが出ている。これを解消するまでの流れ。

そもそもこの値は、カーネルパラメータの
net.ipv4.tcp_max_tw_buckets
で設定されている。デフォルトは16384。
(↑の通り、エラーが出た環境では既にかなり大きな値が設定されていたのだが。)

/proc/sys/net/ipv4/tcp_max_tw_buckets
    システムが同時に保持する time-wait ソケットの最大数。この数を越えると、time-wait ソケットは直ちに破棄され、警告が表示されます。この制限は単純な DoS 攻撃を防ぐためにあります。わざと制限を小さくしてはいけません。ネットワークの状況によって必要な場合は、 (できればメモリを追加してから) デフォルトより増やすのはかまいません。
http://archive.linux.or.jp/JF/JFdocs/Adv-Routing-HOWTO/lartc.kernel.obscure.html

単純に値を増やして対応しても良いのだが、ホストと動いているサービスの状況によって対応策を使い分けたほうが良いと思う。

  • net.ipv4.tcp_max_tw_buckets を増やす

内部NWで閉じて利用しているか、DoS攻撃を防ぐための機能が上位のFirewall等で確実に機能している場合はこれでもよさそう。

  • TIME_WAITを減らすようにkernelパラメータをチューニングする

おおまかなパターンとしては以下の二つがあり、今回は後者を採用した。

1. TIME_WAITが自然解消されるまでの時間を減らす
これはかなり難解で、LinuxではTIME_WAITの保持時間を減らすにはカーネルをリビルドするしかない。
http://d.hatena.ne.jp/ono51/20111012/p1
この件を調べている途中、tcp_fin_timeoutを減らしたらOKという記事をたくさん見かけたが、どうも一緒に指定しているtcp_tw_recycleの作用が出ているだけに思える。
tcp_fin_timeout自体は、FIN-WAIT2からTIME_WAITに状態変化する時間のパラメータであり、TIME_WAIT状態が継続する時間とは関係ない。

2. TIME_WAITになったポートを再利用する
サーバ側で net.ipv4.tcp_tw_recycle を有効にする。
これで10万以上あったTIME_WAITが一気に100以下のレベルに。これは、閉じた環境で100ホスト程度がアクセスしてきているという環境だったから。

ただし、サーバ側で net.ipv4.tcp_tw_recycle が有効で、クライアント側でTCPのタイムスタンプオプションが有効(Linuxの場合net.ipv4.tcp_timestamps = 1)だと、NAT/LBを超えたときにSYNを落としてしまい、接続障害になる。
ユーザー向けに使っているとSB携帯などで障害が発生してしまうようなので、使わないほうがいいかも。

net.ipv4.tcp_tw_reuseを有効にしてもほとんど効果がなかった。これはおそらく、クライアント側も同じソケットをTIME_WAITしているため、クライアント側からの接続は新規セッションが使われるからだと思う。(ということは、クライアントとサーバー両方でONにしないとダメなんじゃないか?という疑問が。誰か検証してくれないかなー。)

  • アプリケーション側でコネクションの総数を減らす工夫をする

HTTPなので、keepaliveを有効にすることもできる。今回はHTTPを呼び出している側のシステムがコネクションプール的な概念を持たないので断念。


net.ipv4.tcp_tw_recycleを使う時の注意点はこのへんが詳しい。先達に感謝。
どさにっき
2007-05-21 - LowPriority