blog(WordPress)の高速化を行ってみた php-fpm編

というわけでまたもやblog(WordPress)の高速化に挑んでみます。前回はWordPressの内部の設定でできる限りの高速化を行う、ということをやってみました。前回の場合だとWordPressを運営しているほとんどのサイトで取り入れることができる方法ですのである意味一般向けになっています。そして今回はWordPressをVPSなど自分でサーバーのソフトウェア環境を変更することができる場合だとどのようにすれば高速化ができるか?ということに着目してやってみました。

 

PHPの実行速度を向上させるには?

当たり前ですがWordPressはPHPを使って動いています。そのため、PHPの実行速度を向上させることができれば応答速度を上げることができ、すなわちPageSpeed Insightsなどで高得点をとることができるようになり、最終的にはユーザー数向上につながります。問題なのはどうやればPHPの速度を向上させることができるのか、です。たとえばPHP5系を使っている場合ですとPHP7系は速度が向上しているらしいので有効である可能性があります。この方法の問題点はPHP7が導入可能な環境なのか(サーバーやパッケージ管理の問題)およびWordPressでPHP7に対応していないプラグインを使用していないかどうか(WordPress本体は4.4でPHP7に対応したらしいですが周りはそうとは限らない)を考える必要があることです。もう一つはPHPの実行に関してPHP側のキャッシュ処理を有効にする方法があります。この方法は意外と汎用的なのですが…。

 

mod_suphpの処理のためにキャッシュ処理が利用できなかったのでphp-fpmに変更した

phpを実行するときのユーザー環境をmod_suphpによって処理していたためphpの実行は「一つのphpファイルにつきphpを起動させて終了させる」というシーケンスとなり、通常キャッシュを使うために必要となる「何回もphpのファイルを実行させる」ができなかった、という問題点がありました。で、今回これを解決するためにphpをFastCGIとして実行できるようにするためサーバー型で実行するphp-fpmを導入して実行ユーザーをphp-fpm側で管理できるようにする、という方式をとることにしました。が、これを使うための設定で難儀したためphpが実行されない状態になるといった状態が途中でできてしまいました…。そのあたりも説明していきたいと思います。

 

php-fpmおよびphpキャッシュ処理モジュールを導入する

実はPHPの「Fatal error: Can’t use function return value in write context」に要注意という記事を書きましたがこれの対応のためにPHPのバージョンを5.4系から5.5系へと変更しています。そのため、キャッシュ処理のパッケージも微妙に異なることに注意しながらやってみます。CentOS6系の場合、PHP5.5などCentOS6標準のPHPより上位のバージョンをパッケージインストールする方法として別のリポジトリを使う方法が有効です。今回はその中でもRemiを使った場合で行います。php-fpmのほかに実行キャッシュ処理などのためにphp-opcacheおよびphp-apcuを導入します。もちろん先にRemiが有効になっていることが条件ですが…。

 

パッケージのインストール

# yum install php-fpm php-opcache php-apcu --disablerepo=base,updates --enablerepo=remi-php55

面倒なのはRemiが提供しているPHPはPHP5.4系、PHP5.5系、PHP5.6系、PHP7系と複数あるのでどのリポジトリを参照するのか指定しなければならないこと、CentOS標準のリポジトリを無効にした状態でパッケージをインストールしなければならないことですね。

 

php-fpmの設定

今回重要なことはPHPを外部で実行できることと同時にPHPを実行するディレクトリで異なるユーザーを設定できることがあります。つまりmod_suphpの役割を設定から担えることがあります。というわけでそれに準拠した設定をするとこうなります。/etc/php-fpm.confはデフォルトでも問題ありませんが/etc/php-fpm.d/以下の設定ファイルに細工が必要です。設定ファイルは各ユーザーごとに設定ファイルを用意して記述する必要があるわけです。たとえばユーザーとしてhtmluserで動作させたい場合はこうします。

