Manjusaka

Manjusaka

Supervisorの隠れた落とし穴

このクソ API の運搬作業員がまたやってきた、= = 今日は Supervisor の隠れた設定パラメータのせいで、重要なプロジェクトがオンラインでクラッシュした。= = シェアする価値があると思ったので、このクソ記事を書いた。

起因#

コードを書いている最中に、突然大量の警告メールが届き、世界がそんなに美しくないと感じた。

そして、例外情報を見てみると?なんだよ?新しい接続を作成するだけで、有名な [Errno 24] Too many open files が発生するって??くそ、やってやろう。

バグの調査#

まず、周知の通り、Linux ではすべてがファイル = = なので、ネットワーク接続の操作も実際には File Descriptor の操作になるわけだ = = まあ、Too many open files なら、まずはシステムの閾値が小さすぎるのかどうかを考えるべきだろう。だから、ulimit -a で確認してみるか?

え?open files の数字は小さくないじゃん?十分じゃん?じゃあ、これは一体なんなんだよ?

まあ、ネットワーク接続を調べてみよう、一発で netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' で、各状態の接続数を統計しよう。

え?面白いな、TIME_WAIT の数が多すぎるじゃん?え?面白いな、じゃあ俺のカーネルネットワークパラメータの半端ない努力を出して、ちょっと改造してみようか?

#パラメータの最適化
#ソケットがローカルからクローズされる場合、このパラメータはFIN-WAIT-2状態に保持される時間を決定する
net.ipv4.tcp_fin_timeout = 30
#keepaliveが有効な場合、TCPがkeepaliveメッセージを送信する頻度。デフォルトは2時間で、300秒に変更する
net.ipv4.tcp_keepalive_time = 300
#SYN Cookiesを有効にする。SYN待ちキューがオーバーフローした場合、クッキーを使用して処理する。デフォルトは0で、無効
net.ipv4.tcp_syncookies = 1
#ソケットがローカルからクローズされる場合、このパラメータはFIN-WAIT-2状態に保持される時間を決定する
net.ipv4.tcp_tw_reuse = 1
#TCP接続中のTIME-WAITソケットのクイックリサイクルを有効にする。デフォルトは0で、無効。現在のTIME-WAITが多いため、クイックリサイクルを有効にする
net.ipv4.tcp_tw_recycle = 1
net.ipv4.ip_local_port_range = 5000 65000

上記の設定項目を /etc/sysctl.conf に追加する。そして、sysctl -p で有効にする。効果を見てみよう。

え!エラーの数が減っているし、TIME_WAIT の数も徐々に正常になっている。

え???ちょっと待って?他のマシンには TIME_WAIT の問題がないのに、これは一体なんなんだ?しかも、TIME_WAIT の接続をクイックにリサイクルすると、他の副作用も発生する(後の章で説明する)

= = まあいい、今疑っているのは Supervisor の問題かどうかだから、いいよ、ドキュメントを見てみよう、始めよう

うん、半日かけて調べた結果、minfds というパラメータに関連していることがわかった

説明は以下の通り

supervisord が正常に起動するために必要なファイルディスクリプタの最小数。setrlimit を呼び出して、supervisord プロセスのソフトリミットとハードリミットを上げて minfds を満たすようにする。ハードリミットは、supervisord が root として実行されている場合にのみ上げることができる。supervisord はファイルディスクリプタを自由に使用し、OS から取得できない場合には障害モードに入るため、実行中にそれらが不足しないようにするために、最小値を指定することは有用である。これらの制限は、管理されているサブプロセスにも継承される。このオプションは特にデフォルトでプロセスごとの fd 制限が低い Solaris で有用です。

要するに、Supervisor が起動する際に、minfds の値に基づいてシステムに十分な空き fd があるかどうかを確認する。同時に、Supervisor で実行されるサービスはすべて Supervisord からフォークされるため、親子関係で安全を確保しながら、単一のプロセスで開かれるディスクリプタの最大数は、minfds で設定された値を超えないようにする。デフォルトでは 1024 になっている。そして、もし root ユーザーで実行するなら、最大の値をデフォルトで設定するよ!

くそ。。。そうだったのか、誰が root ユーザーでサービスを実行するんだよ = = まったく。。。

まあ、パラメータを変更しよう、変更しよう

その後#

最後に、この問題はこれで終わりだ、雷に遭ったけど、カーネルのネットワークパラメータを復習できたし、気持ちいい感じだけど、でも、君の Supervisor は薬を飲め!

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。