背景
大規模なクローリングを行うバッチシステムを構築している際、TCPコネクションが同時に多数確立することでTIME_WAIT状態のものでいっぱいになっていました。、これにより、利用可能なエフェメラルポートが枯渇してクローリングの時間が長くなりすぎていたので、カーネルパラメータをチューニングしてみました。
参考
- TCPとTIME_WAIT : https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux
- TCPの各種状態について : https://hana-shin.hatenablog.com/entry/2022/08/21/214706
確認
- 使用ポート数
-
netstat | grep tcp | wc -l
などで確認可能
-
- TIME_WAIT
-
netstat -nat | grep TIME_WAIT | wc -l
などで確認可能 - 自身のWEBサーバーでlistenしている場合は
grep -v :80
やgrep -v :443
などで除く必要がある
-
- カーネルパラメータ
-
cat /etc/sysctl.conf
で確認 -
sysctl -a
で一覧確認 -
sysctl -n {param}
で対象パラメータの設定値を確認
-
カーネルパラメータ
今回のような事象に有効なlinuxカーネルのパラメータについて
デフォルトはcentos 7.9.2009での値
net.core.somaxconn
- default :
128
- description : ESTABLISHED状態のソケット上限
- note : アプリケーション側での設定で接続数増やそうと頑張ってもここを増やさないと変わらない
net.ipv4.tcp_max_syn_backlog
- default :
512
- description : SYN_RECEIVED状態のソケット上限
- note :
net.core.somaxconn
の上限値が優先される
net.ipv4.tcp_tw_reuse
- default :
2
- description : TIME_WAIT状態から1秒以上経過したソケットのポートを新しい接続に再利用する
net.ipv4.tcp_fin_timeout
- default :
60
- description : FIN_WAIT_2のタイムアウト設定
- note : TIME_WAITのタイムアウトではない.相手から最後のFINパケットを受け取るまで待機し、TIME_WAITに移行する
net.ipv4.tcp_max_tw_buckets
- default :
NR_FILE*2
- description : システムが許容する TIME_WAIT 状態にあるソケットの最大数
net.ipv4.ip_local_port_range
- default :
32768 60999
- description : 使用可能なポート範囲
- note : netstatで確認した大まかなポート使用数が設定値より十分に小さければ問題ない. 上限は
1024 65535
net.ipv4.ip_local_reserved_ports
- default :
-
- description : 予約済みのポート.port_rangeを広げた場合などに使用
設定
上記を踏まえて高負荷条件下での設定値を考えます
-
net.ipv4.tcp_tw_reuse
を1にする- TIME_WAIT状態が溜まっているような場合はこれがよく効きます
-
net.core.somaxconn
を増やす- こちらはデフォルト値が128と少ないので増やしたほうがいい
- よく見る設定値としては1024や2048でしょうか
-
net.ipv4.tcp_max_syn_backlog
も一緒に増やす
-
net.ipv4.tcp_fin_timeout
を減らす- FIN_WAIT_2状態が溜まっているような場合には有効
- 設定値は状況によって変化しうる
- e.g. バッチなら5秒, WEBサーバーなら30秒とか
一時的に設定する例
sysctl -n {name}={value}
# sysctl -w net.core.somaxconn=1024
net.core.somaxconn=1024
# sysctl -w net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_reuse=1
# sysctl -w net.ipv4.tcp_fin_timeout=30
net.ipv4.tcp_fin_timeout=30
永久的に設定する場合
-
vi /etc/sysctl.conf
: 設定値の書き換え -
sysctl -p
: 更新の反映
# vi /etc/sysctl.conf
# cat /etc/sysctl.conf
net.core.somaxconn = 1024
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_fin_timeout=30
# sysctl -p
例のようにとりあえずシステムに影響のない範囲から始めて、様子見ながら状況に応じて他のパラメータなども設定していくという流れが良いかと思われます。
結果
自分の場合、アプリケーション側でのコネクションプーリングに加え上記の設定を反映させることでクローリング終了までの時間が50h -> 7hくらいに大幅に改善しました。