👻

nginxのキャッシュ階層を深くしすぎてinodeが枯渇した

2020/10/01に公開

nginxのproxy_cache_pathの設定がよくなかったためにinodeが枯渇してエラーになったので、メモしておきます。

設定

以下のようにしていました。

proxy_cache_path  /var/nginx/cache levels=2:2:2 keys_zone=hoge:10m inactive=10m max_size=100m;

levels

今回の現象に関係あるlevelsについて説明します。

levelsはキャッシュファイルのディレクトリ階層を設定するパラメータです。
levels=2:2:2だと「2文字を3階層」になります。

キャッシュのキーのMD5がb7f54b2df7773722d382f4809d65029cの場合だと、キャッシュのファイルのフルパスは以下のようになります。

/var/nginx/cache/9c/02/50/b7f54b2df7773722d382f4809d65029c

この設定により、ひとつのディレクトリに全キャッシュファイルを置くのではなく、ディレクトリを作って複数のディレクトリに分散させてファイルを置いていくことができます。

これは、Linuxのファイルシステムのひとつであるext3で、ひとつのディレクトリにファイルを大量に置くとパフォーマンスが低下する問題への対応策だと思われます。

Linuxにはあまり詳しくないので、指摘等あればコメントお願いします。

パラメータの詳細についてはnginxのドキュメントを見てください。

ディレクトリ数

それならばファイルが複数ディレクトリになるべく分散したほうが良いと考えてlevels=2:2:2にしていました。

さて、MD5のHEX文字列から2文字のディレクトリを作るとすると00からffまでの256のディレクトリが作られます。

3階層だと256 * 256 * 256で最大16,777,216ものディレクトリが作られることになります。

max_size

proxy_cache_pathにはmax_sizeというパラメータがあり、キャッシュ全体のサイズの上限を管理しています。

キャッシュがこのサイズを超えると、最も使われていないファイルを削除してくれるようになっています。

ところが、この機構は空っぽになったディレクトリは削除しません。

max_sizeを小さい値に設定しておいてキャッシュのファイルが増えすぎないようにしてもディレクトリが残るので、inodeが消費されていく一方です。

inactiveによる削除についても同様です。

当初、キャッシュディレクトリの合計サイズを調べて「max_sizeを超えているのに削除されない」と思っていましたが、nginxが見ているのがキャッシュのファイルサイズだけのようなので、大量ディレクトリによるスペース消費は抑制できないためでした

まとめ

多階層のlevelsによって大量にディレクトリが作られることによって、inodeが枯渇してエラーになるという事態が発生してしまいました。

また仮にinodeに余裕があったとしても、1ディレクトリが4096バイトを使うので、16,777,216 * 4096で最大64GBを消費するので、場合によってはスペースが先に枯渇するかもしれません。

さて、階層を浅くすると、ファイルの分散の度合いは下がります。

ただし今はmax_size=100mにしているため、キャッシュの平均サイズが1KBと考えるとファイル数は10万程度までしか増えません。

このファイル数であれば「2文字1階層」の256ディレクトリでも、1ディレクトリ平均400ファイルになるので大丈夫そうです。

max_sizeを増やす可能性があるので、ひとまずlevels=2:2に設定を変更することになったのでした。

この記事はQiitaの記事をエクスポートしたものです

Discussion