ぜんぶTIME_WAITのせいだ!

  • 571
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

課題

突然キャンペーンとかの高トラフィックが来る!とか言われると色々困ることはあるものの、今のご時世クラウドだからスペック上げときゃなんとかなるでしょ。ってとりあえずCPUとかメモリあげて見たものの、キャンペーンが始まったら意外と早くブラウザからつながらない!!とか言われたりする。

CPUもメモリもそんなに負荷は特に高くもない。調べてみたらTIME_WAITが大量にあった。

graph 3.png

とりあえず何とかしたい

TIME_WAIT数をコマンドで確認

$ netstat -anp|grep TIME_WAIT

__(snip)__
tcp        0      0 192.168.1.1:80  192.97.67.192:56305         TIME_WAIT   -
tcp        0      0 192.168.1.1:80  192.63.64.145:65274         TIME_WAIT   -
tcp        0      0 192.168.1.1:80  192.39.2.205:3325           TIME_WAIT   -
tcp        0      0 192.168.1.1:80  192.102.102.9:54300         TIME_WAIT   -
tcp        0      0 192.168.1.1:80  192.71.125.102:61769        TIME_WAIT   -
__(snip)__

カーネルパラメータの設定変更

Tuning Red Hat Enterprise Linux on IBM によると、tcp_tw_reuse,tcp_tw_recycle,tcp_fin_timeoutを下記のように追記して設定することで下げることが出来そう。

/etc/sysctl.conf
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30

カーネルパラメータの設定反映

# sysctl -p

graph3.png

ふむ、たしかにTIME_WAIT数は下がる

TIME_WAITってなんなんだ?

EFSM/SDL modeling of the original TCP standard (RFC793) and the Congestion Control Mechanism of TCP Renoのネットワーク状態遷移図を確認してみる。

efsm.png

うーん、これを見ると通信上はFIN-WAIT-1/FIN-WAIT-2/CLOSINGから遷移して、滞留する必要なさそうにみえるのになぁ。

見方を変えてみる

Wikipedia:Transmission Control ProtocolよりESTABLISHEDからCLOSEDになるまでの状態をシーケンスで見てみる。

tcp.jpg

例えば、InisiatorがWEBサーバで、Receiverの方がPCとかスマホとすると、WEBサーバ側がTIME_WAITのときにスマホ側でCLOSEDされるのを待ってるのかぁ。

そうすると、Receiver側のCLOSEDが早く行われるならば、Inisiator側の待ち時間(tw_fin_timeout値)はなるべく縮めても良いのか!?

再度カーネルパラメータの内容を確認してみる

tcp_fin_timeout

Time to hold socket in state FIN-WAIT-2, if it was closed by our side. Peer can be broken and never close its side, or even died unexpectedly. Default value is 60sec. Usual value used in 2.2 was 180 seconds, you may restore it, but remember that if your machine is even underloaded WEB server, you risk to overflow memory with kilotons of dead sockets, FIN-WAIT-2 sockets are less dangerous than FIN-WAIT-1, because they eat maximum 1.5K of memory, but they tend to live longer.

  • ソケットをこちら(上記Receiver)側からクローズしたときに、FIN-WAIT-2 状態に保つ時間。
  • 通信先が切れてしまったり、先方でのクローズをずっと行わなかったり、 あるいは予期しない形で終了してしまうかもしれません。
  • デフォルトの値は 60 秒です。
  • 2.2 で用いられていた通常の値は 180 秒で、 この設定にすることもできますが、マシンが (必ずしも負荷が高くなくても) WEB サーバだったりすると、 膨大な FIN-WAIT-2 ソケットのせいでメモリが溢れる危険もあります。
  • FIN-WAIT-2 ソケットは 1.5K のメモリしか消費しないので FIN-WAIT-1 ほど危険ではありませんが、長く存在する傾向があります。

tcp_tw_reuse

This allows reusing sockets in TIME_WAIT state for new connections when it is safe from protocol viewpoint. Default value is 0 (disabled). It is generally a safer alternative to tcp_tw_recycle.
Note: The tcp_tw_reuse setting is particularly useful in environments where numerous short connections are open and left in TIME_WAIT state, such as web servers and loadbalancers. Reusing the sockets can be very effective in reducing server load. Starting in Linux 2.6.7 (and back-ported to 2.4.27), linux includes alternative congestion control algorithms beside the traditional 'reno' algorithm. These are designed to recover quickly from packet loss on high-speed WANs.

  • これは、プロトコルの観点から安全に新しい接続でTIME_WAITのソケット再利用することを許可します。
  • デフォルトは0(無効)。
  • これは一般的にtcp_tw_recycleよりも安全な方法です。

