純規の暇人趣味ブログ

手を突っ込んで足を洗う

Apacheをpreforkからeventに切り替える

      2015/12/12    HimaJyun

今回はこのブログが乗ってるWeb鯖(Apache)のmpmをpreforkからeventに切り替えました
Centは知りませんが、Ubuntuだと超簡単に切り替えが出来たのでご紹介しようと思います。

eventだとPHPガーとか言われてますけど、FastCGIで動かせば普通に動きますよ!!
このブログ(WordPress=PHP)が動いてるのがその証拠です。

毎度の如く長々と前提条件の説明とかが入ります。
さっさとやり方を知りたい方は「いざ、event!!」まで飛ばした方が良いですよ。

preforkをやめた理由

Keep-Aliveの所為で死にます、以上。

では余りにも不親切過ぎるので、理解してないなりに簡単に説明すると、
preforkにはその仕組み上、避ける事の出来ない限界があります。

同時に捌ける数を超えてアクセスがあると処理待ちに、ってのはどのmpmでも当たり前の話だと思います。
その前提条件によって発生するのがKeep-Alive問題です。

Keep-Aliveは、接続を切らずに複数のデータを転送して高速化しよう!!、と言う方法です。
つまり、設定した時間だけ再度リクエストがあるまで待ち続けると言う訳です。
仮に0.1秒でレスポンスを返せたとしても、Keep-Aliveが5秒だと5秒間は他のリクエストを捌く事が出来ません。

そのため、必然的に「最大クライアント数/Keep-Alive(もしくはレスポンス)の待ち時間」が同時に捌ける接続数の限界になっちゃうわけです。
(今頃のブラウザは5セッション同時に貼って来たりするのでなおさらマズい状況に陥る気がします。)

かと言って、Keep-Aliveを切ってしまうとWebの表示が遅くなってしまいます。
これでは本末転倒ですね。

加えて、子プロセスを起こすfork、これは案外重たい処理です。
と言う訳で事前(pre)にforkするのでpreforkなのですが、瞬発的なアクセスの増加ではpreった分では足りません

結果としてforkが頻発しちゃったり……
かと言って、事前に叩き起こす分を増やせば良いかと言うとそうも行きません、C10K問題にぶち当たります。

これらは上手い具合に調整すれば何とかなるかもしれませんが、各子プロセスは起きた時点で一定量のメモリを要求します。
どう調整しても、プロセス数が増えればとどのつまりはメモリ不足などに陥る、と言う訳です。

preforkによって発生する問題は、僕自身イマイチハッキリと理解してません
いくつか参考になりそうなページを乗せておくので目を通してみて下さい。

まぁ、workerでも良いんですけど、それだとKeep-Alive問題は健在らしいです。
せっかくApache2.4でeventが正式に採用されたので、このeventを採用してみようと思います。

Nginxは?

A:それを扱えるならばご自由にどうぞ。

Nginx(エンジンエックス)は、巷で早い早いとやたら持て囃されているWeb鯖です。
「WordPressをNginxでX倍速く」、もう見飽きたタイトルです。

ですが、Nginxは元々静的コンテンツを高速で配信できるように作られています。
動的コンテンツではそう大した性能向上が無かったりします。(もちろん、静的コンテンツはかなり速いですが)

簡単に言うと、動的コンテンツにおいてネックになるのはプログラムでの処理やDBのI/O待ちであり、Web鯖の問題ではないため、どんなにWeb鯖を改善した所でそう大した性能差が無いのです、WordPressとかのCMSならなおさら。

「遅い→Nginxにしよう」と言う短絡的な思考回路ではどう頑張ってもあなたのサーバは高速化しません。
なぜ遅いのか、原因を突き止めて、適切なチューニングを施す事こそがサーバ高速化への近道ですよ。

それではなぜ、「x倍速くなった」が溢れかえるかと言うと、
これ系の記事を書いてる奴は決まってVPSの少ないメモリでメモリバカ食いするpreforkを使っていたり、Keep-Aliveの設定が不適切(デフォルト=300秒のまま)だったり、NginxやWordPressプラグインのキャッシュを使ってやがります。

