SlideShare a Scribd company logo
TIME_WAIT に関する話
sejima
免責事項
- 本資料において示される見解は、私自身の見
解であって、私が所属する組織の見解を必ずし
も反映したものではありません。ご了承くださ
い。
自己紹介
- まぁまぁ MySQL でご飯食べてます
- 一時期は Resource Monitoring や KVS にも
力入れてました
- ネットワーク的には素人です
- Linuxとハードウェアは嗜む程度
- disk I/O にはむかしから興味あります
- その他 slideshare はこちら
- http://www.slideshare.net/takanorisejima/
本日のお題
- kernel 新しくしたりすると、TCP的に意識したほ
うが良い変化が見つかるので
- 今日は、Webアプリケーションサーバの観点か
ら、 connect(2) する際に気になる TIME_WAIT
について、書いてみようかと思います
- 有識者からのマサカリを、強く歓迎いたします
最初に参考資料
- この二つの記事を読んでいただけば、それで概
ね良いと思うんですが
- Linuxカーネルの「TCP_TIMEWAIT_LEN」変更は無意味?
- Coping with the TCP TIME-WAIT state on busy Linux servers
- これらの記事をなぞりつつ、もうちょっと込み
入った話をしてみようと思います。
対象とする環境
- Ubuntu 14.04 LTS
- kernel 3.13 or 4.4
- 余談ですが、 EC2 で一番使われてるOSは、
Ubuntu らしいですね。
- Amazon EC2でもっとも人気のあるOSはUbuntu
- なので Ubuntu を対象にするのは無難かなと思
います。また、16.04 LTS の GA kernel は 4.4
なので、応用効くと思います。
そもそも、
TIME_WAIT 溜まると
何が起こるのか?
困ったときは
ソースコードを
読んでみよう
kernel 3.13 だとここらへん
- tcp_v4_connect() から読んでくと
- net/ipv4/tcp_ipv4.c#L223
- inet_hash_connect()
- __inet_hash_connect() で
inet_get_local_port_range() で connect(2)
時に割り当てできる Ephemeral port の
range をとって、ひたすら for ループ回して、
空いてる port 見つからなかったら
return -EADDRNOTAVAIL;
- ここに来るはず
- inet_get_local_port_range() は
net.ipv4.ip_local_port_range の min/max を返す関数
で、connect(2) したとき ip_local_port_range で指定し
た範囲で port 割り当てられなかったらエラー。
- ESTABLISHED や TIME_WAIT で socket 大
量に開いてて Ephemeral port 割り当てできな
いと、Linux 的には EADDRNOTAVAIL 返
す。
TCP 的になぜ TIME_WAIT はあるのか
- 理由は二つあって、一つは
a. FIN 受けた側(Passive Close)側が 、 FIN 送った
(Active Close)側に FIN+ACK を返して、 LAST_ACK
に遷移した後
b. Active Close 側が FIN+ACK への ACK を Passive
Close 側に返したんだけど、 packet 落ちても
c. Active Close 側が TIME_WAIT で待ってれば、
Passive Close 側が FIN+ACK 再送すれば、Active
Close 側がACK再送できる(ACK来たら LAST_ACK
から、 直ちに CLOSED に遷移できる)
図に描くとこう
TCP 的になぜ TIME_WAIT はあるのか
- もう一つは
a. Active Close 側が送信した packet を、Passive Close
側が、(packet 落ちるかなんかして)受信できてない状
態になって
b. その後すぐまた connect(2)し、同じ送信元 {ip,port} と
送信先 {ip,port} のセットで通信がはじまって
c. a. で受信できてなかった packet がたまたま遅れてやっ
てきて、それが TCP の sequence number 的にかぶっ
てて packet が受けられてしまうと不味い
ただ、これらの問題は
- RFC 7323 で定義されてる TCP Time Stamp
Option と PAWS で回避できます。
- tcp header に timestamp つけることで、sequence
number が一周しても、 timestamp を比較することに
よって、受信側は古い packet かどうか判断できます。
- ただ Option なので、無効化されている環境もありえま
す。
- ややこしい話なので後述します。
というわけで
- これら二つの TCP 的な目的から TIME_WAIT
という状態が必要で
- TIME_WAIT で待ち続けている socket が多い
状態で connect(2) すると、 Linux は
EADDRNOTAVAIL を返す可能性がある。
- そうであるならば、これら二つの TCP 的な目的
を満たしつつ、 EADDRNOTAVAIL が発生しな
い状況にすればよい。
では、
アプリケーションを
なるべく変更せずに
EADDRNOTAVAIL を
回避するためには
はじめに大前提
- TCP 的に port が 2byte のデータなら、パブリッ
ククラウド使ってるなら、インスタンスをスケール
ダウンして数並べれば良いんじゃないかと思い
ます。
- EC2 の c4.large でも c4.8xlarge でも、 TCP 的に使え
る port が 0-65535 なのは変わらないわけです。
- あるいは、コネクションプーリングできるなら、そ
れでも良いと思います。
ただそれでも
- スケールアップして集約できることに、メリットが
ある場合もあります。
- EC2 の c4.8xlarge は、使える vCPU が多いとか
- 使っているインスタンス or サーバの数が少ないと、監視
などの面で楽だとか。
- というわけで、高性能なサーバを上手く使おうと
するとき、 TIME_WAIT とどう付き合うか、とい
うのを考えたいわけです。
ではどうするか
1. 先ずは Monitoring する。
a. kernel 4.4 にして、 Monitoring の精度を上げる。
b. Ephemeral port の使用状況をざっくり Monitoring す
る。
2. ip_local_port_range を変更する。
a. (必要であれば)ip_local_reserved_ports を指定
3. net.ipv4.tcp_tw_reuse = 1 にする。
4. 接続先の MySQL や KVS を集約して、 tcp_tw_reuse で
TIME_WAIT の socket を再利用しやすくする。
1. 先ずは Monitoring する
- (個人的に)継続的な Monitoring は、すべての
基本だと思うんで、先ずは Monitoring
- TIME_WAIT の数をざっくり調べるには
- /proc/net/sockstat の tw
- なぜざっくりかというと、(少なくとも Ubuntu の)
kernel 3.13 だと、 TIME_WAIT の socket
が、 60sec (TIME_WAIT_LEN)経っても回収
されるとは限らないからです
netstat -o あるいは --timers
- netstat には -o というオプションがあります
- Include information related to networking timers.
- kernel 3.13 で TIME_WAIT 多いサーバで次の
コマンドを打つと、 timewait (0.00/0/0) がけっこ
う残ってることがあるのですが
- $ netstat -nato | egrep -c 'timewait.*(0.00'
- これは TIME_WAIT を回収できるまでの残り時
間で、 60sec 以上経ってるから 0です
なぜ kernel 4.4 にすると良いのか?
- kernel 4.4 の場合、 60sec 経つと、速やかに
TIME_WAIT 回収されるようです。
- ただ、なんでこのあたりの修正が効くのかは、い
まの私にはわかりませんでした。
- kernel4.1 で入ったこの patch で性能改善した
結果なんでしょうか?
お客様の中に
LinuxのTCPプロトコルスタックに
詳しい方が
いらっしゃいましたら、
教えていただけると幸いです
Ephemeral port をざっくり数える
- Webサーバの場合、DBなどに接続するだけで
なく、 Reverse Proxy とか ELB から接続され
たりするので、それらをWebサーバから
close(2) すると TIME_WAIT になりますが、こ
うやって数えることができます
- $ netstat -nat | grep -v '127.0.0.1' | egrep -c '
1[0-9]+.[0-9]+.[0-9]+.[0-9]+:80
+[0-9]+.[0-9]+.[0-9]+.[0-9]+:[0-9]+ .*TIME_WAIT'
TCP:80 の TIME_WAIT を上手く除外
- connect(2) するときに EADDRNOTAVAIL 返
るのが困るなら
- Web サーバが LISTEN してる TCP:80 で残っ
てる TIME_WAIT(Reverse Proxy や ELB な
どに対して close(2) して残った TIME_WAIT)
を除外すると
- connect(2) に影響する、Ephemeral port の
TIME_WAIT を数えやすくなるわけです
2. ip_local_port_range を変更する
- LISTEN してる port と被らないなら、
ip_local_port_range を変更するのは確実
- kernel 3.13 では 32768 - 61000、 kernel 4.4 では
32768 - 60999 が default
- 例えば、下限を 32768 から 24225 にするだけ
で、使える Ephemeral port が約30%増加
- よく使われてる fluentd は default で tcp:24224 を使う
ので、被らないように 24225。
net.ipv4.ip_local_reserved_ports
- fluentd で tcp:24224 を LISTEN してるんだけ
ど、net.ipv4.ip_local_port_range もっと下の
range まで指定したいときは、
ip_local_reserved_ports に、 LISTEN したい
port を列挙しておけば良いです。
- __inet_hash_connect() の中で予約された port を使わ
ないように見てる ので
3. net.ipv4.tcp_tw_reuse = 1 にする
- ようやく出てきました tcp_tw_reuse
- これについては Coping with the TCP
TIME-WAIT state on busy Linux servers が最
高に良い資料で、ほとんどここに書いてあると
思いますが
- いちおう触れておきます
tcp_tw_reuse が使える条件は?
- TCP Time Stamps Option 有効な接続
- net.ipv4.tcp_timestamps = 1(default) で、client も
server も TCP Time Stamps Option 有効 なとき
- かつ、 source の ip と port、 destination の ip
と port が一致してるとき
- __inet_check_established() が INET_MATCH() でみ
てる
ざっくり仕組みを書くと
- source と destination の ip と port が一致した
ら、TIME_WAIT の socket が使ってる
Ephemeral port の再利用を試みるのだが
- tcp_tw_reuse は「TIME_WAIT になって一秒
以上経過した socket」を再利用する
- 何と比較して一秒と判断するかというと、 Active
Close側がFIN+ACK受け取ったときの時間を見
てる
TCP的に問題ない範囲でTW切り上げる
- Active Close 側が送信した最後の ACK が受
信できていた場合
- Passive Close 側に socket 残ってないので、
TIME_WAIT を一秒で切り上げてもOK
- Active Close 側が送信した最後の ACK が受
信できてなかった場合
- Passive Close 側は LAST_ACK で待っているが、(雑
にいうと) TCP Timestamps が効いて(PAWSが効い
て)、SYN 再送しつつ connection 張れる
雑に描くとこう
SEQ = Sequence number
TS = Time stamp
ecr = Time stamp echo reply
timestamp や sequence
number はざっくりしたイメージ
です。
実際のものと、ずらす値や桁数
は異なります。
例えば、tcp_tw_reuse で
sequence number ずらすとき、
実際のソースコードでは
+65535+2 されてます。
- PAWSというものが RFC7323 で定義されてい
る。(雑にいうと)高速な回線では、 sequence
number だけでは不充分。そこで TCP
TimeStamps Option で TCP header につけら
れた timestamp を見て、受信側は packet を破
棄したりできる。timestamp は 1msec ~ 1sec
間隔で更新されるのが RFC で良いとされるて
いるので
tw_reuse が一秒を基準にするのは
- TIME_WAIT に遷移してから 1sec 後に再利用
すれば、TCP header の timestamp が必ず更
新されており、遅延してやってきた
(TIME_WAITなsocketを再利用する前の)
packet があったとしても、遅延してきた packet
は timestamp 古いから、受信側が破棄でき
るってことではないかなぁ。たぶん
Time Stamp Option に守られてないと
- LAST_ACK で待ってる Passive Close 側に
SYN 送ると、 RST が返ってくる。 SYN_SENT
で RST 受けると、 ECONNREFUSED になっ
てしまう。
- TimeStamp Option が有効だと、 ACK の確認
して RST する前に、 PAWS で再送されるか
ら、それで助かるって設計のようだ。
4. 接続先の MySQL や(略)
- tcp_tw_reuse は有効な手段だけど、
INET_MATCH() でマッチしないと使えないので
- WEBサーバから見たとき、 接続先の DB や
KVS が多いと、マッチしない可能性がある
- よって、 tcp_tw_reuse を活用したいなら、接続
先の DB や KVS などの ip と port の組み合わ
せは、少ないほうが望ましい
ただ、そもそもの話として
クライアントライブラリが、
そのソフトウェアで
定義されたプロトコルを
活用できてない可能性があります
COM_QUIT や quit
- MySQL のプロトコルには COM_QUIT、
memcached には quit というコマンドがありま
す
- これらをクライアントから送ると、サーバ側から
close(2) してくれるので、本来、 TIME_WAIT
は mysqld や memcached 側にしか残らない
のが正しいはずです
だがしかし
ここで MySQL の
クライアントライブラリの
ソースコードを
読んでみましょう
MySQL 5.7.18 では
- mysql_close() では、 COM_QUIT 送ってから
end_server() 呼んでるのだが、 COM_QUIT
送るところで skip_check flag 立ってるので、
recv(2) などされず に、最終的に shutdown(2)
& close(2) されている。
- ということは、タイミング次第で RFC793の P.39
の Simultaneous Close Sequence になる可能
性がある。 recv(2) して欲しいなぁ
残念ながら
- COM_QUIT を送ってる client にも mysqld 側
にも、 TIME_WAIT が残ってしまう可能性があ
る
- じっさいこれ見たことあります。 Simultaneous Close
Sequence に入っちゃってるのでしょう。
なので
- Feature request を出してみました
- https://bugs.mysql.com/bug.php?id=8635
6
- 直して欲しい方は、お手数ですが Impact on
me: のところで Affects Me を押して頂けると助
かります。ボタン押すのに Oracle アカウントが
必要なんですが、無料でとれます。
といったところなんですが
基本的に
- DB や KVS に接続しまくるWEBサーバは、
tcp_tw_reuse 有効にすれば、 TIME_WAIT が
溜まっててもだいたい動くと思うけど
- 次のような条件は環境によって異なるので、ど
れくらい reuse できるかは環境依存
- WEBサーバが同時に connect(2) する数
- すなわち、 (thread や process の数) * (一回の
requestで接続されるDBやKVS)
いろいろMonitoringしても難しい
- /proc で TIME_WAIT は数えられるけど、
tcp_tw_reuse で再利用できる TIME_WAITの
数を数えるのは難しいので
- まずは次の式を満たす範囲で運用して
- (ip_local_port_range の 上限 - 下限) >= TIME_WAIT
の総数
- どれくらい reuse されるかは、徐々に試せば良
いのでは
まとめ
- TIME_WAIT の数が気になるなら、先ずはイン
スタンスの数を並べてみては?
- それでもスケールアップしたいなら、できれば
kernel4.4 以降にして、 Monitoring しつつ、次
の設定を変えてみては
- net.ipv4.ip_local_port_range
- net.ipv4.ip_local_port_reserved_ports
- net.ipv4.tcp_tw_reuse
おわり

