ポート枯渇
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