akishin999の日記

調べた事などを書いて行きます。

Linux サーバでの「Too many open files」対策について

Linux サーバでの「Too many open files」エラー対策について調べたのでまとめてみました。
確認した OS は CentOS 5.9 と CentOS 6.3 です。

「Too many open files」は Linux でプロセスが開けるファイルディスクリプタの上限に達してしまうと発生するエラーです。
「ファイルディスクリプタ」という名前ですが、 Linux ではソケットもファイルディスクリプタなので、ファイルを開いた場合だけでなく、ソケットを使って通信を行う場合にもファイルディスクリプタが使用されます。
そのため、Apache や Tomcat などで高負荷なサイトを運用している場合などには、比較的遭遇する確率の高いエラーではないでしょうか。

このエラーを回避するため、プロセスがオープンできるファイルディスクリプタの上限を変更します。

まずは以下のコマンドを実行し、現在ログイン中のユーザの open files の値を確認します。

# ulimit -n
1024

デフォルトでは 1024 に設定されているようですね。
これだと負荷が高めのサーバでは使い切ってしまう事もありそうです。

この制限値を上げるためには、/etc/security/limits.conf ファイルを編集します。

# vi /etc/security/limits.conf 

とりあえずここでは

  • 上限は 65536
  • 全ユーザを対象
  • ソフトリミット、ハードリミット共に同じ値

という事で、以下の設定を追記しました。

* soft nofile 65536
* hard nofile 65536

limits.conf は PAM 認証のモジュールである pam_limits.so の設定ファイルで、ログインの際に読み込まれるファイルです。
なので、一旦 su - して再読み込みをした後に再度値を確認してみます。

# su -
# ulimit -n
65536

設定が反映されましたね。

次にデーモンプロセスの設定を確認します。
ここでは Apache を例に調べてみました。

プロセスの場合は設定変更後にプロセスを再起動すると反映されます。

# service httpd restart

Apache の場合、プロセス毎のオープン可能なファイルディスクリプタの数は以下のようにして確認できます。

# cat /proc/`pgrep httpd | head -1`/limits | grep 'open files'
Max open files            65536                65536                files

無事増えていますね。

これで終わり・・・なら話は簡単なのですが、実際に運用していると OS を再起動しなければならなくなることもあります。
というわけで OS 自体を再起動して、自動起動になっているデーモンプロセスの Max open files がちゃんと増加しているかを確認してみました。

# reboot

再起動が完了したら再度確認コマンドを実行。

# cat /proc/`pgrep httpd | head -1`/limits | grep 'open files'
Max open files            1024                 1024                 files

あれ?
何故か値が戻ってしまっていますね。

どうやら OS 起動時のデーモンは PAM 認証を通らないため、limits.conf の設定が反映されない、という事のようです。
この現象を回避するためには init.d 以下の起動スクリプト内で個別に「ulimit -n 65536」を指定すればいいのですが、複数のプロセスに指定する必要がある場合など、設定変更の度にいちいち個別のファイルを編集するのは面倒です。

何か良い方法はないかと思っていろいろと調べてみたところ、自動起動する全てのデーモンに設定を反映するための方法がちゃんと用意されていました。

Man page of INITSCRIPT
http://linuxjm.sourceforge.jp/html/SysVinit/man5/initscript.5.html

これは正に目的に合いそうです。
というわけで、ここでは /etc/initscript ファイルを作成し、そちらに以下のように記述しました。

デフォルトではファイル自体が存在しないようなので作成します。

# vi /etc/initscript

内容は以下。

ulimit -n 65536
eval exec "$4"

保存したら再度 OS を再起動します。

# reboot

再起動後に確認すると、今度は無事に反映されていました!

# cat /proc/`pgrep httpd | head -1`/limits | grep 'open files'
Max open files            65536                65536                files

これで CentOS 5.9 では OS 再起動時にもファイルディスクリプタの最大数を変更しておくことが出来るようになりました。
負荷テストなどを行えば分りますが、「Too many open files」エラーはほぼ発生しなくなったことが分かるかと思います。

しかし、実は CentOS 6.3 では上記の手順では OS 起動時のデーモンの設定を変更することができません。
RHEL6 からはデーモンの起動に Upstart が使われるようになったのですが、それによりどうやら /etc/initscript が使用されなくなってしまったようです。

何か方法はないか、しばらく検索してみると、ulimit の設定に /etc/sysconfig/init を使用しているという記事がありました。

[RHEL][Upstart] Upstartにおけるulimit設定とcore dump | 半袖野郎 blog.hansode.org
http://blog.hansode.org/archives/52521158.html

ということで /etc/sysconfig/init を使用してみます。

# vi /etc/sysconfig/init

ファイルの末尾に以下を追記しました。

ulimit -n 65536

これで OS を再起動してみると、今度は無事 ulimit の設定が反映されていました!

ただ、検索しても他にこれといった情報が出てこないので、Upstart のお作法的にこの方法が適切か、というのははっきりとはわかりませんでした。
Upstart での /etc/initscript に相当するファイルって存在しないんでしょうか?

とりあえず手元の環境では今のところこの設定で全く問題なく動作しています。

参考

ファイルディスクリプタ数の上限変更とlimits.confの罠 (ゆめ技:ゆめみスタッフブログ)
http://yumewaza.yumemi.co.jp/2010/07/limitsconf.html

ulimitが効かない不安を無くす設定 | 外道父の匠
http://blog.father.gedow.net/2012/08/08/ulimit-configuration/