[htmluser]
listen = /var/run/php-fpm/htmluser.sock
listen.owner = apache
listen.group = apache
listen.mode = 0600
user = htmluser
group = htmluser
pm = dynamic
pm.max_children = 8
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 12
pm.max_requests = 10000
request_terminate_timeout = 600
request_slowlog_timeout = 100
slowlog = /var/log/php-fpm/htmluser-slow.log 
php_admin_value[error_log] = /var/log/php-fpm/htmluser-error.log
php_admin_flag[log_errors] = on
php_value[session.save_handler] = files
php_value[session.save_path]    = /var/lib/php/session
php_value[soap.wsdl_cache_dir]  = /var/lib/php/wsdlcache

同一サーバー上で動作させる場合はsocketで受け渡した方がよいと思われます。別サーバーに分離する場合はlistenの記述を通常のサーバーの待ち受けにすればOKです。

また、listenに設定するlisten.ownerやlisten.groupはPHPを実行するサーバープロセスの実行者がわかっている場合はそれを設定することでlisten.modeを0600と最小限の設定にすることができます。これがわからなかったり別の実行者からもアクセスできるようにするのであればlisten.ownerおよびlisten.groupにnobodyを設定してlisten.modeを0666としておくのがよいと思います。

次に実行ユーザーの設定はuserおよびgroupで行います。対象のsokcetに接続した場合はここで設定したユーザーで実行することになります。これを利用して実行者を分離することが可能になります。ただし、設定ファイル1つにつき1つのユーザーが対応するのでユーザー数だけこのファイルを(セクション名やユーザー名を変更して)増やしていく必要があるわけですね。

残りはphp-fpmのサーバー動作に関する設定です。pm系はphpを同時に処理できる数などを設定します。基本的にはPHPを実行するサーバープロセスの設定に従ってして置けばOKです。その他PHPの実行設定もある程度はここで行えます。

最後にphp-fpmはdaemonとして動作するので起動処理および起動設定を行います。CentOS6系だとこうなります。

# service php-fpm start
# chkconfig php-fpm on

CentOS7系などsystemctlを使う場合はそちらの処理に置き換わります。また、php-fpmはサーバー動作なので設定ファイルを変更した場合はそれをphp-fpmに通知する必要がありますので注意してください。面倒なら再起動すればOKでしょう。

 

php-opcache、php-apcuの設定

次にキャッシュに関する処理です。php-opcacheはPHP5.5系からPHPの実行キャッシュ処理を担当しています。設定ファイルは/etc/php.d/opcache.iniになります。

zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1

注意としてはenable_cliは1に設定することが重要です。これがないとphp-fpm経由で実行したときにキャッシュされない、ということになりますので。

php-apucも設定してしまいましょう。こちらはPHPのデータキャッシュを担当しています。設定ファイルは/etc/php.d/apcu.iniになります。

apc.enabled=1
apc.enable_cli=1
apc.shm_size=64M
apc.ttl=86400
apc.gc_tll=86400

こちらでもenable_cliは1に設定します。また、shm_sizeはキャッシュサイズですのでサーバーのメモリ容量と相談して設定します。

 

Apacheでphp-fpmを使えるようにする

今回難航したのがこの手順です。nginxからphp-fpmを使う手順はいろいろなところにあるのですがApacheから使う手順があまりないこと、またApacheの2.2系と2.4系で使用方法がかなり異なるので苦労しました。今回はCentOS6系だと標準がApache2.2系ですのでそちらに準拠して説明します。Apache2.4系だといくつか変更が必要になりますので注意してください。

 

mod_fastcgiを導入する

php-fpmを使うためにはCGIを実行するモジュールが必要になります。php-fpmの場合はmod_fastcgiを使うとよいと思います。ただし、標準のリポジトリからだとインストールできないのでrpmforgeを使えるようになっていることが条件です。その場合のインストールは

# yum install mod_fastcgi

でOKです。

 

mod_fastcgiを設定する

