お名前.comのVPSで運用中のNginx + PHP-FPM + MySQLサイトをチューニング

お名前.comのVPS(メモリ1Gのプラン)で運用しているNginx + PHP-FPM + MySQLで作ったサイトが最近大変重くなってしまいました。
お名前.comのVPSは2年ほど使っていて、今まではあまりパフォーマンス的にストレスを感じてなかったのですが、アクセス数が増えたためか、ほぼ一瞬で表示されるはずのキャッシュ状態のページも5〜6秒もかかってしまうのです。

そのまま放置して置くわけにも行かないし、メモリ1Gプランでは限界かなと思いながらも、幾つかの設定を変えながらチューニングしました。

サーバの環境はこれです。

  • CentOS6.8
  • Nginx
  • PHP-FPM
  • MySQL
  • Syslog-ng

topvmstatコマンドで調べたところ、CPUの利用率は高くなかったのですが、io待ちとswap利用率が高いのが気になりました。
MySQLの負荷のためかと思い、Slow Queryログを覗いてみたら、Indexが使われているはずのSELECT文に、カラムもあまり持たないINSERTやUPDATE文も大量に出力される大祭りでした。

アクセス数が増えたとはいえ、これまでDBが重くなるのはすでにINDEXの次元ではないでしょう。
メモリ利用率とswapの高い数値からみると、一部のプロセスが無駄にメモリを占有しているのではないかと、疑問が出てきます。

ところで、ps auxで見ると…

問題点

  • php-fpm: pool wwwの数が多い。
  • /usr/libexec/mysqldのメモリ数値(%MEMVSZ)が高い。

これで、原因がわかりました。

原因

  • メモリ空容量が足りなくて、swap in/outが頻繁に行われている。

今の設定だと php-fpmのプロセス起動数が多いので、下記のように修正します。

PHP-FPMの設定

/etc/php-fpm.d/www.conf

設定項目 設定値 設定内容
pm dynamic 動的にプロセス数を調整してくれるdynamic設定は、
メモリの少ないVPSサーバーでは大事でしょう。
pm.max_children 15 アクセスMax状態での最大プロセス起動数を少なめに減らします。
pm.start_servers 3 最初から起動するプロセス数を最低限にします。
pm.min_spare_servers 3 処理待ち状態の最低起動プロセス数を最低限にします。
pm.max_spare_servers 6 処理待ち状態の最大起動プロセス数も少なめに設定します。

PHPプロセス毎の最大メモリ利用制限もデフォルトの半分に減らしておきます。
メモリを多く使うのはバッチ処理であって、Webリクエストでは偉い処理をしないため、とりあえず少なくして置いて様子を見ます。

設定項目 設定値
php_admin_value[memory_limit] 64M

また、mysqlの設定で、各BufferとCache設定を少なめに変更します。
システムメモリが足りないせいで、OS側でSwap IN/OUTが頻繁に行われてはMySQLのCacheが何の価値もありません。

MySQLサーバーの設定

/etc/my.conf

設定項目 設定値 設定内容
max_connections 10 今のアクセス数では最大同時接続数がこれで十分。
少ない数で早く処理したいところ。
thread_cache_size 3 ThreadをCacheにいくつ保存しておくのかの設定だが、
少なくする。
sort_buffer_size 2MB ソートの際に利用される領域ですが、デフォルトの2Mのままにしておく。
join_buffer_size 4M INDEXが使用されない場合の、テーブル結合時に使われるバッファ。
read_buffer_size 1M 全件検索時のレコードがキャッシュされるバッファのサイズ
read_rnd_buffer_size 4M キーを使用したソートで読み込まれた行がキャッシュされるバッファ。
query_cache_size 8M クエリキャッシュで使用するメモリサイズ。
thread_concurrency 2 処理を同時に実行できるスレッド数の上限値、CPUのコア数に合わせる。
innodb_buffer_pool_size 64M InnoDBのデータとインデックスをキャッシュするバッファのサイズ。
innodb_additional_mem_pool_size 4M InnoDBテーブルの定義情報など、データディクショナリ情報を格納。
innodb_log_buffer_size 1M ログファイルのためのバッファのサイズ。
通常ログファイルにはCOMMIT時に書き込まれるので、少なくしても問題なし。

Nginxの設定も一緒に変更しておいます。

Nginxプロセスの設定

/etc/nginx/nginx.conf

設定項目 設定値 設定内容
worker_processes 1 CPUのコア数に合わせるのが一般ですが、PHP-FPMとMySQLともサーバ同居の為最低限にします。

最後にSwap領域の解放しておきます。

Swap領域の解放

$ swapoff -a && swapon -a

以上の設定で、サイトのアクセスが快適になりました。
それから1時間程vmstatで様子をみてもswapが低値のまま落ち着いているようでした。

$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0   4992 129452 149760 451524   44   18   187    79    0    0  2  0 87 10  0    
 0  0   4992 129452 149760 451520    0    0     0     0   36   50  0  0 100  0  0   
 0  0   4992 129452 149768 451520    0    0     0   156   38   63  0  0 94  7  0

これで完璧とはかぎらないですが、問題そのものは綺麗に解決できていますし、今後の様子を引き続き見ていきたいと思います。

過去オンラインゲーム運用の時は、数百万人のユーザ向けに億単位のデータでサービスを提供するなかで、高可用性、耐久性を求めて余裕を持ったインフラ・アーキテクチャ設計、負荷分散、サーバーチューニングをしてきたわけですが、VPSのような低スペックのマシーンにいろんなものを積んでパフォーマンスを絞り出すというのは、違う意味で達成感を感じることができました。