キャッシュを使えば第一にその動的な処理云々が発生しないので速くなるのは当然の事ですね、静的コンテンツに強いNginxならばなおさら。究極を行くなら、全部HTMLで静的コンテンツにする他ないですよ

逸れて行った話を順番に戻します。

単純に速くしたいだけならばNginxでキャッシュを効かせれば良いと思います。
ですが、Nginxには動的モジュールの概念が無く、モジュールを入れ替える時はコンパイルし直さなければなりません。
また、キャッシュを使えば更新がリアルタイムに反映されません、別に良いのですが、良くないです。

加えて、Nginxには.htaccessとかも無く、configを直接編集して動かします。
これらの動的な処理が無いのです、早いのは当然ですね。

Nginx=魔理沙のマスタースパークです。
まっすぐにしか飛びません、融通が利かないです。

Apache=霊夢の夢想封印です。
ホーミングするので融通が利きます。

色々な事に手を突っ込んで足を洗うスタイルの僕にはNginxは扱いきれる気がしないので今回は見送り
単純にWordPressでブログがしたいだけならさっさと戻るボタンを押してNginx+HHVMの記事を探すべきです。

速くなる/速くならないを書きまくっちゃったので勘違いしない様に言っておくと、eventに切り替えたからと言ってApacheの動作が速くなる訳ではありません(多少速くなるらしいですが……)

あくまで、C10KやKeep-Aliveで鯖ががががががが、とかを回避出来るだけです。
preforkから切り替えるとメモリ消費は減るらしいので、性能の貧弱な鯖(VPSとか)は試す価値あるかもです。

どの道、(このサイト含め)大したアクセスの無いサイトでは体感できる程の性能差は生まれませんけどね。

いざ、event!!

さて、event駆動を推してるのか貶してるのかイマイチ良く分からない前提条件はここまで、
本題の設定変更に移りますよ

ちなみにですが、Ubuntu ServerにLAMPでインストールしたApacheです。

まずはCGIを実行出来る様に

