ポート枯渇

WEBサーバがデータベースやKVSや他のAPIサーバに大量に接続するなどしている場合、特に接続をコネクションプーリングしていない場合、TCPコネクションの状態が通信終了間際の状態であるTIME_WAITでいっぱいになり、ポート枯渇してしまう状態への対応をまとめる。

TIME_WAITでいっぱいでローカルポート数の残りが少ないかどうかはnetstat -antp | wc -lで使用ポート数を見ればわかる。ただし、WEBサーバ自身が80ポートや443ポートで接続を受け付けている部分は除かなくてはいけない。ポート枯渇で問題になるのは、自サーバから他のサーバへ接続するときに使用するポートのことで、80/443ポートは接続を受けている側なのでプロセス数の上限やファイルディスクリプタの上限が問題になる。ulimit -u, ulimit -nでそれぞれ上限を確認する。スレッド数の上限はcat /proc/sys/kernel/threads-maxで確認できる。

ポート枯渇の方は、例えば443でLISTENしているのであればnetstat -antp | grep -v :443 | wc -lのように443を除いて接続数を確認するといい。

ip_local_port_range

netstatで確認した大まかなポート使用数がip_local_port_rangeの数より十分に小さければ問題ない。

$ cat /proc/sys/net/ipv4/ip_local_port_range
32768   61000

## 61000 - 32768 = 28232

ip_local_port_rangeが少なければ、エフェメラルポート全部(1024-65535)に広げるといい。

$ sudo vi /etc/sysctl.conf
net.ipv4.ip_local_port_range = 1024    65535

$ sudo sysctl -p

ip_local_reserved_ports

ip_local_port_rangeでエフェメラルポート全部に広げた場合、ハイポートをLISTENするミドルウェアをそのサーバで起動させたいときに問題が起きないようにip_local_reserved_portsにそのポートを設定するといい。

WEBサーバによく同居させるミドルウェアでハイポートを使うものと言えばFluentdがあるので例にとる。

$ sudo vi /etc/sysctl.conf
net.ipv4.ip_local_reserved_ports = 24224

$ sudo sysctl -p

複数設定したい場合は、,でポートを並べればいい。範囲指定したい場合は-でできる。

tcp_tw_reuse

ip_local_port_rangeを変更して使用できるローカルポート数を増やしたところで二倍になる程度であり、また足りなくなってしまう可能性がある。カーネルパラメータのnet.ipv4.tcp_tw_reuseを設定して、TIME_WAIT状態のソケットを新しい接続に対して安全に再利用できるようにした方がいい。

$ sudo vi /etc/sysctl.conf
net.ipv4.tcp_tw_reuse = 1

$ sudo sysctl -p

同様にTIME_WAIT状態のソケットの再利用をしてくれるnet.ipv4.tcp_tw_recycleは危険なので、有効にしてはいけない。net.ipv4.tcp_tw_recycleを有効にすると、同じグローバルIPのクライアントからの接続かつTCPパケットにタイムスタンプ情報が入っている場合、ほぼ同時にパケットを送ると、古いタイムスタンプの方のパケットを勝手にドロップし、ドロップされた方のクライアントはタイムアウトになってしまう。

tcp_fin_timeout

net.ipv4.tcp_tw_recycle は廃止されました ― その危険性を理解するnet.ipv4.tcp_fin_timeoutはTIME_WAIT状態になっている秒数を定義するものではないと記載されていた。FIN_WAIT_2のタイムアウト設定のためのものであり、net.ipv4.tcp_fin_timeoutを変更しても、ポート枯渇状態の改善にはほとんど寄与しないため、net.ipv4.tcp_tw_reuseを変更するだけでいいだろう。

参考
http://qiita.com/kuni-nakaji/items/c07004c7d9e5bb683bc2
http://d.hatena.ne.jp/pullphone/20120511/1336722675