Apache のPrefork MPM + mod_phpで運用していたシステムを、Apache Event MPMに変更したので、その際の記録を残したいと思います。
ApacheのPrefork MPMは「プロセス数=同時接続数」です。しかし、Apacheに割り当てられるメモリには当然限りがあり、Preforkで増やせるプロセスに制限をかける必要があります。つまり接続数に限界がでてくることになります。
KeepAlive Offだと、性能頭打ちするし。。。KeepAlive Onにすると、パラメータ調整が難しく、大丈夫だと思っても、いきなり不安定になるときがあったり。。。設定しては負荷検証を繰り返し繰り返しと戦ったあげく、アーキテクチャをEvent MPMにすることにしました。
このあたりは、似たようなことされているこちらのブログも参考になるかと。
Apacheをpreforkからeventに切り替える | 純規の暇人趣味ブログ
Event MPMにすると何が良いのかを端的に知りたい場合は、以下のスライドのNginx部分をApache EventMPMに読み替えるとイメージつかめるかと思います。
PreforkからEventにMPMを変更するにあたっての記録を残しておきます。
バージョン情報
CentOS7 + Apache 2.4.23 + PHP-FPM 7.0.x
です。
PHP-FPM Systemd化
remiリポジトリからyumで入れました。systemd化は以下のように書きました。
$ cat /etc/systemd/system/php-fpm.service [Unit] Description=The PHP FastCGI Process Manager After=syslog.target network.target [Service] Type=simple PIDFile=/var/run/php-fpm.pid ExecStart=/opt/remi/php70/root/usr/sbin/php-fpm --nodaemonize --fpm-config /etc/opt/remi/php70/php-fpm.conf ExecReload=/bin/kill -USR2 $MAINPID [Install] WantedBy=multi-user.target
一応Reloadも定義していますが、あまり期待しない方が良いかも知れないです。
php-fpmはgraceful restartしません(回避策あり) - White Box技術部
既存資産のRewriteRuleを活かしたい
Apache EventMPM + PHP-FPMの構成の場合、Apacheはリバースプロキシとして動きます。
設定方法はいくつかありますが、Apacheの既存RewriteRuleも活かしたかったので SetHandler
を使う方法にしました。
以下のように書くことで既存のRewriteRuleをそのまま流用できました。
#既存のRewriteRule RewriteRule ^/(.*)$ /index.php/$1 [L] <FilesMatch \.php$> SetHandler "proxy:fcgi://127.0.0.1:9000" </FilesMatch>
Apache PHP-FPM間通信 Unix Socket
か TCP
か
ApacheとPHP-FPMの通信にはTCP
だけではなく、Unix Socket
を利用することができます。
Unix Socketの方が早いという話も聞いていたので、実際に両方のパターンで負荷テストを行ってみました。
環境依存な部分もあると思いますが、今回使っていたシステムの場合は性能面での差が出ることはありませんでした。
むしろ、システムリソースの限界を超えた負荷をかけた場合には、Unix Socketの方がエラーが増えてしまいました。
結果、今回はTCPを利用することにしました。
ちなみにUnix Socket
を使う場合は、前述の SetHandler
を以下のような設定にします。
<FilesMatch \.php$> SetHandler "proxy:unix:/var/run/php-fpm.sock|fcgi://localhost" </FilesMatch>
PHP-FPMの Unix Socket
設定は以下のような感じです。
listen = /var/run/php-fpm.sock listen.owner = apache listen.group = apache listen.allowed_clients = 127.0.0.1
PHP情報をApacheログに出力する場合の変更
PHPの apache_setenv
とか使ってApacheのログをカスタマイズしている場合は、レスポンスヘッダーを使うことで実装できます。
[Apache] レスポンスヘッダーの内容をログに出力して、なおかつヘッダーから削除する方法
Apacheのバージョンは要注意です。 2.4.7
以上が必要なのですが、記事執筆時点のCentOS7のリポジトリでは足りませんでした。
最新安定版の 2.4.23
を入れた時の記事がこちらです。
他にもApacheに依存している部分は見直した方が良いです。例えば、 apache_request_headers
は以下のワークアラウンドが役立つはず。
php - Is there a apache_request_headers alternatve for displaying HTTP Headers - Stack Overflow
他にもアプリケーション依存の修正は諸々あると思いますが、残りは割愛で。
PHP-FPMの並列数
PHPを走らせるPHP-FPMですが並列数を調整する必要がります。
手順としては、 ps
コマンドにて php-fpm
1プロセス当たりの実メモリ利用量(RSS)と並列数をかけて、全プロセスのメモリ利用量を確認します。
以下のような感じです。
$ ps aux | grep php-fpm | grep www | awk '{sum += $6}END{print "Total(KB) :\t",sum,"\nAverage(KB):\t",sum/NR}' Total(KB) : 403452 Average(KB): 20172.6
この情報を元にPHP-FPMのメモリ制約を考えます。あとは実際に負荷テストなどをして、他のミドルウェアやCPUリソースなどとの兼ね合いを確認します。
設定例は以下のような感じです。考え方はApacheと近いです。PHP-FPMの設定ページ確認してください。
pm = static pm.max_children = 90 pm.start_servers = 90 pm.min_spare_servers = 90 pm.max_spare_servers = 90 pm.max_requests = 500
PHP-FPMのモニタリング
PHP-FPMにもApacheで言う、server-status
的なものが存在します。
設定ファイルに以下の項目を追加することで利用できるようになります。
pm.status_path = /phpfpm_status
Apache側の設定例は以下のような感じ。
<Location /phpfpm_status> Require all denied Require ip 127.0.0.0/8 Require ip 192.168.10.0/24 Deny from 192.168.20.0/24 SetHandler "proxy:fcgi://127.0.0.1:9000/phpfpm_status" </Location>
PHP-FPMの状況みたい場合はコンソールから以下のコマンド打って確認できます。
$ curl http://localhost/phpfpm_status pool: www process manager: static start time: 17/Nov/2016:16:17:16 +0900 start since: 191346 accepted conn: 1695725 listen queue: 0 max listen queue: 38 listen queue len: 511 idle processes: 19 active processes: 1 total processes: 70 max active processes: 70 max children reached: 2 slow requests: 1
この設定するとこちらのMuninプラグインも使えるようになります。
GitHub - tjstein/php5-fpm-munin-plugins: A set of Munin plugins for PHP5-FPM
PHPからMySQLへの接続並列数
PHP-FPMのプロセス数が決まってきたら、それに合わせて、MySQLのMax Connectionも調整しました。
mysql> show global variables like 'max_connections'; +-----------------+-------+ | Variable_name | Value | +-----------------+-------+ | max_connections | xxxx | +-----------------+-------+
PHP-FPMのプロセス数にとDB数から算出できます。
PHPのプロセス数 × DB数
以下のSQL文の結果とPHP-FPMのプロセス数を見比べると、よりわかりやすいかもしれないです。
select DB,count(*) from information_schema.processlist group by DB order by count(*) desc;
ただ、コネクション数はそれなりにメモリ食うので、少し運用してみて、以下のパラメータ含めて調整してみると良いと思います。
mysql> show global status like 'Max_used_connections'; +----------------------+-------+ | Variable_name | Value | +----------------------+-------+ | Max_used_connections | XXXXX | +----------------------+-------+ 1 row in set (0.00 sec)
(番外)ELB経由のApacheログにClient IPを出力
ELB配下のApacheの場合、クライアントPCなどのIPを X-Forwarded-For
というヘッダーの情報を元に取得してログに出力できるのですが、なんか項目が複数になってします。
以下のようにApacheに設定することで、
RemoteIPHeader X-Forwarded-For
このようなLogFormatを、
LogFormat "%{X-Forwarded-For}i ....."
こんな感じに書き換えるとうまくログに欲しい情報が出力されるようになりました。
LogFormat "%a......"
こちらを参考にさせていただきました。
mod_extract_forwardedと多段 Proxy - ブログ - ワルブリックス株式会社
参考URL
- FastCGI Process Manager (FPM)設定項目