TCP 関連カーネルパラメータについて

ウェブサーバの高負荷対策、または パフォーマンス改善を図る際に よく耳にするのが以下のカーネルパラメータではないかと。

  • tcp_tw_recycle
  • tcp_tw_reuse
  • tcp_fin_timeout

なかなか厄介なパラメータなので、最初に簡単に注意点とポイントだけ言いますと。

  • TIME-WAIT による高負荷を軽減させたい時には tcp_tw_reuse と tcp_fin_timeout が効果抜群!
  • NAT 環境、またはロードバランサ直下にあるサーバに tcp_tw_recycle を設定すると問題が起こるので、設定するのは止めて~
  • tcp_fin_timeout 値が低すぎると問題になるよ
  • tcp_fin_timeout 値は、アプリによって適正値が異なるから最初からあまりに短くしないでちゃんと検証してから使ってね
  • これらのパラメータチューニングは、専門知識が必要なので下手に触らないでー

各カーネルパラメータの役割を簡単に説明すると以下になります。

パラメータ機能・説明デフォルト値
tcp_tw_recycleTIME_WAIT 状態のソケットを手っ取り早く再利用する0 (無効)
tcp_tw_reuse新しい接続に対して、TIME_WAIT 状態のソケットを再利用する0 (無効)
tcp_fin_timeoutFIN-WAIT2 から TIME_WAIT に状態が変化するまでの時間60 (秒)

さて、tcp_fin_timeout は本当に効果あるのか

これまでのトラブル事例を見ると CPU 負荷は高くもないのに、なぜかウェブサーバが非常に重たく、調べてみると TIME_WAIT 状態のセッションが大量に残っていたので、tcp_fin_timeout 値を調整することによってこの問題を回避しているという情報がほとんどだったんですね。

それで、ちょっと疑問に思ったのが、アクセスが集中しサーバの CPU 使用率が高く、高負荷状態になったときに tcp_fin_timeout 設定は、どれくらい効果があるか。 でした。

ちょうど会社のウェブサーバ検証中に CPU 負荷が高くなったので、なんとか改善したいと思い、TCP パラメータをチューニングすることによってどれくらい負荷が軽減されるか・どれくらい効果があるか tcp_fin_timeout 設定変更 前・後の CPU リソース値を計測してみることにしました。

TCP 関連カーネルパラメータである tcp_tw_recycletcp_tw_reusetcp_fin_timeout については、以下のサイトが内容をよくまとめてくれてるので、細かい内容は省略したいと思います。

同時接続 1千・合計 3万リクエスト

以下は、クライアント 1台 を使って、ウェブサーバへ 同時接続 1,000、30,000リクエスト を投げたときに tcp_fin_timeout 値を変更することによって CPU 使用率がどれくらい変動するかを表したグラフです。

  • net.ipv4.tcp_fin_timeout = 60 の場合、CPU 使用率は 20.34%
  • net.ipv4.tcp_fin_timeout = 10 の場合、CPU 使用率は 13.62%
  • 因みに、net.ipv4.tcp_tw_recycle = 0、net.ipv4.tcp_tw_reuse = 1 にしてます。

リクエスト数が少なかったのか微妙な数値になってしまいましたが、若干効果はあるように見えますね。

tcp_fin_timeout 10

1CPU のウェブサーバへ同時接続 1,000、30,000リクエスト

同時接続 4千・合計 12万リクエスト

ということで、クライアント 4台 を使って、各クライアントから 同時接続 1,000、30,000リクエスト を連続でポン・ポン・ポン・ポン叩いて見ました。

ほぼ同時にコマンドを実行しただけなので、厳密に言うと同時接続とは言えないかも知れませんが、まぁまぁ同時接続 4,000 ということで。 これで 合計リクエスト数は、120,000 になります。

  • net.ipv4.tcp_fin_timeout = 60 の場合、CPU 使用率は 100%
  • net.ipv4.tcp_fin_timeout = 10 の場合、CPU 使用率は 46.75%
  • 因みに、net.ipv4.tcp_tw_recycle = 0、net.ipv4.tcp_tw_reuse = 1 にしてます。

なんと tcp_fin_timeout がデフォルト値のままだとリクエスト数が増えたときに CPU 使用率が 100% 張り付いてしまいました。

その反面、fin_timeout = 10 の場合には、CPU 負荷も減り、処理時間も短くなっているように見えます。 (2つ目の山は無視してください)

tcp_fin_timeout 20

1CPU のウェブサーバへ同時接続 4,000、120,000リクエスト

Nginx と Apache Bench を使いました

ウェブサーバは Nginx、クライアント側の負荷検証ツールは、おなじみの Apache Bench を使いました。

コンテンツサイズは、3KB くらいで大きいサイズではありあません。

ちなみに、今回は AB を使いましたが、精度の高い検証を行う際には、AB ば非推奨で、JMeter のような高機能ベンチマークツールを使うようにしてください。

ab 実行例
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# ab -c 1000 -n 30000 http://100.100.100.100/index.html

・・・

Server Software:        nginx/1.8.1
Server Hostname:        100.100.100.100
Server Port:            80

Document Path:          /index.html
Document Length:        3091 bytes

Concurrency Level:      1000
Time taken for tests:   39.412 seconds
Complete requests:      30000
Failed requests:        0
Write errors:           0
Total transferred:      99950830 bytes
HTML transferred:       92937064 bytes
Requests per second:    761.18 [#/sec] (mean)
Time per request:       1313.748 [ms] (mean)
Time per request:       1.314 [ms] (mean, across all concurrent requests)
Transfer rate:          2476.58 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0  249 1060.8      2   31003
Processing:     1  940 1428.4      2   23240
Waiting:        0  338 892.6      2   23237
Total:          1 1189 1794.5    972   34605

・・・

終わりに

tcp_tw_recycle に関しては、ちらほら問題が起こると報告されているので、リスクを背負ってまで設定する必要はないかなと思ったので、特に設定はしていません。

やっぱり設定するなら tcp_tw_reusetcp_fin_timeout くらいかなと。

上記の検証結果を見れば分かるように、これは使わないと損。 ということで、早速当サイトにも上記の設定を入れました。

最初は、tcp_fin_timeout = 10 を設定していたのですが、タイムアウト値まではちゃんと検証してないし、ちょっと短くないか?という懸念点もあったので、今現在はウェブサーバの適正値と思われる 20~30 の中間値である 25 に設定して運用しています。

/etc/sysctl.conf
1
2
3
4
5
・・・
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 25
・・・
sysctl 設定反映
1
# sysctl -p

最近は、VPS 借りてウェブサイトを運営している方も多いかと思います。

安いスペックは使えるリソースに限界があるし、ちゃんとチューニングしておかないと PV が増えるにつれて、徐々にサーバを圧迫してくるので、限られたリソースで最大限のパフォーマンスが出せるようにするためにも、こういう危険なチューニングはサービスを開始する前に、きちんと検証して設定したほうがいいと思いました。 (今回は事後チューニングでしたけど)

特に、この結果については、仮想環境内・仮想マシンで試した結果であって、ネットワーク構成だったり、物理環境・仮想環境など、様々な要因によって十分変わる可能性があるので、100% 信用せずに、自分の環境で検証を行った上で、適正値を確認することをオススメします。 (そもそもこの値は触らずに、うまく付き合ったほうが良いという意見もあります)

私は適用して使っていて、今までこれといった問題はありませんでした。 ご参考までに。

以上、tcp_fin_timeout 設定は本当に効果あるのか (ありました) でした。