「httpd」や「tomcat」などを運用していて多くのユーザーにアクセスされたときに、プロセスに障害が発生することがあります。
ログを調査すると「Too many open files」がエラーを出力することがあります。
原因は多くのファイルをオープンし過ぎたためなのですが、オープンできるファイル数は「ファイルディスクリプタ」で設定することができます。
ファイルディスクリプタの設定は、数多くのファイルをオープンするアプリケーションで必要になります。
ファイルディスクリプタとは?
ファイルとは、そのまま「ファイル」です。
テキストファイル、バイナリファイル、画像ファイル、オフィスファイルなどです。
「ディスクリプタ」とはなんでしょうか?
ディスクリプタとは「descriptor」です。
descriptor の意味は「記述子、記述語」です。
「記述子、記述語」とは、ファイルやデータの構造や性質(属性)などの情報を記載しているデータのことを言います。
コマンドでファイルディスクリプタを調べてみます。
[root@cent07 fd]# ls /proc/493/fd ← プロセス「493」がいくつファイルディスクリプタを利用しているかチェックします。 |
ここで分かることは、
- ファイルディスクリプタは「整数」
- /dev/null
- socket
- PID
ということでしょうか。
一般的にファイルディスクリプタとは
- 0 → stdin
- 1 → stdout
- 2 → stderr
と説明されます。
つまり「整数」です。
ファイルディスクリプタの数が足りなくなるとどうなるか?
たとえば httpd プロセスのファイルディスクリプタが足りなくなると、プロセスの挙動がおかしくなり、その結果サイトにアクセスできなくなります。
しかし制限をしなければ無制限にプロセスを起動してしまいメモリが枯渇してしまいます。
そのためサーバーのリソースにマッチしたチューニングをする必要があります。
OS全体のファイルディスクリプタ数の制限とプロセスごとのファイルディスクリプタ数の制限がある
ファイルディスクリプタ数の制限値は2種類あります。
- OS全体のファイルディスクリプタ数
- プロセスごとのファイルディスクリプタ数
OS全体のファイルディスクリプタ数の制限値を上げても、個々のプロセス単位でもファイルディスクリプタ数の制限値を上げないと、プロセスには何の影響もありません。
例えば、tomcat のような個々のプロセスでファイルディスクリプタを消費するような場合は、プロセスごとにファイルディスクリプタの制限数を変更する必要があります。
ファイルディスクリプタの変更手順
- OS全体の設定
- 個々のプロセス単位の設定
を説明します。
/etc/sysctl.conf で設定する方法
「/etc/sysctl.conf」は OS 全体で規定するファイルディスクリプタ数の制限値の設定です。
個々のプロセス単位でのファイルディスクリプタ数の制限は別になります。
OS全体の制限数を増やしたからと言って、プロセス(デーモン)1つあたりの制限には影響しません。
そのため、最初に OS 全体のファイルディスクリプタ数を増やしておいてから、この後個々のプロセス(デーモン)のファイルディスクリプタ数を変更します。
[root@cent07 ~]# cat /etc/sysctl.conf
# file descriptor fs.file-max = 65536 ← OS全体のファイルディスクリプタの数を「65536」に設定します。 |
変更したら「sysctl」コマンドで設定を反映します。(OSの再起動は不要です)
[root@cent07 ~]# sysctl -p |
※ただし、この設定は OS 全体の設定なので各プロセス(デーモン)のファイルディスクリプタの設定はデフォルトのままです。(この後、説明します)
/etc/security/limits.conf で設定する方法
「/etc/security/limits.conf」でも設定できますが、「/etc/sysctl.conf」と同じく「/etc/security/limits.conf」で設定した値はログインしない「デーモンプロセス」には効果がありません。
細かく説明すると、理由はログイン時や su コマンド実行時に PAM 認証されたタイミングで /etc/security/limits.conf の設定が適用されるため、デーモンプロセスには効かないということになります。
「/etc/security/limits.conf」の記述方法(設定方法)はファイルに詳しくサンプルが載っているので参考にして設定します。
[root@cent07 system]# cat /etc/security/limits.conf #* soft core 0 # End of file |
記述方法(設定方法)を解説すると、
<domain> <type> <item> <value>
の形式で設定します。
【例】
test soft nofile 65536
test hard nofile 65536
test アカウントの設定を入れています。
【例】アカウント全体(root、ftp、httpd など全て)に適用する場合
* soft nofile 65536
* hard nofile 65536
※*(アスタリスク)で全ユーザーが対象になります。
ulimitで設定する方法
ulimit は設定ファイルではなく「コマンド」です。
ulimit コマンドで個別アカウントを指定することで個別アカウントごとにファイルディスクリプタ数を設定することができます。
ulimit コマンドとは?
ulimit コマンドは、シェルのビルトイン(builtin)コマンドです。
ビルトインコマンドとは「シェルに組み込まれているコマンド」です。
ビルトインコマンドを簡単に説明しますと、例えばビルトインコマンドではない「ls」コマンドを実行するとシェルが中継をしてくれて「/bin/ls」コマンドを実行してくれて、その結果を端末に返してくれます。
しかしビルトインコマンドを実行すると、シェルは中継をせずに自分で実行して結果を端末に返してくれます。
「ならば全部ビルトインコマンドにすればいい」と思うかもしれませんが、全部ビルトインコマンドにしてシェルに組み込むとシェルが巨大化してしまいます。
(巨大化しすぎるといろいろ管理が大変です)
話が飛びましたが、ulimit コマンドはプロセスに割り当てる各リソースの制限を行うために使用します。
起動スクリプトに ulimit コマンドを入れる方法
CentOS6 の場合は起動スクリプトに「ulimit」コマンドを入れることで個々のプロセス(デーモン)に対してファイルディスクリプタの制限値を設定することができます。
CentOS7 の場合は「起動スクリプト」はなくなり「systemd」に変わりました。
CentOS7 の場合の設定方法
CentOS7から「systemd」に変わりましたので、「httpd」を例に設定を説明します。
[root@cent07 ~]# cd /usr/lib/systemd/system
[Service] LimitNOFILE=65536 ← この行を追加します。
[Install] [root@dr-falcon-elif1 system]# |
設定を反映させるためにサービスを再起動します。
[root@cent07 ~]# systemctl restart httpd.service |
tomcat は tomcat プロセスではなく Java が起動している
「httpd」ではなく「tomcat」プロセスに対してファイルディスクリプタの設定をしたい場合もあります。
しかし、ps コマンドを実行しても「tomcat」プロセスなるものは存在しません。
tomcat という名前のプロセスはなく、通常は java プロセスが作られます。
1インスタンスの tomcat のプロセス数は1なので、もし tomcat を1つだけしかインストールしていないけど、 java プロセスが2つあったら tomcat 以外の java アプリが起動しているということになります。
tomcat のファイルディスクリプタの設定の確認方法
現在の設定の確認方法です。
「httpd」と「tomcat」のファイルディスクリプタの制限値を確認しています。
- httpd のファイルディスクリプタの上限値 : 1024
- tomcat のファイルディスクリプタの上限値 : 1024
[root@cent07 system]# cat /proc/`pgrep httpd | head -1`/limits | grep ‘open files’ |
ここで気が付くのは、「Max open files 1024 4096 files」のように数字が2つあるということです。
それぞれの項目は以下のようになっています。
Limit Soft Limit Hard Limit Units
Max open files 1024 4096 files
「ソフトリミット(Soft Limit)」と「ハードリミット(Hard Limit)」の2種類があります。
- ソフトリミット 一般ユーザーが設定したファイルディスクリプタ数の制限値
- ハードリミット 一般ユーザーが設定できるファイルディスクリプタ数の上限
ハードリミットを設定すると、一般ユーザーはそのハードリミットの範囲内でしかファイルディスクリプタ数の値を変更できません。(この数値を超えることができません)
一般ユーザーがハードリミットより大きな値に変更しようとするとエラーになります。
ハードリミットをそれより大きな値に変更する場合は root 権限が必要になります。
man ulimit コマンドより
ulimit [-HSTabcdefilmnpqrstuvx [limit]] Provides control over the resources available to the shell and to processes started by it, on systems that allow such control. The -H and -S options specify that the hard or soft limit is set for the given resource. A hard limit cannot be increased by a non-root user once it is set; a soft limit may be increased up to the value of the hard limit. If neither -H nor -S is specified, both the soft and hard limits are set. The value of limit can be a number in the unit specified for the resource or one of the special values hard, soft, or unlimited, which stand for the current hard limit, the current soft limit, and no limit, respectively. If limit is omitted, the current value of the soft limit of the resource is printed, unless the -H option is given. When more than one resource is specified, the limit name and unit are printed before the value.
If limit is given, it is the new value of the specified resource (the -a option is display only). limitを指定すると、指定されたリソースの新しい値になります(-aオプションは表示のみ)。 If no option is given, then -f is assumed. オプションを指定しない場合、-fが仮定されます。 Values are in 1024-byte increments, except for -t, which is in seconds, -p, which is in units of 512-byte blocks, and -T, -b, -n, and -u, which are unscaled values. -tは秒単位、-pは512バイト単位、-T、-b、-n、および-uはスケーリングされていない値です(1024バイト単位)。 無効なオプションまたは引数が指定されていないか、新しい制限を設定中にエラーが発生しない限り、戻りステータスは0です。 In POSIX Mode 512-byte blocks are used for the `-c’ and `-f’ options. POSIXモードでは、 `-c ‘と` -f’オプションのために512バイトブロックが使われます。 |
設定変更後の反映
プロセス単位で設定を変更した後は、OS の再起動まで必要ありません。
対象のサービス(プロセス)を再起動します。
まとめ
ファイルディスクリプタの設定は「OS」と「プロセス(デーモン)」の2種類があります。
また、ファイルディスクリプタの設定は「ソフト」と「ハード」があります。
この組み合わせを意識する必要があります。
例えば、いくら「ftp」アカウントのファイルディスクリプタ数を「65536」にしても「OS全体」のファイルディスクリプタ数が「1024」(少なすぎますが)なら、それ以上はファイルをオープンできません。
コメント