Sep 12, 2005

MT 3.2 on Apache + FastCGI

Brad Choate: MT 3.2 and LightTPD/FastCGIを読んでLightTPD/FastCGI化したいなあと思っていましたが、Apacheの負の遺産が大き過ぎて結局LightTPDへの移行は見送りました。

同様に、BP's Weblog: MT 3.2 with FastCGI under Apache)でApache上でFastCGI化しようとしている方がいますが、これで紹介されている方法ではアプリケーションごとに異なるインスタンスが生成されてしまうので効率が良くありません。

そこでこのエントリーでは「ApacheでMT 3.2を(真っ当にシングルインスタンスで)FastCGI化する方法」について説明します(けど、あまりよく分かっていないので私にFastCGIの質問はしないこと!!)。

前提としてApache 1.3.33+mod_fastcgiがインストールされているものとします。Apache2でも同様に設定できると思いますがここでは説明しません。

設定方法

設定方法を6ステップで説明します。

ステップ0: Movable TypeにUnofficial Patchをあてる。

Ogawa::Buzz: MT 3.2日本語版 Unofficial Patchで公開しているUnofficial Patchをあてておきます。このパッチを当てることによって、FastCGI環境などで生じるMTの不具合がまとめて修正されます。

ステップ1: httpd.confを設定する。

httpd.confには以下のように設定して動的にFastCGIアプリケーションが起動できるようにしておきます。

LoadModule fastcgi_module libexec/apache/mod_fastcgi.so
AddModule mod_fastcgi.c

<IfModule mod_fastcgi.c>
    AddHandler fastcgi-script .fcgi
    FastCGIConfig -maxClassProcesses 4 -minProcesses 1
    FastCgiIpcDir /tmp
</IfModule>

ステップ2: dispatch.fcgiを用意する。

以下のスクリプトをdispatch.fcgiという名前でMTDIRに置き、実行パーミッションを設定するのをお忘れなく。

#!/usr/bin/perl -w
use strict;
use lib 'lib';
use MT::Bootstrap;
use CGI::Fast qw(:standard);

my $handlers = {
    'mt.fcgi' => 'MT::App::CMS',
    'mt-comments.fcgi' => 'MT::App::Comments',
    'mt-tb.fcgi' => 'MT::App::Trackback',
    'mt-search.fcgi' => 'MT::App::Search',
## uncomment if necessary, but this adds a lot of
## overhead since it loads up LibXML.
##    'mt-atom.fcgi' => 'MT::AtomServer',
};
eval "use $_" foreach (values %$handlers);

eval {
    while (my $q = new CGI::Fast) {
        my $cgi = $q->url;
        $cgi =~ s!.*/!!;
        my $pkg = $handlers->{$cgi};
        die "Invalid handler for $cgi" unless $pkg;
        my $app = $pkg->new(CGIObject => $q) or die $pkg->errstr;
        local $SIG{__WARN__} = sub { $app->trace($_[0]) };
        MT->set_instance($app);
        $app->init_request(CGIObject => $q) unless $app->{init_request};
        $app->run;
        my $mode = $app->mode || '';
        if ("$pkg->$mode" eq 'MT::App::CMS->plugin_control') {
            exit; # allows server to recycle after changing plugin switches
        }
    }
};
if ($@) {
    print "Content-Type: text/html\n\n";
    print "Got an error: $@";
}

dispatch.fcgiを手で起動してみて不足しているモジュールをCPANなどからインストールしましょう。FreeBSDならportsからp5-FastCGIとfcgi-devkitをインストールしておけば大丈夫だったみたいです。

ちなみにこのスクリプトはlighttpd + FastCGI環境でも問題なく動作するはずです。また、ときどき修正しているので最新版をフォローしたい場合にはogawa - Revision 650: /trunk/fastcgiをウォッチしていてください。

ステップ3: RewriteRuleを設定する。

MTDIRに.htaccessを作り、以下のように設定しておきます。

