mod_fcgid

  • 投稿者:
  • 投稿カテゴリー:apache / PHP

概要

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)

2015-01-03_010136

設定

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