Note: tcp_tw_reuse設定は、Webサーバやロードバランサーのような、大量に短い接続がオープンしTIME_WAIT状態になるような環境では特に有用です。ソケットの再利用は、サーバ負荷軽減に非常に効果的です。Linux2.6.7から始まり、linuxは伝統的な'Reno'アルゴリズムに加えて代替の輻輳制御アルゴリズムを実装している。高速WAN上でパケットロスから素早く復旧するためデザインされている。

tcp_tw_recycle

This enables fast recycling of TIME_WAIT sockets. The default value is 0 (disabled). Should be used with caution with loadbalancers.

  • TIME-WAITソケットを早く再利用することが出来る。
  • デフォルトの値は0。
  • ロードバランサなどを使用している場合は注意して使用する必要がある。

tcp_fin_timeoutはどのくらいにすればいいの?

何件かのサイト(1,2,3)をチェックしてみたが、30秒説がよく見つかるものの幅は1〜30秒とこれといった決定打はなかった。ネットワーク速度が向上してきたので60秒では長いけど それぞれのサーバ環境に合わせて と言った感じに取れる。

設定の参考値を自分なりに考えてみる

  • なるべく積極的にリソースを開放したい
  • 外部と通信するものは通信状態が様々なのである程度時間を保つ
  • 内部で通信するものはGigabit Ethernetなどが増えているのである程度短くできる
サーバ用途 tcp_fin_timeout参考値 備考
WEB 20〜30秒
Hadoop 15〜30秒
KVS(cassandra) 1〜5秒 攻めすぎ?

TIME_WAITの待ち時間状況を確認するには?

netstatにまだ便利オプションがあった。これで確認しながら調整するのが良さそう。

$ netstat -anpto

__(snip)__
tcp    0   0 192.168.1.1:80  192.2.197.186:58601    TIME_WAIT   -   timewait (52.22/0/0)
tcp    0   0 192.168.1.1:80  192.82.117.34:56977    TIME_WAIT   -   timewait (30.70/0/0)
tcp    0   0 192.168.1.1:80  192.135.233.60:60358   TIME_WAIT   -   timewait (40.65/0/0)
tcp    0   0 192.168.1.1:80  192.4.206.121:64294    TIME_WAIT   -   timewait (29.46/0/0)
tcp    0   0 192.168.1.1:80  192.2.197.186:58600    TIME_WAIT   -   timewait (52.22/0/0)
tcp    0   0 192.168.1.1:80  192.82.117.34:56464    TIME_WAIT   -   timewait (28.03/0/0)
tcp    0   0 192.168.1.1:80  192.169.145.26:60343   TIME_WAIT   -   timewait (13.64/0/0)
tcp    0   0 192.168.1.1:80  192.193.81.104:61038   TIME_WAIT   -   timewait (24.37/0/0)
__(snip)__

tcp_tw_recyleになにか問題あるの?

ココココ2の検証によるとnet.ipv4.tcp_timestampsnet.ipv4.tcp_tw_recycleの両方を使うと

Linux/net/ipv4/tcp_ipv4.c
Linux/include/net/tcp.h

上記部分のロジックで、ロードバランサーやNATの配下ある場合に問題が発生するらしい。

tcp_tw_recycle.png

まとめ

LB直下にない単独のサーバであれば下記の様な設定が良い気がする。また、Inbound側だけの通信ならうまくTIME_WAITが減らせるかも知れない。※tcp_fin_timeoutは環境に合わせて調整

/etc/sysctl.conf
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30

LB直下のWEBサーバなどはtcp_tw_recycleを無効にした設定を考えたほうが良いかもです。

/etc/sysctl.conf
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30

今回は昔からよく出ている3つのパラメータだけにフォーカスしたが、沢山パラメータがあるのでより効率よくするためにまだまだ余地はありそう。

デフォルト値は歴史的に変化していないものがありそうなのでちゃんと理解が必要なんだなと再認識。