先日、某氏が mod_perl 環境で時折暴走するという凶悪なスクリプトを書いてくれたのである。 よりによって俺の管理するサーバ上で。・・・まあ、mod_perl でなくて素の CGI で動かせば暴走はしない(アクセス毎にプロセスが起動しては殺されるから問題が顕在化しない) ので、そうしておいたのだが、アクセスが増えると CGI では捌ききれなくなり、 気がついたら Load Average 20 なんて状況に陥る始末 (殺す気かこら) で、止むを得ず対処。
普通の CGI の消費リソースを制限するのには RLimitCPU とかの Apache のディレクティブがあるのだが、これは mod_perl には効かない。 オライリーから出ている mod_perl本 (Apache 拡張ガイド) を読んで、 Apache::Resource モジュール (mod_perl 附属)を使えばよいらしい、と知る。
まず、CPAN から BSD::Resource モジュール(BSD-Resource-1.11.tar.gz) を落としてきて、普通にインストール。
次に httpd.conf に、
PerlModule Apache::Resource PerlSetEnv PERL_RLIMIT_CPU 10 PerlChildInitHandler Apache::Resource
と記述。(この場合は CPU 時間を 10秒使い切った時点で子プロセスが kill される)
実験。単純に無限ループする、
while(1){};
というスクリプトを /home/httpd/perl/loop.cgi として保存して、ブラウザから叩いてみる。 と、約10秒間 CPU使用率が 100% になった後、プロセスが kill される。めでたしめでたし。
・・・で、ここまで会社でやってて、自宅で検証したら上手く行かなかったので補足。mod_gzip モジュールを使用して
mod_gzip_item_include handler perl-script
として、mod_perl の出力を gzip 圧縮しようとしている場合には、 Apache::Resource の PERL_RLIMIT_CPU が効かないっぽい。handler の利くタイミングの絡みかしらん (<いいかげんだなおい)。