Linux

2010年05月31日

Autoconf/configureスクリプトを使ってアプリケーションの設定を自動化

はてなブックマーク -Autoconf/configureスクリプトを使ってアプリケーションの設定を自動化 Check
みなさん、こんにちは、
kaoruです。

はじめに
UNIX環境でアプリケーションをインストールしたことがある方には、次のコマンドはおなじみだと思います。
./configure
make
make install

これらのコマンドは通常はC/C++で書かれたアプリケーションで利用されることが多いのですが、PHPやPythonなど、スクリプト言語で書かれたアプリケーションであっても利用価値があります。 とはいえ、スクリプト言語の開発者にはあまり馴染みがないと思いますので、簡単に紹介をしたいと思います。

Autoconf/configureスクリプトを利用するメリット

よくある設定ファイルを直接書き換えるような方法だと、書き換えた設定ファイルをバージョン管理システムに登録できないので、困ってしまいます。環境ごとに設定ファイルを複数用意して、全部バージョン管理するような方法だと、一箇所を修正すると全部のファイルを忘れずに修正しないといけなくなります。

また、設定ファイルに誤りがあっても、すぐにはわからないという問題があります。configureスクリプトはシェルスクリプトですから、どんな方法のエラーチェックも可能です。

configureスクリプトとは

configureスクリプトの役割はテンプレートとなるテキストファイルを入力として、文字列置換を行い、出力となるファイルを書き出すことです。よくある入力ファイルはMakefile.inで、出力ファイルはMakefileなのですが、ここでは、config.iniというスクリプト言語用の設定ファイルを出力することを考えてみましょう。

テンプレートファイルの書き方

出力したいファイルのファイル名の末尾に「.in」をつけたファイル名を用意します。 置き換えたい場所に、@変数名@というプレースホルダーの記述をします。ここでいう変数名はシェルの変数名で、configureスクリプトから渡されます。

configure.acの書き方

こちらは、autoconfのマニュアルを見てほしいのですが、ポイントとしてはAC_SUBSTというマクロを使うことで、先ほどのテンプレートファイルで指定したプレースホルダーの記述を置換することができます。

configureスクリプトの生成方法
シェルのプロンプトから
autoreconf
と入力するだけで、configure.acからconfigureスクリプトが生成されます。
configureスクリプトの使い方

./configure --help とすると、おなじみのヘルプが表示されます。

サンプル
config/config.ini.in
BASE_DIR = @abs_srcdir@
MASTER_DB = @MASTER_DB@
SLAVE_DB = @SLAVE_DB@
IMAGE_DIR = @IMAGE_DIR@

configure.ac
AC_PREREQ(2.59)
AC_INIT(MyProject, 1.00)

AC_CONFIG_SRCDIR([config/config.ini.in])

AC_ARG_ENABLE(
    [image-dir],
    AS_HELP_STRING([--enable-image-dir=ARG], [画像ファイルの置き場所]),
    [IMAGE_DIR=$enableval],
    [IMAGE_DIR=$prefix/htdocs/image]
)
AC_SUBST(IMAGE_DIR)

AC_ARG_ENABLE(
    [master-db],
    AS_HELP_STRING([--enable-master-db=ARG], [マスターDBのDSN]),
    [MASTER_DB=$enableval],
    [MASTER_DB=]
)
AC_SUBST(MASTER_DB)

AC_ARG_ENABLE(
    [slave-db],
    AS_HELP_STRING([--enable-slave-db=ARG], [スレイブDBのDSN]),
    [SLAVE_DB=$enableval],
    [SLAVE_DB=]
)
AC_SUBST(SLAVE_DB)

AC_CONFIG_FILES([config/config.ini])
AC_OUTPUT
サンプルの実行例
autoreconf
./configure --enable-image-dir=/home/test/image --enable-master-db="mysql:host=master-db01.sample port=3306 user=web_user" --enable-slave-db="mysql:slave-db01.sample port=3306 user=web_user"

次のようなメッセージが表示されます。

