#課題
突然キャンペーンとかの高トラフィックが来る!とか言われると色々困ることはあるものの、今のご時世クラウドだからスペック上げときゃなんとかなるでしょ。ってとりあえずCPUとかメモリあげて見たものの、キャンペーンが始まったら意外と早くブラウザからつながらない!!とか言われたりする。
CPUもメモリもそんなに負荷は特に高くもない。調べてみたらTIME_WAITが大量にあった。
#とりあえず何とかしたい
####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
を下記のように追記して設定することで下げることが出来そう。
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
####カーネルパラメータの設定反映
# sysctl -p
ふむ、たしかにTIME_WAIT数は下がる
#TIME_WAITってなんなんだ?
EFSM/SDL modeling of the original TCP standard (RFC793) and the Congestion Control Mechanism of TCP Renoのネットワーク状態遷移図を確認してみる。
うーん、これを見ると通信上はFIN-WAIT-1/FIN-WAIT-2/CLOSINGから遷移して、滞留する必要なさそうにみえるのになぁ。
#見方を変えてみる
Wikipedia:Transmission Control ProtocolよりESTABLISHEDからCLOSEDになるまでの状態をシーケンスで見てみる。
例えば、InisiatorがWEBサーバで、Receiverの方がPCとかスマホとすると、WEBサーバ側がTIME_WAITのときにスマホ側でCLOSEDされるのを待ってるのかぁ。
そうすると、Receiver側のCLOSEDが早く行われるならば、Inisiator側の待ち時間(tw_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 ほど危険ではありませんが、長く存在する傾向があります。
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上でパケットロスから素早く復旧するためデザインされている。
This enables fast recycling of TIME_WAIT sockets. The default value is 0 (disabled). Should be used with caution with loadbalancers.
- TIME-WAITソケットを早く再利用することが出来る。
- デフォルトの値は0。
- ロードバランサなどを使用している場合は注意して使用する必要がある。
- [注]kernel4.1.2で削除されているようです。 tcp: remove tcp_tw_recycle
#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_timestamps
とnet.ipv4.tcp_tw_recycle
の両方を使うと
Linux/net/ipv4/tcp_ipv4.c
Linux/include/net/tcp.h
上記部分のロジックで、ロードバランサーやNATの配下ある場合に問題が発生するらしい。
#まとめ
LB直下にない単独のサーバであれば下記の様な設定が良い気がする。また、Inbound側だけの通信ならうまくTIME_WAITが減らせるかも知れない。※tcp_fin_timeoutは環境に合わせて調整
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
LB直下のWEBサーバなどはtcp_tw_recycle
を無効にした設定を考えたほうが良いかもです。
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
今回は昔からよく出ている3つのパラメータだけにフォーカスしたが、沢山パラメータがあるのでより効率よくするためにまだまだ余地はありそう。
デフォルト値は歴史的に変化していないものがありそうなのでちゃんと理解が必要なんだなと再認識。