Apache 2 prefork から event への乗り換え
はじめに
先日、C100 と Geeko Magazine の告知を出した際、geeko.jp のウェブサーバーが落ちてしまいました。普段は全然問題ないのですが、512 MB の VPS のため、ほんの少しアクセスが増えるとメモリーを使い果たしてしまうようです。
openSUSE の Web サーバーは 2009 年頃から使い続けていることもあって Apache で、しかも MPM (Multi-processing modules) は prefork です。複数のアクセスに対してプロセスを作成して対応するので、メモリー使用量やプロセスの生成コストが課題です。設定変更前のメモリー使用量を見ると次のような状態で、1プロセスあたり 30 MB 前後使っています。とりあえずの対策としては、プロセスの最大数を抑えればよいのですが、メモリー使用量削減のため、これを機に MPM を event にすることにしました。
# smem -U wwwrun -k
PID User Command Swap USS PSS RSS
26053 wwwrun /usr/sbin/httpd-prefork -DS 24.2M 576.0K 1.3M 9.5M
23952 wwwrun /usr/sbin/httpd-prefork -DS 14.5M 20.8M 21.7M 30.4M
26051 wwwrun /usr/sbin/httpd-prefork -DS 5.6M 23.5M 24.4M 32.9M
23953 wwwrun /usr/sbin/httpd-prefork -DS 6.3M 23.7M 24.4M 31.8M
2023 wwwrun /usr/sbin/httpd-prefork -DS 5.4M 24.0M 24.8M 32.7M
7054 wwwrun /usr/sbin/httpd-prefork -DS 7.5M 26.0M 26.8M 35.0M
23955 wwwrun /usr/sbin/httpd-prefork -DS 5.0M 32.6M 33.6M 42.2M
23956 wwwrun /usr/sbin/httpd-prefork -DS 5.5M 34.2M 34.9M 43.1M
23959 wwwrun /usr/sbin/httpd-prefork -DS 5.5M 34.4M 35.3M 43.8M
2022 wwwrun /usr/sbin/httpd-prefork -DS 6.0M 34.7M 35.6M 43.6M
少し前までの Apache の MPM といえば worker で、スレッドを使って並列処理をします。私の知識もここで止まっていました。event は新しい MPM で、今どきのイベント駆動で実装されています。ワーカースレッド内で受信待ちやソケット書き込み待ちをせずに、パケット到着や書き込み可能になったイベントを受けてスレッドに処理を割り当てるようです。
MPM を prefork から event にするために必要なことは、次の通りです。
- event MPM をインストールする
- PHP を mod_php による実行から mod_proxy_fcgi + php_fpm (FastCGI) による実行に変える
- php_fpm で Web アプリを実行できるように AppArmor のプロファイルを設定する
結構面倒くさいですね。
openSUSE のバージョンは Leap 15.4 です。
event MPM をインストールする
これは簡単で apache2-event をインストールするだけです。apache2-prefork もインストールされいる環境では、apache2-event が優先されます。
zypper in apache2-event
mod_proxy_fcgi + php_fpm への変更
これまでは mod_php でこの Word Press などを実行してきました。mod_php の場合、PHP のスクリプトは Apache のプロセスで実行されていました。マルチスレッドに対応していない mod_php は event や worker では使用できません。php_fpm で PHP を別プロセスで起動しておき、リクエスト時にこの PHP プロセスに処理を依頼する形に変更する必要があります。
セットアップ手順は以下の通りです。php7-fpm をインストールして、パッケージに含まれるデフォルトの設定を有効化します。
zypper in php7-fpm
cd /etc/php7/fpm
mv php-fpm.conf.default php-fpm.conf
cd php-fpm.d
mv www.conf.default www.conf
systemctl enable php-fpm
systemctl start php-fpm
Apache 側の設定を変えます。openSUSE では Apache で使用するモジュールは /etc/sysconfig/apache2 で有効化します。php7 を削除し、proxy と proxy_fcgi を追加します。
APACHE_MODULES="(省略)proxy proxy_fcgi"
次に、php ファイルのハンドリングを mod_php から php_fpm に切り替えます。/etc/apache2/conf.d/ に以下のファイルを作成します。openSUSE のデフォルト設定では php7_fpm は 9000 で待ち受けていますので、php へのアクセスを 127.0.0.1:9000 に転送するようにします。
ProxyErrorOverride on は php_fpm がエラーを返した場合に、php_fpm のエラーメッセージをそのままブラウザに返すのではなく、Apache 側で設定したエラー画面を表示するための設定です。
SetHandler "proxy:fcgi://127.0.0.1:9000"
SetHandler application/x-httpd-php-source
DirectoryIndex index.php4
DirectoryIndex index.php5
DirectoryIndex index.php7
DirectoryIndex index.php
ProxyErrorOverride on
AppArmor の設定変更
openSUSE Leap 15.4 では php_fpm 用の AppArmor プロファイルが含まれており、php_fpm が行える操作に制限がかかっています。そのため、何も設定しないと、php-fpm が php ファイルにアクセスできません。/var/log/audit/ に次のようなログが出力され、403 が返ります。
type=AVC msg=audit(1661003085.840:89050): apparmor="DENIED" operation="open" profile="php-fpm" name="/srv/www/htdocs/index.php" pid=20329 comm="php-fpm" requested_mask="r" denied_mask="r" fsuid=498 ouid=498
php_fpm のプロファイルを調整するには、/etc/apparmor.d/php-fpm.d/ に次のような設定ファイルを作成し、php-fpm がアクセスできるディレクトリを設定します。
# tmp へのアクセス include# htdocs への読み書き # 読み取りだけであれば もあり owner /srv/www/htdocs/** rw,
結果
設定後にメモリー使用量を見てみます。
PID User Command Swap USS PSS RSS
29193 wwwrun /usr/sbin/httpd-event -DSYS 208.0K 804.0K 1.3M 4.9M
29195 wwwrun /usr/sbin/httpd-event -DSYS 208.0K 1.6M 2.8M 11.0M
29194 wwwrun /usr/sbin/httpd-event -DSYS 208.0K 1.6M 2.8M 11.1M
29196 wwwrun /usr/sbin/httpd-event -DSYS 208.0K 2.2M 3.4M 11.6M
29278 wwwrun /usr/sbin/httpd-event -DSYS 208.0K 3.2M 4.4M 12.7M
29094 wwwrun php-fpm: pool www 208.0K 25.3M 30.3M 47.1M
29306 wwwrun php-fpm: pool www 208.0K 33.8M 38.4M 54.5M
29337 wwwrun php-fpm: pool www 208.0K 35.6M 39.5M 54.0M
php-fpm のプロセスが増えましたが、Apache のプロセスのメモリー使用量はぐっと小さくなりました。Apache と php-fpm の初期プロセス数、最大プロセス数はこれから調整したいと思います。