configure: creating ./config.status
config.status: creating config/config.ini

config/config.ini が生成されているはずなので、確認してみてください。

はてなブックマーク -Autoconf/configureスクリプトを使ってアプリケーションの設定を自動化 Check
日時: 2010年05月31日 19:59 | コメント (0) | トラックバック (0)
2010年07月26日

ファイルディスクリプタ数の上限変更とlimits.confの罠

はてなブックマーク -ファイルディスクリプタ数の上限変更とlimits.confの罠 Check


こんにちは、mikkoです。

今回は、1プロセスが同時オープン可能なファイルディスクリプタ数の上限を変更する方法と、その際の落とし穴についてです。

Linuxでは、同時にオープンできるファイルディスクリプタ数が制限されています。
OS全体での設定は、/proc/sys/fs/file-max等で確認でき、大規模なアクセスがあるサーバでは/etc/sysctl.confに設定を追加して上限を増やしているケースも多いでしょう。

これとは別に、1プロセスが同時オープン可能なファイルディスクリプタ数は、標準で1024となっています。
apacheのpreforkモデル等の子プロセスが多数稼動するアプリケーションだと、1プロセスあたりのファイルオープン数はそこまで増加しないので、普段はあまり意識することは少ないかもしれません。
しかし、apacheのworkerモデル等のスレッドを使用するアプリケーションでは、この制限が致命的になるケースが発生します。

では、このファイルディスクリプタ数上限を上げるにはどうすればよいのでしょうか。

1プロセスあたりのファイルディスクリプタ数上限を上げるには3通りあり、

  1. カーネルヘッダファイル中のINR_OPENを書き換えてrebuild
  2. ulimit -n で一時的に更新する
  3. /etc/security/limits.conf に設定を記述する

の何れかを選択することになりますが、カーネルのrebuildは手間なので除外すると、選択肢は2つになります。

※プログラム内からsetrlimit()システムコールを呼ぶ方法もありますが、今回は既存のプログラムを扱うことを前提としているので除外します

ulimitとは何なのか?

シェルのbuilt-inコマンドで、プロセスに割り当てる各種リソースの制限を行うためのものです(csh系はlimitコマンド)。
ulimit -n を実行すると、現在のファイルディスクリプタ数上限値(SoftLimitおよびHardLimit)が確認できます。
SoftLimitはHardLimit以下の範囲内で一般ユーザが変更可能ですが、HardLimitはrootでしか変更できません。
ulimitを実行したシェルおよび、そのシェルから起動される子プロセスに対して一時的に設定を変えることが出来ます。

/etc/security/limits.conf とは何なのか?

/etc/security/limits.confは、PAM認証モジュールの1つであるpam_limitsの設定ファイルです。

<domain> <type> <item> <value>

というフォーマットで記述を行い、

root soft nofile 2048
root hard nofile 2048

と書けば、rootで実行されるプロセス単位の最大ファイルオープン数が2048に設定されます。

pam_limitsモジュールは、PAMの設定ファイル/etc/pam.d/system-auth内においてsession型で定義されており、/etc/pam.d/loginや/etc/pam.d/sshd、/etc/pam.d/sudo等多くの設定ファイルがsystem-authをインクルードする形で使用しています。
従って、先の設定はログイン時やsudo時など、PAM認証が発生する(且つsessionでpam_limitsモジュールがrequireされる)タイミングで有効化されます。

つまり、「PAM認証を介さないようなdaemon系プログラムの制限には/etc/security/limits.confは使えない」ことになるのです。

ところが、googleで「apache limits.conf」等を検索すると、limits.confを書き換えればapacheの上限ファイルディスクリプタ数を増加可能という内容の記事が散見されます。 これはいったいどういうことなのでしょうか?

罠1:該当ユーザでのulimit -nによる確認

limits.confに

* soft nofile 2048
* hard nofile 2048

を記述し、su等でrootになり、ulimit -nを実行して変更確認を行う方法です。

