ファイルディスクリプタと上限の診断
目標
ファイルディスクリプターの制限が何層あるかを確認し、fdリークを追跡するツールを手で作ります。
なぜ重要なのか
Too many open filesを見ると、ほとんどの人がulimit -n 65536を実行します。しかし、その値は自分のシェルの値であり、すでに動いているサービスには何の影響もありません。確認すべき場所は/proc/<PID>/limitsで、実際の使用量はls /proc/<PID>/fd | wc -lです(lsof -pはcwd・実行ファイル・mmapされたライブラリまで数えますが、これらは制限に含まれません)。そして、制限を上げることが答えになる場合はまれです。たいていはリークで、そのとき制限を引き上げるのは、障害を数時間先送りするだけです。リークなのか負荷なのかは、fd数の増加パターンで見分けます。
ステップ
/root/fdディレクトリで作業します。
/proc/1/limitsから開いているファイルの制限を読み取り、/root/fd/limits.txtに次の2行で書いてください(プレースホルダーは値です)。soft=<값>hard=<값>
/etc/hostnameを50回以上同時に開いているプロセスをバックグラウンドで起動し、PIDを/root/fd/holder_pid.txtに書いてください。以降のステップの間ずっと生きている必要があります。- そのプロセスのfdのうち
/etc/hostnameを指しているものの個数と、それらが指している異なる対象の個数を、/root/fd/distinct.txtに次の2行で書いてください(プレースホルダーは個数です)。count=<개수>distinct=<개수>
- 制限を下げた環境でファイルを開き続けて
Too many open filesエラーを再現し、そのメッセージを/root/fd/emfile.txtに保存してください。 - システム全体のfd上限を、数字だけ
/root/fd/file_max.txtに書いてください。 python3 -m http.server 8099 --bind 127.0.0.1をバックグラウンドで起動し、PIDを/root/fd/http_pid.txtに書いてください。/root/fd/fdtop.sh <N>を作成してください。開いているfdの個数が上位N個のプロセスを、<fd개수> <PID>の形式で降順に出力します(プレースホルダーはfdの個数とPIDです)。ちょうどN行である必要があります。/etc/hostnameのinode番号を/root/fd/inode.txtに書いてください。
参考
python3 -c "fs=[open('/etc/hostname') for _ in range(60)]; import time; time.sleep(9999)" &で、fdを握らせることができます。リストに入れておかないと、すぐに閉じられます。bash -c 'ulimit -n 32; python3 -c "..."' 2> /root/fd/emfile.txtのように、サブシェルで制限を下げられます。/proc/sys/fs/file-nrは、할당된 수 / 미사용 할당분 / 최대값の3つの数字です(韓国語の3つの語は、順に「割り当て済みの数」「未使用の割り当て分」「最大値」を意味します)。- よくあるミス1: ステップ7で
/proc/selfや、すでに終了したプロセスのためにエラーが出ます。失敗する項目はスキップしてください。 - よくあるミス2: ステップ4でエラーを標準出力で受け取ろうとすると、何も入りません。エラーは標準エラー出力に出ます。
本当の制限を読み取る
/proc/1/limitsから開いているファイルの制限を読み取り、/root/fd/limits.txtに次の2行で書いてください(プレースホルダーは値です)。
soft=<값>hard=<값>
自分のシェルのulimitではなく、そのプロセスの/proc//limitsを見る必要があります。「Max open files」の行に、softとhardが並んでいます。
fdを50個握る
/etc/hostnameを50回以上同時に開いているプロセスをバックグラウンドで起動し、PIDを/root/fd/holder_pid.txtに書いてください。以降のステップの間ずっと生きている必要があります。
python3で同じファイルを何度も開いてリストに入れておき、sleepすればよいです。変数に入れておかないと、すぐに閉じられます。
fdの個数と対象の個数を数える
そのプロセスのfdのうち/etc/hostnameを指しているものの個数と、それらが指している異なる対象の個数を、/root/fd/distinct.txtに次の2行で書いてください(プレースホルダーは個数です)。
count=<개수>distinct=<개수>
/proc//fdの各項目をreadlinkして、対象を確認してください。同じファイルを何度も開くとどうなるかを見てください。
EMFILEを再現する
制限を下げた環境でファイルを開き続けてToo many open filesエラーを再現し、そのメッセージを/root/fd/emfile.txtに保存してください。
サブシェルでulimit -nを下げ、その中でファイルを開き続けてください。エラーメッセージをファイルに保存します。
システム全体の上限を読み取る
システム全体のfd上限を、数字だけ/root/fd/file_max.txtに書いてください。
/proc/sys/fs/file-nrは数字が3つです。最後が最大値です。
ソケットもfdです
python3 -m http.server 8099 --bind 127.0.0.1をバックグラウンドで起動し、PIDを/root/fd/http_pid.txtに書いてください。
python3 -m http.serverを127.0.0.1で起動して、そのプロセスのfdをreadlinkしてみてください。
fd上位のプロセスを取り出す
/root/fd/fdtop.sh <N>を作成してください。開いているfdの個数が上位N個のプロセスを、<fd개수> <PID>の形式で降順に出力します(プレースホルダーはfdの個数とPIDです)。ちょうどN行である必要があります。
/proc/*/fdの項目数を数えて、並べ替えます。権限エラーが出る項目はスキップしてください。
fdからinodeまで追跡する
/etc/hostnameのinode番号を/root/fd/inode.txtに書いてください。
stat -Lでシンボリックリンクをたどると、そのfdが指しているファイルのinodeがわかります。