実は今回一番苦労したのがこの設定です。すべての設定を正しく行ったはずなのにPHPがうまく実行されない場合はこの部分の設定が誤っていることがほとんどだと思います。

まずPHPをFastCGI経由で実行することになるのでモジュールとして連結している設定を解除します。いままでPHPの設定は/etc/conf.d/php.confおよび/etc/conf.d/suphp.confが担当していたのでこれらのファイルを読み込まないように設定します。単純に拡張子を変えてしまえば読み込まれなくなりますので

# mv /etc/httpd/conf.d/php.conf /etc/httpd/conf.d/php.conf.bak
# mv /etc/httpd/conf.d/suphp.conf /etc/httpd/conf.d/suphp.conf.bak

とすればOKです。また設定が終わったらmod_suphpは削除しても問題ないと思います。

次にFastCGIの設定をします。設定ファイルは/etc/httpd/conf.d/fastcgi.confになります。肝となる設定は以下のようになります。

FastCgiWrapper Off
DirectoryIndex index.php
AddType application/x-httpd-php .php
Alias /php-htmluser /var/www/fcgi-bin/php-htmluser
Action php-fpm-htmluser /php-htmluser
FastCGIExternalServer /var/www/fcgi-bin/php-htmluser -socket /var/run/php-fpm/htmluser.sock

ここで間違いやすいポイントを挙げると

  • FastCGIWrapperは基本Offにする。
  • FastCGIExternalServerで設定する呼び出し用仮想実行ファイルは存在する必要はないがその上のディレクトリ(この場合は/var/www/fcgi-bin/)までは実際に存在する必要がある
  • ActionだけではなくAliasも設定しておくこと
  • 複数ユーザーに対応する場合SetHandler(AddHandler)はここでは設定しない
  • 複数のユーザーが存在する場合はその数だけAlias、Action、FastCGIExternalServerを設定する(htmluserの部分を変更する)

があります。実は今回の更新で画面を真っ白にしてしまった理由がFastCGIWrapperをデフォルトの設定であるOnのままにしたためでした。いろいろと調べて判明しましたがまさかこれが原因だとはわかりませんでしたよ…、ということです。あとFastCGIExternalServerで設定したファイルは実際には作成されません。あくまでApache内部でアクセス処理を行ったときに実行ファイルとして内部的に見えるようになっているためです。そのためファイルが作成されていない、と慌てないでくださいね。

ちなみにApache2.4系以降だとDirectoryディテクティブから/var/www/fcgi-bin/にgrant allを設定しないと実行できないようです。アクセス権限が厳密になっているようですね。

 

Directoryの設定を変更する

FastCGIでの実行はCGIの実行権限がディレクトリに必要になります。ファイルを実行可能にする必要はありませんが…。例として/var/www/以下をhtmluser権限でPHPを実行できるようにします。

<Directory "/var/www/">
    Options ExecCGI
    <FilesMatch ".+\.php$">
        SetHandler php-fpm-htmluser
    </FileMatch>
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>

注意点としては

  • OptionsにExecCGIが必要になる(これがよく忘れる)
  • SetHandlerを拡張子phpに設定する。このときhtmluser権限で実行することをActionの設定から持ってくる

があります。これを使って各ディレクトリの設定でSetHandlerとActionの定義よりユーザー名を指定して実行する、ということが可能になるわけです。

 

Apacheに設定変更を通知する

変更した設定が正しいかどうか確認してから再読込を行いましょう。

# service httpd configtest
# service httpd reload

 

で、速度は向上したの?

これで向上しなかったら悲しいですよね…。速度測定はPageSpeed InsightsよりWPtouchを使っているモバイルのサイトを対象にしたところ2.8秒から0.6秒まで向上させることに成功しました。ポイントも70点を上回る結果となりまあ改善した甲斐はあったかな、というところです。これ以上の向上を考えるとPHPのバージョンアップやVPSの乗り換えとかそんな話になりそうですね。

 

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

この記事のトラックバック用URL