さて、PHPをCGIで動かすのですよ、当然ながらCGIが実行できる必要があります。
「/etc/apache2/sites-available/*.conf」に「Options ExecCGI」を設定して下さい。

以下の設定例を参考にして下さい。

<VirtualHost *:80>
 ServerName yourdomain.example.com

 ServerAdmin admin@example.com
 DocumentRoot /var/www/html
 <Directory /var/www/html>
  #このOptionsにExecCGIを追加して下さいね。
  Options ExecCGI
  AllowOverride All
 </Directory>

 ErrorLog ${APACHE_LOG_DIR}/error.log
 CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

PHP-FPM+FastCGIを設定する。

まずは必要なモノをインストールします。

sudo apt-get install libapache2-mod-fastcgi php5-fpm apache2-mpm-event

次に、エディタで「/etc/apache2/mods-available/fastcgi.conf」を開いて下さい。
開いたら以下の内容を追記して下さい。

AddHandler php5-fcgi .php
Action php5-fcgi /php5-fcgi
Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi
FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -socket /var/run/php5-fpm.sock -pass-header Authorization

<Directory /usr/lib/cgi-bin>
 Require all granted
</Directory>

多分、以下の様な感じになると思います。

<IfModule mod_fastcgi.c>
 AddHandler fastcgi-script .fcgi
 #FastCgiWrapper /usr/lib/apache2/suexec
 FastCgiIpcDir /var/lib/apache2/fastcgi

 AddHandler php5-fcgi .php
 Action php5-fcgi /php5-fcgi
 Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi
 FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -socket /var/run/php5-fpm.sock -pass-header Authorization

 <Directory /usr/lib/cgi-bin>
  Require all granted
 </Directory>
</IfModule> 

PHP-FPMの設定を調整

今までの設定だと、「/etc/php5/apache2/php.ini」が参照されていたと思います。
php-fpmに切り替えると、「/etc/php5/fpm/php.ini」が参照される様になるので設定を更新しましょう。

内容は同じ(だと思うので)以下のコマンドでコピーしてしまいましょう。

sudo cp /etc/php5/apache2/php.ini /etc/php5/fpm/php.ini

もし心配であればdiffとか使って差分を調べれば良いと思います。

次に、fpmの設定を調整します。
エディタで「/etc/php5/fpm/pool.d/www.conf」を開いて下さい。

ごちゃごちゃと詰まっていると思いますけど、以下の点を変更して下さい。
その他の設定項目は自分の理想に合わせて設定して下さい。

[www]
; fpmの実行ユーザ、Apacheの設定と合わせる方が無難だと思います。
user = www-data
group = www-data

; Unixソケットで動かします、TCP/IPで動かすより軽いらしい
listen = /var/run/php5-fpm.sock

; ソケットにWeb鯖がアクセス出来る様に
; つまり、Apacheの実行ユーザと同じ
listen.owner = www-data
listen.group = www-data

; FPMの動かし方
; dynamic = preforkと同じ感じ、負荷に応じて子プロセスを起こしたり寝かしたり。余計な子プロセスが起きないので省メモリ
; static = 常に一定数の子プロセスで動かす、メモリは食うけどこっちの方が速い
pm = static

; 子プロセスの起動数、「pm」の設定が
; dynamic = 起こす子プロセスの最大数。
; static = 起こす子プロセスの数
pm.max_children = 10

; 起動時に起こす子プロセス数
; staticの場合は設定不要
pm.start_servers = 2

; 待機中の子プロセス数の最小
; staticの場合は設定不要
pm.min_spare_servers = 1

; 待機中の子プロセス数の最大
; staticの場合は設定不要
pm.max_spare_servers = 3

; ここで設定した数のリクエストを捌くとプロセスが再起動する、PHPのメモリリーク防止
pm.max_requests = 500

mpm_eventの設定を調整

さて、せっかくのeventでもスレッドの数が不適切だと無意味です。
スレッド数を調整しましょう。

エディタで「/etc/apache2/mods-available/mpm_event.conf」を開いて下さい。
以下の項目をお好きな様に調整してね。

<IfModule mpm_event_module>
 # サーバ起動時に起こす子プロセス数
 StartServers 2
 # 待機スレッドが足りない時はこの数までスレッドを増やす
 MinSpareThreads 25
 # 待機スレッドが多い時はこの数までスレッドを減らす
 MaxSpareThreads 75
 # 子プロセス当たりのスレッド数
 ThreadsPerChild 25
 # 同時に捌けるリクエスト数
 MaxRequestWorkers 150
 # この数のリクエストを捌くと子プロセスが再起動、PHPのメモリリーク防止
 MaxConnectionsPerChild 1000
</IfModule>

設定を適用する

以下のコマンドを実行して下さい。

sudo a2enmod actions fastcgi
sudo a2dismod mpm_prefork php5
sudo a2enmod mpm_event
sudo service apache2 restart
sudo service php5-fpm restart

再起動時の注意点

今まではApacheとPHPが一体で動いていたので、PHPの設定を変更した際は
「sudo service apache2 restart」だけで良かったのですが

CGIで動かすと言う事は別のプロセスが動くと言う意味なので、PHPの設定を変更した際に
「sudo service php5-fpm restart」でphpを再起動する必要があります。

切り替えた後は

Apacheがeventに切り替わったかどうかは「sudo apache2ctl -V | grep 'MPM'」で確認できます。
eventに切り替わっていると「Server MPM: event」と表示されるはずです。

PHPが正しくFastCGIになったかはphpinfo();で確認して下さい。
正しく設定されていると「Server API」の項目が「FPM/FastCGI」になっているはずです。
apache-switch_to_event_001

実際にブラウザからアクセスしてみて、あなたのサイトが正しく表示されるか確認しましょう。
正しく表示されていれば設定完了です、何かエラーが出る時はエラーログを元に修正して下さい。

どうでも良いですが、PHPをFPM/FastCGIに切り替えると、HTTP300の時にファイル候補としてセットされる
「$_SERVER['REDIRECT_VARIANTS']」が「$_SERVER['REDIRECT_REDIRECT_VARIANTS']」になります。

バグ……なのかな?、こんな変数使う事滅多に無いから気付かれてない?
まぁ、配列のキーが変わっただけで中身は同じなので気にする必要ないとは思います。

 - サーバ運営