ownCloudのパフォーマンスチューニング
前回、簡単に設定しましたが、パフォーマンスを考えると必要な設定がまだまだあります。パフォーマンスをよくするためのチューニングとしては、ownCloudでは以下のようなポイントがあります。
パフォーマンスチューニングのポイントは、いかにDiskへの書き込みを避け、メモリーを有効に活用するかにつきます。また、接続時のセッションを使い回し、PHPの中間コード生成回数を少なくし、DBの処理速度を上げることも行います。
それでは、チューニングポイントを以下の5つに分けて説明していきましょう。
- cron設定
- DBトランザクション分離レベルの変更とPHPのDB(mysqli)接続設定
- PHPキャッシュの導入(OPcache、APCu)
- KVSの導入(Redis)
- Apache+PHP-FPMの設定
cron設定
ownCloudにはJob実行機能があります。これは1時間に1回送信されるメール通知に利用されたり、ゴミ箱に入っているファイルやバージョニングの履歴ファイルを自動的に削除するクリーンナップのスケジュール実行として利用されています。
デフォルトの設定では、このJob実行機能はAjaxで動作するモードになっています。このモードではユーザーがブラウザーでownCloudを操作するときにジョブが実行されるようになっています。これは効率が悪いのみならず、ブラウザーのレスポンスにも影響を与えます。
そこで、このJob実行機能をLinuxのcron機能から呼び出されるように変更します。
/etc/cron.dに以下のファイルを作成します。
# vi /etc/cron.d/owncloud-cron-php
そのファイルの中に以下の内容を記載します。ここでは、WebサーバーをApache、そして起動タイミングを毎分としています。もし、WebサーバーをApache以外で実行している場合は適宜修正してください。また、5分に1回程度の起動でも通常は問題ないでしょう。
* * * * * apache php -f /var/www/html/owncloud/cron.php > /dev/null 2>&1 || logger "cron failed. ret=$? `/bin/awk \'{print $1}\' /proc/$$/cmdline`"' > /etc/cron.d/owncloud-cron-php
cron設定に変更したので、ownCloudのconfig.phpにも設定変更を反映します。以下のコマンドを実行して下さい。config.phpの設定が修正されます。
# sudo -u apache /var/www/html/owncloud/occ background:cron
DBトランザクション分離レベルの変更とPHPのDB(mysqli)接続設定
ここではデータベース関連の設定を行います。これはPHPのキャッシュ設定の次に効果の高いチューニングです。
server.cnfの設定
データベースのデータ更新時のトランザクションの分離レベルを修正します。MariaDBの分離レベルはデフォルトでは、REPEATABLE-READですが、これをREAD-COMMITEDに変更します。REPEATABLE-READは、トランザクション開始時のデータを繰り返し読み込めるという意味ではよいのですが、トランザクションを継続している間にロック競合が発生します。その為、更新時のパフォーマンスが落ちる等の問題があります。ownCloud社では、トランザクション分離レベをREAD-COMMITEDに変更することを推奨しています。以下のページに詳しい解説があります。
漢(オトコ)のコンピュータ道: さらにMySQLを高速化する7つの方法
設定方法は以下の通りです。
以下の様にMariaDBの設定ファイルを開きます。
# vi /etc/my.conf.d/server.cnf
[mysqld]の次の行に以下を追記します。
transaction-isolation=READ-COMMITTED
編集を終了して、MariaDBを再起動すれば設定が反映されます。
# systemctl restart mysql
php-mysqlndの設定
さらにPHPからMariaDBへ接続するセッション設定も修正します。これによりセッションのパーシステンスが確保されMariaDBへの接続が速くなります。ここでは、php-mysqlndパッケージを利用しているとします。
以下の様にphp-mysqlndの設定ファイルを開きます。
# vi /etc/php.d/30-mysqli.ini
以下の行を追加します。
mysqli.allow_local_infile=On mysqli.allow_persistent=On mysqli.cache_size=2000 mysqli.max_persistent=-1 mysqli.max_links=-1 mysqli.connect_timeout=60 mysqli.trace_mode=Off
編集を終了して、Webサーバーを再起動すれば設定が反映されます。
PHPキャッシュの導入(OPcache、APCu)
次に、PHPのキャッシュ設定を行います。PHPのキャッシュとは何か簡単に説明しておきましょう。
PHPはインタープリター言語です。インタープリター言語とは実行の度にソースコードを中間言語にして実行しています。その中間コードをオペコードと言います。実行の度にソースコードを読み込んでオペコードに変換するのは非常に効率が悪く、実行速度も遅くなります。
しかし、オペコードはソースコードが変わらなければ生成されるものは同一です。では、それをキャッシュしておくとオペコードにする操作が省けるので実行が早くなるのでは?と考えた人がいて、それをキャッシュする機能をPHPに実装しました。これがPHPのキャッシュと呼ばれるものです。これは昔からある技術で、eAcceleratorやXCache、APCなどのキャッシュ用の機能拡張が行われてきました。しかし、PHPの新しいバージョンへの対応などの問題があり、安定的に利用できるものではありませんでした。PHP 5.5からは、Zend社が提供したOPcacheがPHPに組み込まれました。このPHPキャッシュ設定により、CPU負荷が下がり、実行速度がアップします。
OPcacheはオペコードをキャッシュしますが、実はオペコードだけでプログラムが実行されるわけではありません。プログラムを実行するにはデータが必要です。しかし、OPcacheにはこのデータをキャッシュする機能がありません。そこで、データをキャッシュするためにAPCuというものを使います。現在では、OPcacheとAPCuの両方を使って設定することが多くなってきています。
OPcacheのインストール/設定
OPcacheをインストールします。
CentOSの標準レポジトリにはOPcacheやAPCuがありません。そこで、レポジトリを追加してインストールします。
# yum install epel-release # yum install php-pecl-zendopcache php-pecl-apcu
しかし、デフォルトの設定のままでは中間コードのキャッシュが2秒なのでそれを600秒に伸ばします。また、fast_shutdownが無効なので有効にします。これによりセッションの切断が早くなりPHPの稼働効率が上がります。
/etc/php.d/opcache.iniの以下の項目を修正します。
opcache.enable = 1 opcache.enable_cli = 1 opcache.memory_consumption = 128 opcache.interned_strings_buffer = 8 opcache.max_accelerated_files = 4000 opcache.revalidate_freq=600 opcache.fast_shutdown=1
こちらの項目については、php.netのOPcacheのインストール手順でも紹介されています。
APCuの設定
上記の説明の通りOPcacheは、オペコードをキャッシュしてくれますが、データのキャッシュは提供されませんので、APCuを設定します。
/etc/php.d/apcu.ini の以下の項目を修正します。
apc.enabled = 1 apc.enable_cli = 1 apc.shm_size = 64M apc.ttl=7200
以上で、PHPのキャッシュ設定が終了です。
KVSの導入(Redis)
次にKVSを導入しましょう。KVSはKey Value Storeの略です。このKVSはSQLを利用するデータベースとは違うシステムです。ここではKVSをユーザーデータとセッション情報の保存先として使用します。上記のPHPキャッシュの導入では、APCuをインストールしました。APCuはデータをキャッシュする機能だと説明しました。しかし、キャッシュされたデータはどこに保存されるでしょうか?通常はLinuxのオンメモリに保存されます。そして、この場合 1台のサーバーで動かしているときには問題になりませんが、複数台のサーバーがあった場合、それぞれのサーバーのメモリーにユーザーデータが保存されます。これは効率が良くありません。
また、ユーザーのセッション情報もPHPがLinuxのファイルシステム上にファイルとして保存しています。セッション情報はブラウザー経由でユーザーが操作する度にチェックされ更新されます。ファイルに保存するという方式では読み込み書き込み時に負荷がかかります。そういった更新頻度が高いデータがハードディスクに書き込まれるというのは、負荷の高い操作になりがちです。
そこで、そういった問題を解消するためにメモリー上にユーザーデータとセッション情報を保存するように設定します。これによりハードディスクへのアクセスが減り、複数台のサーバーで実行したときにも効率の良いアクセスが可能になります。1台で動かしている場合でも十分効果の高いものですので、サーバーのメモリーの許す限りKVSを導入するとよいでしょう。
また、ownCloudは、ファイルの同時書き込みの競合チェックのために通常はデータベースを利用していますが、これについてもKVSを利用することによりownCloudのパフォーマンスを上げることが可能です。
Redisインストールと設定
Redisは、セッション情報等を入れるキーバリューストアサーバーです。KVSとして利用できるプロダクトはいくつかありますが、今回はRedisを利用します。Redisは他のKVSよりもスピードは劣りますが、セッション情報の破棄などが厳密にされており、ownCloud社ではmemcachedよりもRedisを推奨しています。セッション情報をRedisに切り替えることによりパフォーマンスが向上し、特に利用者数が増えてきたときに高いスループットを発揮します。
KVSのRedisをインストールします。
# yum install -y redis
saveで始まる行を全てコメントアウトします。
# sed -i.bak -e s'/^save/#save/' /etc/redis.conf
これは、Redisにあるデータを永続化するオプションを無効化します。Redisはデータを永続化する為にDiskに書き出す機能があります。しかし、ownCloudのRedisに保存するデータは、一時データであり永続化機能は必要ありません。また、永続化していた場合にはDiskへの書き込みが発生し、パフォーマンスが落ちてしまいます。
Redisサーバーを起動して有効化します。
# systemctl start redis # systemctl enable redis
PHP Redisパッケージのインストール
次に、PHPから Redisを利用する為のパッケージ php-pecl-redis をインストールします。
# yum install php-pecl-redis
PHPセッション情報をRedis上に保存
PHPのセッション情報をredisへ切り替えます。
以下のファイルを編集します。
# vi /etc/httpd/conf.d/php.conf
下記のように項目を書き換えます。
php_value session.save_handler "redis" php_value session.save_path "tcp://127.0.0.1:6379"
ownCloudでAPCuの設定
ownCloudのconfig.phpでRedisとAPCuを設定します。同時にownCloudが保持しているファイルの同時書き込みの競合チェックをDBへの書き込みからRedisのKVSへ移動します。ファイルの同時書き込み競合チェックをKVSにすることにより多くのユーザーが同時にファイルをアップロードするときのパフォーマンスが向上します。ownCloud 7の時にはこの機能がなくファイルの同時書き込み競合チェックがデータベースのボトルネックとなりパフォーマンスが頭打ちになっていました。
# vi /var/www/html/owncloud/config/config.php
以下の内容を「);」 の前の行に追記
'memcache.distributed' => '\OC\Memcache\Redis', 'memcache.locking' => '\OC\Memcache\Redis', 'memcache.local' => '\OC\Memcache\APCu', 'redis' => array( 'host' => '127.0.0.1', 'port' => 6379, ),
設定が終了したら、Webサーバーを再起動してください。
Redisに設定が変更されていれば、Redisサーバーを停止するとブラウザーからownCloudに接続できなくなるはずです。また、PHPのセッションファイルが /var/lib/php/session/ 以下に作成されますので、このファイルを削除してログアウトしなければ、セッション情報はRedis上で管理されています。
ApacheとPHP-FPMの設定
ApacheとPHP-FPMというのは聞き慣れないかたもいらっしゃるかも知れませんが、ApacheとPHPを分けることによりスピードアップする方法の一つです。通常は、Apache上でPHPを動かすmod_phpというのが一番ポピュラーな稼働方法です。しかし、これには問題点があります。その問題はApacheというWebサーバーとPHPが密につながりすぎているといことです。
例えばこういう状況を考えてみましょう。ユーザーがブラウザーから大量のファイルをアップロードしました。そのファイルのアップロードは時間のかかる操作でアップロードが終了するまでPHPがずっと実行されています。その場合、Apacheはプロセスをずっと起動したままPHPの処理が終わるのを待っています。それが複数同時に起こったとしたらどうでしょうか?
そういう状況の場合、Apacheのプロセスがずっと起動したままたくさん貯まっていくということが発生します。Apacheは、PHPを実行する以外にも静的なファイルをブラウザーに返したりするという機能も提供しています。これらのプロセスがたくさんになってくるとApacheの動作が重くなって、パフォーマンスが悪くなっていくのです。
そこで、Apacheの静的なファイルを返す機能とPHPを実行する機能を分割します。静的なファイルを返す機能をApacheで、PHPを実行する機能をPHP-FPMというデーモンで担います。PHP-FPMはPHPの実行のみを担当できるので、キャッシュや実行を効率的にこなすことができるようになります。
では、このPHP-FPMを設定しましょう。
PHP-FPM をインストールします。
まず、PHP-FPMのパッケージをインストールします。
# yum install php-fpm
次に、PHP-FPMデーモンを起動し、有効にします。
# systemctl start php-fpm # systemctl enable php-fpm
php-fpmをsocket起動に書き換えます。
# sed -i -e 's#listen = 127.0.0.1:9000#listen = /var/run/php-fpm/php-fpm.sock#g' /etc/php-fpm.d/www.conf
PHP-FPMは、ブラウザーからのphpを呼びだす時の接続をネットワーク接続と、Unixソケット経由での接続が可能です。Unixソケット経由の接続の方がネットワーク接続よりもスピードが若干速くなります。その分設定が難しくなります。
次いでPATH環境変数を修正します。
# sed -i -e 's#;env\[PATH\].*#env\[PATH\] = /sbin:/bin:/usr/sbin:/usr/bin#g' /etc/php-fpm.d/www.conf
Apacheの設定
Apache側もPHPの実行環境をmod_phpからソケット経由のPHP-FPMに変更します。
# vi /etc/httpd/conf.d/php.conf
以下の様な項目の記載を
SetHandler application/x-httpd-php
以下のように修正します。
SetHandler "proxy:unix:/var/run/php-fpm/php-fpm.sock|fcgi://localhost"
KVS導入時の以下の項目をコメントアウトします。(PHP-FPMでは別の場所で設定します)
# vi /etc/httpd/conf.d/php.conf
#php_value session.save_handler "redis" #php_value session.save_path "tcp://127.0.0.1:6379"
ApacheをPrefork起動からMPM Event起動に変更します。
# vi /etc/httpd/conf.modules.d/00-mpm.conf
LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
をコメントアウト、
#LoadModule mpm_event_module modules/mod_mpm_event.so
をアンコメントし、以下のようにします。
#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so LoadModule mpm_event_module modules/mod_mpm_event.so
httpdを再起動します。
# systemctl restart httpd
再起動するときの注意点
ApacheとPHP-FPMを再起動するときには、PHP-FPMを再起動してからApacheを再起動するようにしてください。
# systemctl restart php-fpm # systemctl restart apache
PHP-FPM ソケット接続のトラブルシューティング
PHP-FPMとApacheの組み合わせで困るのは、Unixソケット経由の接続が動かない場合にどこに問題があるか分かりにくく、トラブルシューティングしにくい点です。
以下の点に気をつけて設定を確認してください。
- www.conf で設定した listen = のパス(ここでは/var/run/php-fpm/php-fpm.sock)にUnixソケットファイルができているか確認する
- Unixソケットファイルのユーザーが合っているか確認する(WebサーバーがApacheならソケットファイルがApacheになっているか)
- Unixソケットファイルのパーミッションがあっているか確認する(読み書き権限があるか)
- Webサーバーで設定したproxy でSocketのファイルパスが合っているか確認する
以上の内容を確認してください。
PHPのバージョンアップ
PHPは新しいバージョンの方がパフォーマンスが上がります。最新のバージョンはPHP7.0です。できるだけ新しいPHPを使うことをお勧めします。
ここまでは、OSの標準パッケージのPHP5.5を使った設定を説明してきました。PHP7.0にバージョンアップすることによりパフォーマンスが2倍程度アップするとされています。弊社でも社内環境で利用していますが、CPUの負荷が下がったことを確認しています。
レポジトリのインストール
PHP7.0はRemiレポジトリを利用します。そのため、Remiレポジトリの設定をインストールします。しかし、RemiレポジトリはEPELレポジトリと依存関係がありますので、その前にEPELレポジトリもインストールします。
# yum install -y epel-release # yum install -y http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
古いPHPの削除とPHP7.0をインストール
古いPHPを一旦削除します。
# yum remove -y php\*
新しいPHP7.0をインストールします。
# yum install -y php php-fpm php-gmp php-mbstring php-mbstring \ php-mcrypt php-mysqlnd php-opcache php-pear-Net-Curl \ php-pecl-apcu-bc php-pecl-apcu php-pecl-redis php-pecl-zip \ php-soap php-intl php-ldap php-gd --enablerepo remi-php70
PHP-FPMの設定を再度設定
PHPを一旦削除し、再度インストールしましたので、設定がデフォルトに戻ってしまっています。再度設定し直す必要があります。
以下のファイルを設定します。
/etc/php.d/10-opcache.ini /etc/php.d/40-apcu.ini /etc/php-fpm.d/www.conf /etc/httpd/conf.d/php.conf
- /etc/php.d/10-opcache.ini の修正
- /etc/php.d/40-apcu.ini の修正
- PHPキャッシュの導入(OPcache、APCu)を再度設定します。
/etc/php-fpm.d/www.confの修正でUnixソケットを設定します。
# sed -i -e 's/;listen.owner = nobody/listen.owner = apache/g' /etc/php-fpm.d/www.conf # sed -i -e 's/;listen.group = nobody/listen.group = apache/g' /etc/php-fpm.d/www.conf # sed -i -e 's#listen = 127.0.0.1:9000#listen = /var/run/php-fpm/php-fpm.sock#g' /etc/php-fpm.d/www.conf
Webサーバーがnginxの場合は、Apacheをnginxにします。
環境変数PATHの設定
# sed -i -e 's#;env\[PATH\].*#env\[PATH\] = /sbin:/bin:/usr/sbin:/usr/bin#g' /etc/php-fpm.d/www.conf
PHP-FPMでセッション情報をPHPデフォルトのセッションファイル保存からRedisへ切り替え
# sed -i.bak -e 's/files$/redis/g' /etc/php-fpm.d/www.conf # sed -i -e 's#/var/lib/php/session#"tcp://127.0.0.1:6379"#g' /etc/php-fpm.d/www.conf
/etc/httpd/conf.d/php.confをApacheの設定で再度修正します。
以上でパフォーマンスチューニングは終了です。次回は、PHPを活用したownCloudのカスタマイズについてご紹介します。