KUSANAGI(nginx + Apache + PHP-FPM + MariaDB)で運用している本サイト(bacchi.me)を含む複数サイトの表示が遅くなる事象が発生したため、調査から対処までの内容を記録しておきます。
症状
bacchi.me など同一 VPS 上でホストしているサイトすべてで表示が遅くなっていました。
調査方法
まずは負荷状況を確認するため、基本的なコマンドで全体像を確認しました。
uptime
free -h
df -h
nproc
結果、以下が判明しました。
- load average が 12.83(CPUは3コアのみ)と、コア数に対して大幅に過大
- 物理メモリ2GBに対し、swapを2.5GB/4GB使用
次にプロセス単位で状況を確認しました。
ps aux --sort=-%cpu | head -20
ps aux --sort=-%mem | head -20
単一プロセスがCPUを占有しているわけではなく、php-fpmのworkerプロセスが多数、D状態(ディスクI/O待ち)やR状態で滞留していましたた。
そこで vmstat でメモリのswap in/out とI/O待ち(iowait)の推移を見たところ、決定的な兆候がでていました。
vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
5 0 2521544 204572 8816 385652 107 101 170 108 2 1 5 1 92 2 0
6 1 2524260 73128 8816 385532 148 2748 148 2750 2878 1562 45 6 48 0 0
0 5 2532540 74396 8816 384876 272 8516 272 8516 2833 1487 41 3 21 35 0
0 5 2544272 67460 8816 388776 1020 12472 5116 12472 1964 1575 8 3 12 77 0
0 5 2554508 69408 8816 388724 612 10968 612 11028 1572 1354 1 2 4 93 0
swap の読み書き(si/so)が継続的に発生し、iowait が瞬間的に77〜93%まで跳ね上がっていました。空きメモリ(free)も数十 MB 程度しか残っておらず、典型的なスワップ・スラッシング状態でした。
原因を掘り下げるため、MariaDBとPHP-FPMのメモリ関連設定を確認しました。
mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
mysql -e "SHOW VARIABLES LIKE 'max_connections';"
grep -E "^pm|^pm\." /etc/opt/kusanagi/php-fpm.d/www.conf
結果は以下の通り。
- MariaDB
innodb_buffer_pool_size = 768M(物理メモリ2GBの約40%) - MariaDB
max_connections = 900(2GBのVPSには過大) - PHP-FPM
pm.max_children = 50
実際の php-fpm ワーカー1プロセスあたりの RSS (実使用メモリ)を計測すると、平均 65MB 前後だった。
ps aux | grep 'php-fpm: pool' | awk '{sum+=$6; count++} END {print sum/count, count}'
pm.max_children = 50 × 65MB ≒ 3.3GB となり、MariaDB のバッファプール 768MB と合わせると、瞬間的な必要メモリが物理 RAM (2GB)を大きく超える設計になっていたことが分かりました。アクセスが重なり php-fpm の worker が増えるたびに、Linux カーネルが強制的にページを swap へ追い出し、その結果 I/O 待ちが発生してリクエスト全体が詰まる、という悪循環が起きていました。
修正方法
物理メモリ 2GB に収まる範囲まで、PHP-FPM と MariaDB のメモリ関連設定を縮小ました。
PHP-FPM(/etc/opt/kusanagi/php-fpm.d/www.conf)
| 項目 | 変更前 | 変更後 |
|---|---|---|
| pm.max_children | 50 | 12 |
| pm.start_servers | 10 | 3 |
| pm.min_spare_servers | 5 | 2 |
| pm.max_spare_servers | 15 | 5 |
MariaDB(/etc/my.cnf.d/server.cnf)
| 項目 | 変更前 | 変更後 |
|---|---|---|
| innodb_buffer_pool_size | 768M | 256M |
| max_connections | 900 | 100 |
設定ファイルは変更前にバックアップを取得したうえで編集し、以下の順でサービスを再起動して反映しました。
systemctl restart mariadb
systemctl restart php-fpm
修正後の動作
再起動直後はまだ残っていた swap の影響で応答が2秒台でしたが、数秒後には安定し、明確な改善が確認できました。
| 指標 | 修正前 | 修正後 |
|---|---|---|
| load average | 12.83 | 2.61 |
| iowait | 77〜93% | 0〜2% |
| bacchi.me 応答時間 | 遅延 | 0.30秒 |
php-fpm の worker 数も実測7プロセス(上限12)に収まり、swap の新規発生もほぼ止まったようです。
まとめ
複数の WordPress サイトを 2GB という小さめのメモリの VPS に同居させる場合、MariaDB や PHP-FPM のデフォルト・過去の設定値が実メモリに対して過大になっていないか定期的に見直す必要がある。特に php-fpm のpm.max_childrenは「実測RSS × max_children」が物理メモリを超えないよう設計するのが重要だと再確認しました。トラフィックが今後増える場合は、設定チューニングだけでなく VPS 自体のプラン変更も検討したいです。