More Related Content

TIME_WAITに関する話

  • 3. 自己紹介 - まぁまぁ MySQL でご飯食べてます - 一時期は Resource Monitoring や KVS にも 力入れてました - ネットワーク的には素人です - Linuxとハードウェアは嗜む程度 - disk I/O にはむかしから興味あります - その他 slideshare はこちら - http://www.slideshare.net/takanorisejima/
  • 4. 本日のお題 - kernel 新しくしたりすると、TCP的に意識したほ うが良い変化が見つかるので - 今日は、Webアプリケーションサーバの観点か ら、 connect(2) する際に気になる TIME_WAIT について、書いてみようかと思います - 有識者からのマサカリを、強く歓迎いたします
  • 5. 最初に参考資料 - この二つの記事を読んでいただけば、それで概 ね良いと思うんですが - Linuxカーネルの「TCP_TIMEWAIT_LEN」変更は無意味? - Coping with the TCP TIME-WAIT state on busy Linux servers - これらの記事をなぞりつつ、もうちょっと込み 入った話をしてみようと思います。
  • 6. 対象とする環境 - Ubuntu 14.04 LTS - kernel 3.13 or 4.4 - 余談ですが、 EC2 で一番使われてるOSは、 Ubuntu らしいですね。 - Amazon EC2でもっとも人気のあるOSはUbuntu - なので Ubuntu を対象にするのは無難かなと思 います。また、16.04 LTS の GA kernel は 4.4 なので、応用効くと思います。
  • 10. kernel 3.13 だとここらへん - tcp_v4_connect() から読んでくと - net/ipv4/tcp_ipv4.c#L223 - inet_hash_connect() - __inet_hash_connect() で inet_get_local_port_range() で connect(2) 時に割り当てできる Ephemeral port の range をとって、ひたすら for ループ回して、 空いてる port 見つからなかったら
  • 11. return -EADDRNOTAVAIL; - ここに来るはず - inet_get_local_port_range() は net.ipv4.ip_local_port_range の min/max を返す関数 で、connect(2) したとき ip_local_port_range で指定し た範囲で port 割り当てられなかったらエラー。 - ESTABLISHED や TIME_WAIT で socket 大 量に開いてて Ephemeral port 割り当てできな いと、Linux 的には EADDRNOTAVAIL 返 す。
  • 12. TCP 的になぜ TIME_WAIT はあるのか - 理由は二つあって、一つは a. FIN 受けた側(Passive Close)側が 、 FIN 送った (Active Close)側に FIN+ACK を返して、 LAST_ACK に遷移した後 b. Active Close 側が FIN+ACK への ACK を Passive Close 側に返したんだけど、 packet 落ちても c. Active Close 側が TIME_WAIT で待ってれば、 Passive Close 側が FIN+ACK 再送すれば、Active Close 側がACK再送できる(ACK来たら LAST_ACK から、 直ちに CLOSED に遷移できる)
  • 14. TCP 的になぜ TIME_WAIT はあるのか - もう一つは a. Active Close 側が送信した packet を、Passive Close 側が、(packet 落ちるかなんかして)受信できてない状 態になって b. その後すぐまた connect(2)し、同じ送信元 {ip,port} と 送信先 {ip,port} のセットで通信がはじまって c. a. で受信できてなかった packet がたまたま遅れてやっ てきて、それが TCP の sequence number 的にかぶっ てて packet が受けられてしまうと不味い
  • 15. ただ、これらの問題は - RFC 7323 で定義されてる TCP Time Stamp Option と PAWS で回避できます。 - tcp header に timestamp つけることで、sequence number が一周しても、 timestamp を比較することに よって、受信側は古い packet かどうか判断できます。 - ただ Option なので、無効化されている環境もありえま す。 - ややこしい話なので後述します。
  • 16. というわけで - これら二つの TCP 的な目的から TIME_WAIT という状態が必要で - TIME_WAIT で待ち続けている socket が多い 状態で connect(2) すると、 Linux は EADDRNOTAVAIL を返す可能性がある。 - そうであるならば、これら二つの TCP 的な目的 を満たしつつ、 EADDRNOTAVAIL が発生しな い状況にすればよい。
  • 18. はじめに大前提 - TCP 的に port が 2byte のデータなら、パブリッ ククラウド使ってるなら、インスタンスをスケール ダウンして数並べれば良いんじゃないかと思い ます。 - EC2 の c4.large でも c4.8xlarge でも、 TCP 的に使え る port が 0-65535 なのは変わらないわけです。 - あるいは、コネクションプーリングできるなら、そ れでも良いと思います。
  • 19. ただそれでも - スケールアップして集約できることに、メリットが ある場合もあります。 - EC2 の c4.8xlarge は、使える vCPU が多いとか - 使っているインスタンス or サーバの数が少ないと、監視 などの面で楽だとか。 - というわけで、高性能なサーバを上手く使おうと するとき、 TIME_WAIT とどう付き合うか、とい うのを考えたいわけです。
  • 20. ではどうするか 1. 先ずは Monitoring する。 a. kernel 4.4 にして、 Monitoring の精度を上げる。 b. Ephemeral port の使用状況をざっくり Monitoring す る。 2. ip_local_port_range を変更する。 a. (必要であれば)ip_local_reserved_ports を指定 3. net.ipv4.tcp_tw_reuse = 1 にする。 4. 接続先の MySQL や KVS を集約して、 tcp_tw_reuse で TIME_WAIT の socket を再利用しやすくする。
  • 21. 1. 先ずは Monitoring する - (個人的に)継続的な Monitoring は、すべての 基本だと思うんで、先ずは Monitoring - TIME_WAIT の数をざっくり調べるには - /proc/net/sockstat の tw - なぜざっくりかというと、(少なくとも Ubuntu の) kernel 3.13 だと、 TIME_WAIT の socket が、 60sec (TIME_WAIT_LEN)経っても回収 されるとは限らないからです
  • 22. netstat -o あるいは --timers - netstat には -o というオプションがあります - Include information related to networking timers. - kernel 3.13 で TIME_WAIT 多いサーバで次の コマンドを打つと、 timewait (0.00/0/0) がけっこ う残ってることがあるのですが - $ netstat -nato | egrep -c 'timewait.*(0.00' - これは TIME_WAIT を回収できるまでの残り時 間で、 60sec 以上経ってるから 0です
  • 23. なぜ kernel 4.4 にすると良いのか? - kernel 4.4 の場合、 60sec 経つと、速やかに TIME_WAIT 回収されるようです。 - ただ、なんでこのあたりの修正が効くのかは、い まの私にはわかりませんでした。 - kernel4.1 で入ったこの patch で性能改善した 結果なんでしょうか?
  • 25. Ephemeral port をざっくり数える - Webサーバの場合、DBなどに接続するだけで なく、 Reverse Proxy とか ELB から接続され たりするので、それらをWebサーバから close(2) すると TIME_WAIT になりますが、こ うやって数えることができます - $ netstat -nat | grep -v '127.0.0.1' | egrep -c ' 1[0-9]+.[0-9]+.[0-9]+.[0-9]+:80 +[0-9]+.[0-9]+.[0-9]+.[0-9]+:[0-9]+ .*TIME_WAIT'
  • 26. TCP:80 の TIME_WAIT を上手く除外 - connect(2) するときに EADDRNOTAVAIL 返 るのが困るなら - Web サーバが LISTEN してる TCP:80 で残っ てる TIME_WAIT(Reverse Proxy や ELB な どに対して close(2) して残った TIME_WAIT) を除外すると - connect(2) に影響する、Ephemeral port の TIME_WAIT を数えやすくなるわけです
  • 27. 2. ip_local_port_range を変更する - LISTEN してる port と被らないなら、 ip_local_port_range を変更するのは確実 - kernel 3.13 では 32768 - 61000、 kernel 4.4 では 32768 - 60999 が default - 例えば、下限を 32768 から 24225 にするだけ で、使える Ephemeral port が約30%増加 - よく使われてる fluentd は default で tcp:24224 を使う ので、被らないように 24225。
  • 28. net.ipv4.ip_local_reserved_ports - fluentd で tcp:24224 を LISTEN してるんだけ ど、net.ipv4.ip_local_port_range もっと下の range まで指定したいときは、 ip_local_reserved_ports に、 LISTEN したい port を列挙しておけば良いです。 - __inet_hash_connect() の中で予約された port を使わ ないように見てる ので
  • 29. 3. net.ipv4.tcp_tw_reuse = 1 にする - ようやく出てきました tcp_tw_reuse - これについては Coping with the TCP TIME-WAIT state on busy Linux servers が最 高に良い資料で、ほとんどここに書いてあると 思いますが - いちおう触れておきます
  • 30. tcp_tw_reuse が使える条件は? - TCP Time Stamps Option 有効な接続 - net.ipv4.tcp_timestamps = 1(default) で、client も server も TCP Time Stamps Option 有効 なとき - かつ、 source の ip と port、 destination の ip と port が一致してるとき - __inet_check_established() が INET_MATCH() でみ てる
  • 31. ざっくり仕組みを書くと - source と destination の ip と port が一致した ら、TIME_WAIT の socket が使ってる Ephemeral port の再利用を試みるのだが - tcp_tw_reuse は「TIME_WAIT になって一秒 以上経過した socket」を再利用する - 何と比較して一秒と判断するかというと、 Active Close側がFIN+ACK受け取ったときの時間を見 てる
  • 32. TCP的に問題ない範囲でTW切り上げる - Active Close 側が送信した最後の ACK が受 信できていた場合 - Passive Close 側に socket 残ってないので、 TIME_WAIT を一秒で切り上げてもOK
  • 33. - Active Close 側が送信した最後の ACK が受 信できてなかった場合 - Passive Close 側は LAST_ACK で待っているが、(雑 にいうと) TCP Timestamps が効いて(PAWSが効い て)、SYN 再送しつつ connection 張れる
  • 34. 雑に描くとこう SEQ = Sequence number TS = Time stamp ecr = Time stamp echo reply timestamp や sequence number はざっくりしたイメージ です。 実際のものと、ずらす値や桁数 は異なります。 例えば、tcp_tw_reuse で sequence number ずらすとき、 実際のソースコードでは +65535+2 されてます。
  • 35. - PAWSというものが RFC7323 で定義されてい る。(雑にいうと)高速な回線では、 sequence number だけでは不充分。そこで TCP TimeStamps Option で TCP header につけら れた timestamp を見て、受信側は packet を破 棄したりできる。timestamp は 1msec ~ 1sec 間隔で更新されるのが RFC で良いとされるて いるので tw_reuse が一秒を基準にするのは
  • 36. - TIME_WAIT に遷移してから 1sec 後に再利用 すれば、TCP header の timestamp が必ず更 新されており、遅延してやってきた (TIME_WAITなsocketを再利用する前の) packet があったとしても、遅延してきた packet は timestamp 古いから、受信側が破棄でき るってことではないかなぁ。たぶん
  • 37. Time Stamp Option に守られてないと - LAST_ACK で待ってる Passive Close 側に SYN 送ると、 RST が返ってくる。 SYN_SENT で RST 受けると、 ECONNREFUSED になっ てしまう。 - TimeStamp Option が有効だと、 ACK の確認 して RST する前に、 PAWS で再送されるか ら、それで助かるって設計のようだ。
  • 38. 4. 接続先の MySQL や(略) - tcp_tw_reuse は有効な手段だけど、 INET_MATCH() でマッチしないと使えないので - WEBサーバから見たとき、 接続先の DB や KVS が多いと、マッチしない可能性がある - よって、 tcp_tw_reuse を活用したいなら、接続 先の DB や KVS などの ip と port の組み合わ せは、少ないほうが望ましい
  • 41. COM_QUIT や quit - MySQL のプロトコルには COM_QUIT、 memcached には quit というコマンドがありま す - これらをクライアントから送ると、サーバ側から close(2) してくれるので、本来、 TIME_WAIT は mysqld や memcached 側にしか残らない のが正しいはずです
  • 44. MySQL 5.7.18 では - mysql_close() では、 COM_QUIT 送ってから end_server() 呼んでるのだが、 COM_QUIT 送るところで skip_check flag 立ってるので、 recv(2) などされず に、最終的に shutdown(2) & close(2) されている。 - ということは、タイミング次第で RFC793の P.39 の Simultaneous Close Sequence になる可能 性がある。 recv(2) して欲しいなぁ
  • 45. 残念ながら - COM_QUIT を送ってる client にも mysqld 側 にも、 TIME_WAIT が残ってしまう可能性があ る - じっさいこれ見たことあります。 Simultaneous Close Sequence に入っちゃってるのでしょう。
  • 46. なので - Feature request を出してみました - https://bugs.mysql.com/bug.php?id=8635 6 - 直して欲しい方は、お手数ですが Impact on me: のところで Affects Me を押して頂けると助 かります。ボタン押すのに Oracle アカウントが 必要なんですが、無料でとれます。
  • 48. 基本的に - DB や KVS に接続しまくるWEBサーバは、 tcp_tw_reuse 有効にすれば、 TIME_WAIT が 溜まっててもだいたい動くと思うけど - 次のような条件は環境によって異なるので、ど れくらい reuse できるかは環境依存 - WEBサーバが同時に connect(2) する数 - すなわち、 (thread や process の数) * (一回の requestで接続されるDBやKVS)
  • 49. いろいろMonitoringしても難しい - /proc で TIME_WAIT は数えられるけど、 tcp_tw_reuse で再利用できる TIME_WAITの 数を数えるのは難しいので - まずは次の式を満たす範囲で運用して - (ip_local_port_range の 上限 - 下限) >= TIME_WAIT の総数 - どれくらい reuse されるかは、徐々に試せば良 いのでは
  • 50. まとめ - TIME_WAIT の数が気になるなら、先ずはイン スタンスの数を並べてみては? - それでもスケールアップしたいなら、できれば kernel4.4 以降にして、 Monitoring しつつ、次 の設定を変えてみては - net.ipv4.ip_local_port_range - net.ipv4.ip_local_port_reserved_ports - net.ipv4.tcp_tw_reuse