しかし、ログイン時やsu時においてPAM認証が行われた時点でlimits.confの設定が適用されるため、rcスクリプトやdaemontoolsから起動されるプロセスと環境が異なり、確認の意味を成していません。

罠2:rcスクリプトによる再起動での確認

実際にdaemonの再起動を行い、'Too many open files'のエラーが出なくなったことを確認する方法です。

実は、rcスクリプトやapachectl等で再起動を行うと、実際に設定が反映されています。
これは非常にわかりにくく、以下のような状況が発生しています。

例:sudo /etc/init.d/httpd restart

  1. sudoでrootユーザになる際にPAM認証が行われ、limits.confの設定が適用される(ulimit -n 2048と同義)
  2. rootユーザで/etc/init.d/httpdを実行する際、上限値が子プロセス(/etc/init.d/httpdはスクリプトなので、bashになる)に引き継がれる
  3. スクリプト内でhttpdを停止・開始する際、上限値が子プロセス(開始されるhttpd)に引き継がれる
  4. 上限値が引き継がれたhttpdが開始される

実際に以下の手順で検証してみました。

  1. 1100個のファイルを同時オープンする簡単なphpスクリプト(index.php)を用意
  2. <?php
    $fp = array();
    for ($i = 0; $i < 1100; $i++) {
        $fp[] = fopen('index.php', 'r');
    }
    echo '<pre>';
    var_dump($fp);
    echo '</pre>';
    
  3. apache経由でアクセスし、1000個付近でファイルが開けなくなることを確認
  4. ...snip...
    [999]=>
    resource(1002) of type (stream)
    [1000]=>
    resource(1003) of type (stream)
    [1001]=>
    bool(false)
    [1002]=>
    bool(false)
    ...snip...
    
    ※ apacheのerror_logにも、'Too many open files'が表示されています。
    ※ 上限の1024に満たないのは、httpdプロセス自身がいくつか消費しているためです。lsofコマンド等で確認が可能です。
  5. limits.conf を書き換える
  6. * soft nofile 2048
    * hard nofile 2048
    

    を追加する。

  7. sudo /etc/init.d/httpd restartでapacheを再起動する
  8. 再度apache経由でアクセスし、1000個以上ファイルが開けることを確認
  9. ※ ここまでで、上限値が反映されたと誤解してしまいます!
  10. マシンを再起動する
  11. 三度apache経由でアクセスし、1000個付近でファイルが開けなくなることを確認
  12. ※ 上限値が1024に戻ってしまいます!

つまり、手動で再起動した場合は一時的にlimits.confの設定内容が有効になるだけなのです。
当然、マシン自体が再起動した場合はinitが各daemonを起動し、PAM認証が入らないため、OS規定の上限値である1024に戻ってしまいます。

結局、リミット値を上げるには、ulimit -n の記述を

  • daemontoolsであれば、/service/<サービス名>/runファイルに追加
  • rcスクリプトであれば、/etc/init.d/<サービス名>ファイルに追加

しないといけません。

ただしapacheの場合、apachectl経由での制御を行うとrcスクリプトを介さないため、apachectlにも追加しておかないといけないことに注意してください。

  • apachectl restart
  • 既存のhttpdプロセスにシグナルを送り、既存httpdから新規httpdがforkされるため、既存httpdのリミット値が引き継がれます。

  • apachectl start
  • 新規httpdが起動され、上記rcスクリプトの実行の流れと同じ現象が起こるため、ulimitの追加が必要です。

結論

daemon系プロセスのファイルディスクリプタ数上限を設定する際、/etc/security/limits.conf は使えません。状況によっては一見設定されたように見えますが、大きな落とし穴にはまることになります。
面倒ですが、必要なプロセス毎にulimitを用いて適切に設定しましょう。

はてなブックマーク -ファイルディスクリプタ数の上限変更とlimits.confの罠 Check
日時: 2010年07月26日 17:47 | コメント (0) | トラックバック (0)
ゆめみ深田浩嗣のブログ Mercury mobmail
YUMEMI Labs Sweet