ご利用ガイド ロードバランサーを使って1000Req/secを捌くWordPressクラスタを作る
目次
今回はConoHaのロードバランサーを使って、1000req/secをさばけるWordPressのクラスタ構成を作ってみます。WordPressの負荷軽減策やスケールアウトはよく語られるテーマではありますが、今回はロードバランサー、イメージ保存、1Gbpsのローカルネットワークなど、ConoHaならではの機能を使って構築してみます。またその際のチューニングポイントなども解説します。こちらはConoHa以外の環境でもきっと有用だと思います。
このページではあまり限界性能を引き出すチューニングを目指すのではく、実際に皆様がConoHa上でクラスタ構成を構築する際に、どういう構成を作れば良いのか、どんなオプションを使えば良いのか、スケールするにはどういうチューニングを行うのか、などの参考にしていただくのが目的です。WordPressは利用者も多く、PHPで書かれていてMySQLへの接続も必要な、かなり重量級のアプリケーションで、事例にするには良いでしょう。実際にConoHa上でVPSがどのくらい必要で、どのくらいのコストがかかるのか、ご参考になれば幸いです。
システム構成
今回の構成をご説明します。
ロードバランサー、Webサーバー、DBサーバーという基本的な構成です。ConoHaのロードバランサーを使い、配下にWordPressの稼働しているWebサーバーを配置していきます。そして、ローカルネットワークにMySQLサーバーを配置します。構成としてはnginx + php-fpm(PHP5.5) + MySQL(5.5)という環境です。
ロードバランサーはConoHaのロードバランサーを使います。これはL4ロードバランサーで、今回はラウンドロビンでWebサーバーにトラフィックを分散します。方式はDSRです。このロードバランサーは100万Req/secでも余裕で捌けるので、今回はボトルネックにはなり得ません(ロードバランサーの性能についてはまた今度。
すべてのWebサーバーとMySQLサーバーには、ConoHaの2Gプラン(CPU3コア 2GBメモリ)を使用しています。
テストには運用環境に近いWordPressを使用
WordPressもインストールしただけの状態では、負荷も低くベンチマークも良い結果が出ます。ただ、実際には、テーマをカスタマイズしてプラグインを入れて使われることがほとんどでしょう。当然負荷も増えます。
なので、今回はConoHaのオフィシャルサイトのWordPressをコピーしてきてそのまま使います。テーマもかなり作り込まれていますし、プラグインも多く使われています。負荷的にも一般的なWordPressサイトに近いと思います。
実際、初期状態のWordPressとベンチマークを比較すると、倍以上負荷がかかっていました(後ほど掲載)。
キャッシュは無効
nginxやFastCGIのキャッシュの使用はWordPress高速化の常套手段ですが、今回はアプリケーションの負荷を見たいので、キャッシュ類は無効にしています。なのでPHPも実行され、MySQLへの接続/クエリの実行も全てのリクエストで実行されています。PHPのOPCacheやWordPressのキャッシュ系プラグインはそのまま使っています。
イメージ保存によるインスタンスイメージの複製
ConoHaには「イメージ保存」というVPSのストレージをそのまま保存する機能があり、保存したイメージからは新規にVPSを作成することができます。今回の構成ではWebサーバを大量に複製する必要があるので、イメージ保存はとても便利です。そしてConoHaのAPIを利用することでVPS作成を自動化することができます。これにより、大量のサーバー構築を手間をかけずに行うことができます。
このあたりの話はSoftware Design 2015年9月号にも書きましたので、良かったらご覧下さい。以下のURLからもご覧いただけます。
第2回 海外リージョン,ロードバランサ,GeoDNSを使う:個人利用から大規模開発までConoHaで始めるクラウド開発入門|gihyo.jp … 技術評論社
負荷の傾向とチューニングポイント
今回の構成でのチューニングポイントや設定値などをご紹介します。
サイトの組み方にもよりますが、WordPressシステムの場合、最も多くCPU時間が必要になるのはPHPの処理です。MySQLに大きな負荷はかかりません。ただ、PHP自体の性能向上はあまりできません。OPCacheというオプコードキャッシュを入れたり、HHVMなどにする方法がありますが、取れる策はこれくらいでしょう。
なので、今回のシステムでは、Webサーバーの数を増やすことで性能向上を図ります。一方MySQLには大量のDB接続が来るので、Webサーバーが増えても接続エラーなどが出ないようなチューニングを行います。
結論から言ってしまうと、Webサーバ30台でWordPressを1,000リクエスト/秒で捌くことができました。以下はそのときの設定とチューニングポイントです。
php-fpm
php-fpmはPHPのFastCGI実装です。prefork型なので、基本的には子プロセスの起動数等の調整を行うことになります。Apacheのprefork MPMと同じような感じですね。
/etc/php-fpm.confや/etc/php-fpm.d/などに設定があります。
・ pm.max_children
・ pm.max_requests
・ pm.start_servers
・ pm.min_spare_servers
・ pm.max_spare_servers
php-fpmプロセスの並列数を調整します。ディレクティブの説明はPHPのマニュアルにあります。
重要なのはpm.max_childrenで、これはphp-fpmのプロセス数の上限を決めます。増やすと並列数が上がり同時に処理できるリクエスト数が増えますが、増やすとその分メモリを食うのでトレードオフになります。基本的にはswapに落ちないように増やしていけば良いのですが、WordPressのように重いアプリケーションを動かす場合、先にCPUが頭打ちになることもあります。今回はCPUが3コアしか無いので、php-fpmのプロセス数20くらいでCPU占有率100%になりました。こうなると20以上に増やしても性能は期待できません。
nginx
ここはボトルネックにはなりません。以下の設定以外は、デフォルトのまま使いました。
worker_processes 3;
events {
worker_connections 2048;
multi_accept on;
use epoll;
}
MySQLの調整
今回MySQLサーバーは一つです。WordPressが発行するクエリは単純なSELECTばかりなのと、クエリキャッシュなども効きやすいので負荷はそんなに上がりません。ただ、HTTPリクエストの度にMySQLへ接続が来るので、コネクションの数はそれなりに大きくなります。
MySQLのパラメータで気をつけるのはこのあたりでしょうか。
・ max_connections
・ thread_cache_size
・ back_log
max_connectionsはMySQLサーバに同時に接続できる上限値です。接続数が増えると当然メモリの使用量が増えるので、トレードオフを見ながら調整します。今回の構成では1500前後の同時接続数になりましたので、それより大きな値にします。
thread_cache_sizeは、一度作成したスレッドをMySQLがキャッシュする数です。スレッドの作成は重い処理です。以下のように、負荷をかけながらThread_createdの値をチェックし、増加し続けているならこの値を増やす必要があるでしょう。一般的にもmax_connectionsと同じ数を設定するのが良いようです。
mysql> show global status like 'Thread_%';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_cached | 2 |
| Threads_connected | 670 |
| Threads_created | 42318 | <= 増え続ける場合はthread_cache_sizeを増やす
| Threads_running | 17 |
+-------------------+-------+
4 rows in set (0.00 sec)
back_logはOSがTCP接続要求を受けつけてアプリケーションが処理する開始するまでのキューの長さです・・・。ちょっと違いますか・・・。アプリケーションがlisten(2)で接続を受け付け、クライアントからTCP接続要求が来ると、それはカーネルのlistenキューに格納されます。このlistenキューの長さがbacklogです。接続要求はアプリケーションがaccept(2)するとデキューされます。
backlogが一杯の状態で新規にTCP接続要求がきた場合(クライアントがSYNパケットを送った場合)、サーバー側はそれを無視します。そのためクライアントは何度かSYNパケットを送り、再送回数を超えた場合タイムアウトと判断します。再送回数は/proc/sys/net/ipv4/tcp_syn_retriesから取得できます。接続数が多いと、クライアント側でタイムアウトが増え、スループットが上がらないという問題になります。
実際、backlogの変更をせずに負荷をかけてみたところ、MySQLサーバー側でSYNパケットの取りこぼしが発生しました。これはnetstat -sの以下の項目で確認できます。取りこぼしがなければ、これらの項目は現れません。
# netstat -s | grep -i list
1103 times the listen queue of a socket overflowed
1481 SYNs to LISTEN sockets dropped
さて、backlogの設定は、MySQL側とカーネルパラメータの両方を変更する必要があります。MySQLのback_logはデフォルトで50、カーネル側は多くの場合128です。これは少なすぎなので1024にします。カーネルの方は設定値の2/3が使われるので、少し多めに設定します。先ほどのmax_connections, thread_cache_sizeと併せて、my.cnfを設定します。(設定した後はmysqldをリスタートして下さい)
・my.cnf
[mysqld]
max_connections = 2000
thread_cache_size = 2000
back_log = 1024
カーネルパラメータの設定は/etc/sysctl.confに書きます。
・/etc/sysctl.conf
net.core.somaxconn = 2048
net.ipv4.tcp_max_syn_backlog = 2048
sysctlコマンドで設定を有効化します。
# sysctl -p
net.core.somaxconn = 1024
net.ipv4.tcp_max_syn_backlog = 1024
これでSYNパケットの取りこぼしがなくなります。
ネットワーク受信キューの調整
もう一つ、今回のMySQLサーバーのように大量のパケットが届くサーバーでは、ネットワークの受信処理が1つのコアに集中し性能が落ちることがあります。以下は負荷をかけているときのMySQLサーバのtopコマンドの実行結果です。CPU1だけ28.3 siとソフトウェア割り込みが多く、負荷の高い状態です。
top - 19:58:24 up 20 min, 1 user, load average: 3.49, 1.36, 0.76
Tasks: 90 total, 5 running, 85 sleeping, 0 stopped, 0 zombie
%Cpu0 : 13.1 us, 21.1 sy, 0.0 ni, 51.6 id, 13.5 wa, 0.0 hi, 0.4 si, 0.4 st
%Cpu1 : 24.8 us, 41.4 sy, 0.0 ni, 5.2 id, 0.3 wa, 0.0 hi, 28.3 si, 0.0 st
%Cpu2 : 9.5 us, 15.3 sy, 0.0 ni, 72.7 id, 2.5 wa, 0.0 hi, 0.0 si, 0.0 st
これはRPS(Receive Packet Steering)を使用することで複数コアに分散させスループットを上げることができます。RPSはNICのバッファから取得したデータをソフトウェア的に処理する前に複数CPUに分散してくれます。RPSについては以下のリンクなどを参照していただければと思います。
8.7. Receive Packet Steering (RPS)
Linuxでロードバランサやキャッシュサーバをマルチコアスケールさせるためのカーネルチューニング
設定は以下のコマンドで行います。rps_cpusは処理を割り当てるCPUコアを指定するパラメータで、下位ビットからCPU0, CPU1, CPU2となっています。今回はCPUが3コアなので0111の十進表現で7を設定します。4コアの場合は”f”になります。
# echo "7" > /sys/class/net/eth1/queues/rx-0/rps_cpus
こうすると以下のように均等に負荷が分散されました。今回は3コアですが、コア数が多い場合は更なるパフォーマンス改善を期待できそうです。
top - 20:11:09 up 33 min, 1 user, load average: 0.08, 0.27, 0.57
Tasks: 90 total, 3 running, 87 sleeping, 0 stopped, 0 zombie
%Cpu0 : 15.8 us, 26.8 sy, 0.0 ni, 43.0 id, 1.3 wa, 0.0 hi, 13.1 si, 0.0 st
%Cpu1 : 16.4 us, 26.5 sy, 0.0 ni, 44.6 id, 2.0 wa, 0.0 hi, 10.4 si, 0.0 st
%Cpu2 : 11.2 us, 24.5 sy, 0.0 ni, 36.1 id, 10.0 wa, 0.0 hi, 18.2 si, 0.0 st
ベンチマーク
ではベンチマークです。今回はwrkを使用しました。”modern HTTP benchmarking tool”とのことで、C言語で実装されていて、スケーラブルなマルチスレッドをサポートし、一つのマルチコアCPUで動作することを想定して開発されたツール、だそうです。Luaスクリプトでいろいろカスタマイズできるようですが、残念ながら私が知らないため試せてません。。。
# wrk --help
Usage: wrk <options> <url>
Options:
-c, --connections <N> Connections to keep open
-d, --duration <T> Duration of test
-t, --threads <N> Number of threads to use
-s, --script <S> Load Lua script file
-H, --header <H> Add header to request
--latency Print latency statistics
--timeout <T> Socket/request timeout
-v, --version Print version details
Numeric arguments may include a SI unit (1k, 1M, 1G)
Time arguments may include a time unit (2s, 2m, 2h)
-cで接続数、-tでスレッド数(並列数)、-dで実行時間を指定します。-latencyオプションで応答時間の統計が表示されるので、これも指定します。実行結果は以下のようになります。
# wrk -c 300 -t 5 -d 1m --latency http://157.7.94.65/
Running 1m test @ http://157.7.94.65/
5 threads and 300 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 292.21ms 112.70ms 1.11s 76.42%
Req/Sec 207.94 44.20 380.00 70.09%
Latency Distribution
50% 268.31ms
75% 340.15ms
90% 434.09ms
99% 685.91ms
62073 requests in 1.00m, 1.02GB read
Requests/sec: 1032.88
Transfer/sec: 17.36MB
ベンチマーク結果は以下のようになりました。接続数200~300くらいに性能のピークがありそうです。それ以上になると応答時間が長くなってきます。全てのベンチマーク結果はこちらのGistをご覧下さい。
また、VPS一台に対して行ったベンチマークはこちらで、37req/secくらいでした。これが30台あるので 37 * 30 = 1100 と、おおよそ台数に応じて性能が上がっていると思います。
そして最後にWordPressをインストールしただけの状態のベンチマークはこちらです。67req/secほど出ています。これはConoHaのWordPressアプリケーションイメージを使い、PHPの設定だけ今回の環境に合わせたものです。テーマを作り込んでプラグインをインストールするだけでも、WordPress自体の負荷がかなり増えることがわかります。
おわりに
というわけで、ベンチマークとチューニングポイントなどをご紹介しました。ConoHaはイメージ保存やロードバランサーなど、Webアプリケーションをスケールアウトするのに便利な機能がそろっています。一方で、サーバーを増やしたときに、それに応じてきちんと性能が上がるような設定を行うのも、非常に重要になります。ボトルネックになるポイントを見つけるのは難しいこともありますが、このエントリが何かお役に立てれば幸いです。
私は過去にnginxのキャッシュを設定したのに全くキャッシュが効かず、実は某プラグインがこっそり「Cache-Control: no-cache」と吐いていて一日無駄にしたりしました・・・。
- 問題は解決できましたか?
-