概要
apacheではFastCGIをfcgidモジュールで実現することができる
fastcgi(mod_fastcgi)モジュールもあるが、問題があるらしく後発のfcgidモジュールの設定について記載する
FastCGIとは
CGIはユーザーから要求がある度にプロセスの生成と破棄が行われる
FastCGIはプロセスをメモリ上に永続化させることで、その起動と終了にかかる時間をカットし
結果としてプログラム動作速度の向上およびサーバ負荷の低下を実現する
具体的には、あらかじめ複数の CGI プロセスを待機状態で起動しておく
そしてリクエストがあったときにその中のひとつをアクティベートしてスクリプトを実行し、結果をクライアントに返却する
実行し終わったプロセスは再び待機状態になり(つまりfork/execのオーバーヘッドが取り除かれる)、リクエストを待つ
(fcgid-scriptとしてハンドラーに渡されたものは、FastCGIプロトコルで処理される)
(FastCGIプロセスはhttpdの動いている別のサーバで動かすことも可能)
欠点として、FastCGIによって起動したプログラムはリソースを消費し続ける
継続的に管理するためにも、管理者がひとつのプログラムがどれくらいリソースを消費するかを考えなければならない
環境
root@hostname:/home/shimizu# uname -a Linux hostname 3.2.0-4-amd64 #1 SMP Debian 3.2.63-2+deb7u2 x86_64 GNU/Linux root@hostname:/home/shimizu# apache2 -v Server version: Apache/2.2.22 (Debian) Server built: Jul 24 2014 15:34:00 root@hostname:/home/shimizu# php-cgi -v PHP 5.4.36-0+deb7u1 (cgi-fcgi) (built: Dec 31 2014 07:33:54) Copyright (c) 1997-2014 The PHP Group Zend Engine v2.4.0, Copyright (c) 1998-2014 Zend Technologies root@hostname:/home/shimizu# aptitude search mod-fcgi | grep ^i i libapache2-mod-fcgid - an alternative module compat with mod_fast i libapache2-mod-fcgid-dbg - debugging symbols for mod_fcgid root@hostname:/home/shimizu# apache2ctl -M | grep cgi Syntax OK cgi_module (shared) fcgid_module (shared)
設定
root@hostname:/home/shimizu# cat /etc/apache2/mods-enabled/fcgid.conf <IfModule mod_fcgid.c> ### .phpファイルと.fcgiファイルが、fcgid-scriptとして扱われるようになる AddHandler fcgid-script .php .fcgi AddType application/x-httpd-php .php ### FastCGIに渡す環境変数名と値を指定する。どうもphpの環境変数 ### 同時起動する子プロセス数。0より大きい場合、PHP_FCGI_MAX_REQUESTSは子プロセスに関して有効となる ### 子プロセスが要求回数を満たし、自動的に終了しても親プロセスが子プロセスを再起動する FcgidInitialEnv PHP_FCGI_CHILDREN 2 ### リクエストの数が PHP_FCGI_MAX_REQUESTS の値に達すると、 PHP はプロセスを kill する FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 20 ### FastCGIアプリケーションプロセスの最大数 FcgidMaxProcesses 4 ### FastCGIアプリケーションクラスにおける最大プロセス数(Max process count of one class of FastCGI application) FcgidMaxProcessesPerClass 1 ### FastCGIアプリケーションクラスにおける最小プロセス数(Min process count of one class of FastCGI application) FcgidMinProcessesPerClass 1 ### 使用するラッパーを指定する FcgidWrapper /usr/bin/php-cgi .php SocketPath /var/lib/apache2/fcgid/sock DefaultInitEnv PHPRC=/etc/php5/cgi ### windowsのための待ち時間。UNIXには適用されない FcgidConnectTimeout 20 ### FCGIサーバへのリクエストがあってから、待機をしている時間。FastCGIサーバへの通信の際のタイムアウト時間 ### 全てのFCGIを使用するアプリケーションに適用される。特定のアプリケーションのみ設定することも可能 FcgidIOTimeout 3600 ### アイドルプロセスのタイムアウト時間 FcgidIdleTimeout 300 ### アイドルプロセスがないかこの間隔で監視(FcgidIdleTimeout と FcgidProcessLifeTimeを監視) FcgidIdleScanInterval 120 ### この期間経過したら、FCGIアプリケーションをkillする。時間のかかる処理を止めるための機構 FcgidBusyTimeout 600 ### FcgidBusyTimeoutを監視する間隔 FcgidBusyScanInterval 120 ### 保留中のプロセスの終了を処理する間隔(FcgidIdleTimeout と FcgidProcessLifeTimeを超えているプロセスを監視) FcgidErrorScanInterval 3 ### ゾンビプロセスの監視間隔 FcgidZombieScanInterval 3 ### プロセスが生成される際の速度を調整する。FCGIごとに基準(スコア)が存在し、現在の値がこの基準以上であれば新しくプロセスを生成しない FcgidSpawnScoreUpLimit 10 ### 各spawnはプロセスアクティビティのスコアにこの値を追加 FcgidSpawnScore 1 ### それぞれ終了したプロセスは、プロセスアクティビティのスコアにこの値を追加する FcgidTerminationScore 2 ### 各FastCGIアプリケーションが処理する最大リクエスト FcgidMaxRequestsPerProcess 0 ### 最大HTTPリクエストの長さ FcgidMaxRequestLen 1073741824 ### アイドルプロセスの寿命。もしプロセス数がFcgidMinProcessesPerClassを超えており、この値も超えていれば終了する FcgidProcessLifeTime 3600 </IfModule>
・apacheを停止したら、php-cgiのプロセスは全て終了する
・なぜプロセスの再起動が必要なのかというと
PHPの場合、どうしてもメモリが膨れ上がったり、リークする危険性があるため、ある程度処理したらプロセスを入れ替える必要がある
しかし、問題が発生することがある。それは、PHP_FCGI_MAX_REQUESTSを超えた処理を要求した場合、
php-cgiは、超えたと判断したら処理を無条件に終了する。つまり500、503エラーを出力する
回避策としては、FcgidMaxRequestsPerProcessにPHP_FCGI_MAX_REQUESTSの数値以下の数字を指定したり、両方の変数を0にする
・また、要求数を無駄なリソースなく効率的に処理するためには、子プロセスは利用するべきでない
mod_fcgidを利用する場合、PHP_FCGI_CHILDREN=0で子プロセスを無効推奨
設定内容確認
PHP_FCGI_CHILDREN・PHP_FCGI_MAX_REQUESTS
1つのphpを実行すると、(PHP_FCGI_CHILDRENが有効な場合は)PHP_FCGI_CHILDREN個の子プロセスが一気に起動し
そのPHP_FCGI_CHILDREN個のプロセスがそれぞれPHP_FCGI_MAX_REQUESTS件処理すると再起動する
root@hostname:/home/shimizu# cat /var/www/htdocs/getpid.php <?php $PID=getmypid(); echo $PID."\n"; ?> root@hostname:/home/shimizu# curl http://49.212.204.46/getpid.php 7069 root@hostname:/home/shimizu# curl http://49.212.204.46/getpid.php 7068 root@hostname:/home/shimizu# ps afx ... 7043 ? Ss 0:00 /usr/sbin/apache2 -k start 7047 ? S 0:00 \_ /usr/sbin/apache2 -k start 7066 ? Ss 0:00 | \_ /usr/bin/php-cgi 7068 ? S 0:00 | \_ /usr/bin/php-cgi 7069 ? S 0:00 | \_ /usr/bin/php-cgi 7049 ? S 0:00 \_ /usr/sbin/apache2 -k start 7050 ? S 0:00 \_ /usr/sbin/apache2 -k start ### それぞれ20回アクセスすると、PIDが変更した ### root@hostname:/home/shimizu# curl http://49.212.204.46/getpid.php 7068 root@hostname:/home/shimizu# curl http://49.212.204.46/getpid.php 7069 root@hostname:/home/shimizu# curl http://49.212.204.46/getpid.php 7120 root@hostname:/home/shimizu# curl http://49.212.204.46/getpid.php 7122 root@hostname:/home/shimizu# ps afx ... 7043 ? Ss 0:00 /usr/sbin/apache2 -k start 7047 ? S 0:00 \_ /usr/sbin/apache2 -k start 7066 ? Ss 0:00 | \_ /usr/bin/php-cgi 7120 ? S 0:00 | \_ /usr/bin/php-cgi 7122 ? S 0:00 | \_ /usr/bin/php-cgi 7049 ? S 0:00 \_ /usr/sbin/apache2 -k start 7050 ? S 0:00 \_ /usr/sbin/apache2 -k start
FcgidBusyTimeout
120秒以上かかるプログラムを実施してみる
エラーメッセージが出力され、内部的にkillされている模様
root@hostname:/home/shimizu# cat /var/www/htdocs/time.php <?php // 現在の時刻 echo date('l jS \of F Y h:i:s A')."\n" ; // 10 秒間遅延させる sleep(240); // 現在の時刻 echo date('l jS \of F Y h:i:s A')."\n" ; ?> root@hostname:/home/shimizu# curl http://49.212.204.46/time.php Saturday 3rd of January 2015 08:05:21 PM Saturday 3rd of January 2015 08:07:29 PM ### エラーログには ### [Sat Jan 03 20:07:29 2015] [info] [client 49.212.204.46] mod_fcgid: /time.php took longer than busy timeout (120 secs) ### この間に他のプログラムを実行するとエラー ### [Sat Jan 03 20:06:40 2015] [warn] [client 49.243.165.176] mod_fcgid: can't apply process slot for /usr/bin/php-cgi
time.phpを実行している間に他のphpプログラムは503となるため理由を追ってみる
この間htmlファイルは問題なく返却される
試しに以下プログラムのようにsleepを分割すると、途中でkillされず実行された
root@hostname:/home/shimizu# cat /var/www/htdocs/time2.php <?php for ($i = 1; $i <= 30; $i++) { // 現在の時刻 echo date('l jS \of F Y h:i:s A')."\n" ; // PIDを表示 $PID=getmypid(); echo $PID."\n"; // 10 秒間遅延させる sleep(10); } ?> root@hostname:/home/shimizu# curl http://49.212.204.46/time2.php Saturday 3rd of January 2015 08:49:14 PM 8884 Saturday 3rd of January 2015 08:49:24 PM 8884 Saturday 3rd of January 2015 08:49:34 PM 8884 Saturday 3rd of January 2015 08:49:44 PM 8884 Saturday 3rd of January 2015 08:49:54 PM 8884 Saturday 3rd of January 2015 08:50:04 PM 8884 Saturday 3rd of January 2015 08:50:14 PM 8884 Saturday 3rd of January 2015 08:50:24 PM 8884 Saturday 3rd of January 2015 08:50:34 PM 8884 Saturday 3rd of January 2015 08:50:44 PM 8884 Saturday 3rd of January 2015 08:50:54 PM 8884 Saturday 3rd of January 2015 08:51:04 PM 8884 Saturday 3rd of January 2015 08:51:14 PM 8884 Saturday 3rd of January 2015 08:51:21 PM 8884 Saturday 3rd of January 2015 08:51:31 PM 8884 Saturday 3rd of January 2015 08:51:41 PM 8884 Saturday 3rd of January 2015 08:51:51 PM 8884 Saturday 3rd of January 2015 08:52:01 PM 8884 Saturday 3rd of January 2015 08:52:11 PM 8884 Saturday 3rd of January 2015 08:52:21 PM 8884 Saturday 3rd of January 2015 08:52:31 PM 8884 Saturday 3rd of January 2015 08:52:41 PM 8884 Saturday 3rd of January 2015 08:52:51 PM 8884 Saturday 3rd of January 2015 08:53:01 PM 8884 Saturday 3rd of January 2015 08:53:11 PM 8884 Saturday 3rd of January 2015 08:53:21 PM 8884 Saturday 3rd of January 2015 08:53:31 PM 8884 Saturday 3rd of January 2015 08:53:41 PM 8884 Saturday 3rd of January 2015 08:53:51 PM 8884 Saturday 3rd of January 2015 08:54:01 PM 8884
time2.phpが実行されている間、別コンソールでpsを確認すると
以下のようにphp-cgiがapacheモジュールではなく、スタンドアロンとして実行されていた模様。。
そのためmod_fcgid: /time2.php took longer than busy timeout (120 secs) というメッセージは表示されていたが、killされずに最後まで実行された
8792 ? Ss 0:00 /usr/sbin/apache2 -k start 8796 ? S 0:00 \_ /usr/sbin/apache2 -k start 8882 ? Zs 0:00 | \_ [php-cgi] <defunct> 8798 ? S 0:00 \_ /usr/sbin/apache2 -k start 8862 ? S 0:00 \_ /usr/sbin/apache2 -k start 8888 ? S 0:00 \_ /usr/sbin/apache2 -k start 8891 ? S 0:00 \_ /usr/sbin/apache2 -k start 8884 ? S 0:00 /usr/bin/php-cgi
最終的に、
PHP_FCGI_CHILDREN=4
FcgidMaxProcesses=2
FcgidMaxProcessesPerClass=10
としたところ、time.phpを実行中に、別のphpプログラムを実行しても問題なく動作した
以下にプロセスを記載しているが、2つ目のプロセスが起動し、処理した模様
12054 ? Ss 0:00 /usr/sbin/apache2 -k start 12058 ? S 0:00 \_ /usr/sbin/apache2 -k start 12076 ? Ss 0:00 | \_ /usr/bin/php-cgi 12078 ? S 0:00 | | \_ /usr/bin/php-cgi 12079 ? S 0:00 | | \_ /usr/bin/php-cgi 12080 ? S 0:00 | | \_ /usr/bin/php-cgi 12081 ? S 0:00 | | \_ /usr/bin/php-cgi 12090 ? Ss 0:00 | \_ /usr/bin/php-cgi 12092 ? S 0:00 | \_ /usr/bin/php-cgi 12093 ? S 0:00 | \_ /usr/bin/php-cgi 12094 ? S 0:00 | \_ /usr/bin/php-cgi 12095 ? S 0:00 | \_ /usr/bin/php-cgi 12060 ? S 0:00 \_ /usr/sbin/apache2 -k start ...
FcgidMaxProcesses・FcgidMaxProcessesPerClass
PHP_FCGI_CHILDRENを0にして大量アクセスした場合、FcgidMaxProcessesが4の場合は、php-cgiが4プロセス立ち上がった
FcgidMaxProcessesPerClassを3にした場合は、3プロセスしか立ち上がらないため、チューニング時は両方を変更する必要がある
10701 ? Ss 0:00 /usr/sbin/apache2 -k start 10705 ? S 0:00 \_ /usr/sbin/apache2 -k start 10753 ? S 0:00 | \_ /usr/bin/php-cgi 10754 ? S 0:00 | \_ /usr/bin/php-cgi 10839 ? S 0:00 | \_ /usr/bin/php-cgi 10841 ? S 0:00 | \_ /usr/bin/php-cgi 10707 ? S 0:00 \_ /usr/sbin/apache2 -k start ...
mod_fcgid: can’t apply process slot for
回避策としては、
・FcgidMaxRequestsPerProcessにPHP_FCGI_MAX_REQUESTSの数値以下の数字を指定する、または両方共0にする(プログラムによっては問題を引き起こす)
PHP_FCGI_MAX_REQUESTSを超えた処理を要求した場合、php-cgiは超えたと判断したら、処理を無条件に終了し500や503を返却する
このときにこのエラーが表示される時がある
・原因が複雑な場合もある。PHP-FRMに乗り換えなよ
http://www.megalinux.net/mod_fcgid-cant-apply-process-slot/
・MaxProcessCountが小さいのかも
http://www.webhostingtalk.com/showthread.php?t=1203229
http://linuxtechme.wordpress.com/2013/08/24/mod_fcgid-cant-apply-process-slot-for-usrlocalcpanelcgi-sysphp5/
参考URL
Apache Module mod_fcgid
http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html
apache の FastCGI(mod_fcgid) で phpを動かしてみる
http://server-setting.info/centos/apache_fastcgi_fcgid_php.html
Apache with fcgid: acceptable performance and better resource utilization
http://2bits.com/articles/apache-fcgid-acceptable-performance-and-better-resource-utilization.html