RewriteEngine on
RewriteRule ^mt\.fcgi(.*)$          dispatch.fcgi$1 [L]
RewriteRule ^mt-comments\.fcgi(.*)$ dispatch.fcgi$1 [L]
RewriteRule ^mt-tb\.fcgi(.*)$       dispatch.fcgi$1 [L]
RewriteRule ^mt-search\.fcgi(.*)$   dispatch.fcgi$1 [L]

ステップ4: mt-config.cgiを変更する。

最後にmt-config.cgiを編集して以下のように設定しておきます。

AdminScript mt.fcgi
CommentScript mt-comments.fcgi
TrackbackScript mt-tb.fcgi
SearchScript mt-search.fcgi

他のXMLRPCScript, ViewScript, AtomScriptなども同様に設定できるのかもしれませんが、私は確認していません。Brad Choateの記事を読む限り、AtomScriptはFastCGI化できるようですね(ただXML::Atomが読み込まれるので常駐領域が巨大になるっぽい)。

ステップ5: 動作確認とパラメタチューニングをする。

これで動くはずです。また、httpd.confのパラメータチューニングもお好みで行うと良いでしょう。詳しいパラメータの意味はApache module mod_fastcgiあたりを参照してください。


2005-09-12追記: 動的にFastCGIアプリケーションを起動するとちょっと動作がおかしいことがあるようです。気がついた範囲ではコメントのNotifyメールを送られなかったり、管理画面が一部文字化けしたりすることがあります。後者は純粋にベータ版のせいのような気もしますが、試しに静的に起動するようにして実験中です。こんな感じで↓

<IfModule mod_fastcgi.c>
#    AddHandler fastcgi-script .fcgi
#    FastCgiConfig -maxClassProcesses 4 -minProcesses 1
    FastCgiIpcDir /tmp
    FastCgiServer <fullpath>/dispatch.fcgi -processes 2
</IfModule>

2005-09-14追記: どうもNotifyメールが送られないのはLaunchBackgroundTasksが1にセットされている場合のようです。このオプションをセットするとクリティカルパスがforkされたプロセスで実行されるようになり、Notifyメールはおそらくそのforked processの中で送っていると想像されます。送信処理でもどうせsendmailを起動するのにforkしているはずだからこのあたりがやばいのかもねーと思って、Mail::Sendmailを使うように変更してみましたがやっぱり送信されません。しょうがないのでLaunchBackgroundTasks 0にしてみています。


2005-09-16追記: ちょっと気になってMT->set_instance($app);という行を追加してみた。管理画面が時折文字化けしたり、FastCGIを動的に起動したときにたまにconfig情報の取得に失敗したりする問題の解決を期待して。


2005-09-22追記: 09-16の修正は結果的に大正解。動的に起動しても非常に安定して動作するようになりました。

また、FastCgiServerで静的に起動するのは逆にerrorousなので止めました。強制的に2プロセス以上静的起動させていると、例えばプラグインの設定変更を行うと、そのリクエストを処理したプロセス自体は再起動して設定が反映されますが、それ以外のプロセスは影響を受けません。つまり、リクエストごとに不整合が生じ得ます。

これを避けるには、(1) リクエストごとにpluginsディレクトリを再帰的に読み込み直す、(2) リクエストごとにプラグインの初期化をし直す、(3) ビジーでないプロセスはとりあえず再起動させる、くらいの解決策があるでしょう。(1) はリクエストごとにナンセンスなコストがかかり、わざわざFastCGIにした意味がありません。(2) は初期化ルーチンを分離してプラグインが記述されている必要があります。MT 3.2的にはプラグインをMT::Pluginのサブクラスとして定義し、init_requestというメソッドを定義して欲しい感じにも見えるのですが、実装が中途で終わっている模様。下位互換性もない方法になります。

というわけで(3)が現状有効っぽいわけです。ループカウンタやタイマで制御することもできますが、ApacheのFastCGIモジュールでは、動的起動するアプリケーションはdemandがなければ残り1個になるまで自動的にkillしてくれるので、結局意図したような動作になってしまうようです。

About Me

My Photo

つくばで働く研究者

Total Pageviews

Amazon

Copyright 2012 Ogawa::Buzz | Powered by Blogger
Design by Web2feel | Blogger Template by NewBloggerThemes.com