日別アーカイブ: 2013年12月5日

WordPressで新規投稿ができなくなった原因がプラグインだった

おがた (@xtetsuji) です。

このブログ、2013年の夏から自分で契約したVPSにWordPressを入れて運用しているのですが、2013年の11月頃から新規投稿ができなくなってしまいました。新規投稿URL wp-admin/post-new.php にアクセスすると画面真っ白。個々のブログの画面表示も重くなった感じ。複数の投稿をDBから引っ張ってくるだろうトップページもまた画面真っ白という状況。

何が悪いのか原因を調査してもよく分からず。閲覧は重いながらもできたので、しばらく放置していました。

最近コメントスパム爆撃を受けた

結果的にこれは直接の関係は無かったのですが、2013年11月に大規模にコメントスパム爆撃をくらって、サーバがたびたび無反応になるケースが増えてきました。VPSのバーチャルシリアルコンソールでもログインができず、OOM (Out of Memory) Killer によってApacheがkillされる光景だけが見えるというもの。

Apacheのprefork MPMとmod_phpでPHP運用するケースは多いと思いますが、prefork MPM のパラメータ調整をしておかないと、Apacheの子プロセスのメモリサイズが増え続けることがあるので注意したいところです。

<IfModule mpm_prefork_module>
    StartServers             5
    MinSpareServers          5
    #MaxSpareServers         10
    MaxSpareServers         15
    #MaxClients             150
    MaxClients              50
    MaxRequestsPerChild    500
</IfModule>

Debianのパッケージの素で入れたApache2とprefork MPM (libapache2-mpm-prefork) の場合、MaxRequestsPerChild が0になっています。これは「子プロセスはどんなにリクエストを受けても刷新されない」という意味。

PHPも大きなファイルを扱ったりやコメントスパムなどの大量爆撃を受けたりすると、解放されないメモリが出てきたりします。PHP、すなわちmod_phpとApacheは渾然一体なので、これはApacheの子プロセスのメモリサイズが肥大化していく事を意味します。

だいたい、サーバでApacheが使える合計メモリサイズを計算し、だいたい1プロセス辺りに期待する最大プロセスサイズを割った数くらいをMaxSpareServersに設定するとよいでしょう。

「Apacheの子プロセスが実際にこれだけのサイズになったら自動的に終了(child terminate)してほしい」という要求はPHPでも出来るとは思うのですが、私はあまりPHPに詳しくないPerlプログラマだったので、mod_perl2のApache2::SizeLimitというモジュールで行いました。Debianであればmod_perl2 (libapache2-mod-perl2) を入れておけば使えるようになるでしょう。

mod_perl2を有効にして (Debian であれば sudo a2enmod perl として Apache を再起動すれば良い) 以下の記述を Apache の設定ファイル (Debian であれば apache2.conf) に書いて再起動して反映すればよいです。

<Perl>
use Apache2::SizeLimit;
# sizes are in KB
$Apache2::SizeLimit::MAX_PROCESS_SIZE  = 12000; # 12MB
$Apache2::SizeLimit::MIN_SHARE_SIZE    = 6000;  # 6MB
$Apache2::SizeLimit::MAX_UNSHARED_SIZE = 5000;  # 5MB
</Perl>
PerlCleanupHandler Apache2::SizeLimit

使用できるメモリを確認する

ネットを検索すると、特にレンタルサーバでは使用できるメモリ容量に制限があることで画面真っ白現象に見舞われることがあるとのことでした。

対処法としては

  • wp-config.php に define(‘WP_MEMORY_LIMIT’, ’64M’); と書く
  • .htaccess か ApacheのVirtualHostの設定ファイルに php_value memory_limit 64M と書く
  • php.ini に memory_limit = 64M と書く

などといった解決方法が見つかりました。ただ、Debian wheezy の標準の PHP では既にphp.iniで64MBになっているようで、あまりこの部分は関係ないと思われます。

デバッグモードにする

手がかりを得るために、WordPressにあるデバッグモードをオンにしました。

具体的には wp-config.php にある以下の false を true にすること。PHPファイルは都度読み込まれるのでApacheの再起動などは必要ありません。

define('WP_DEBUG', false);

これでブラウザの画面やApacheのエラーログに情報が出力されるようになります。

ただ、これで得られたのは「GoogleアナリティクスのプラグインがWordPressの変数を再定義している」というNoticeくらい。Noticeなので大した影響もないわけで、途方にくれました。

チャット(Yancha)で聴いてみたら「プラグイン、テンプレート、これらが怪しい」ということで、最近入れたプラグインを無効化して一つ一つ試していくことにしました。

そうしたところ、数回のアクセスで以下のような出力を得ることができました。

PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 12565 bytes) in /var/www/sites/post.tetsuji.jp/wp-content/plugins/crayon-syntax-highlighter/crayon_wp.class.php on line 261

私の場合、Crayon Syntax Highlighterというプラグインがメモリをバカ食いしていたようです。何かのサイトでシンタックスハイライトの便利なプラグインとして紹介されていたので導入したのは10月頃だった気がするのですが、これを無効化したところ、wp-admin/post-new.php で新規投稿もできるようになり、トップページの表示も非常に早くなりました。

このプラグイン、もともと動作が遅いことで有名なようで、検索してみると色々な情報がひっかかりました。設定を改善することで多少は速くなるのかもしれません。

WordPressでトラブルにあったら

まずは落ち着いて WP_DEBUG を true にしてデバッグ出力を観察。

今まで出来ていたことが出来なくなった系は、大抵は最近入れたプラグインかテンプレートに起因すると見て良いので、デバッグ出力を観察しながら確認。

参考サイト

様々なサイトを参考にさせていただきましたが、一部を列挙します。

結論

自分でブログを運用するのは、安価で自由度が高い反面、トラブルに会うと自分で解決しなきゃならないので、面倒なことを経験したくない人は、多少課金してでも商用のブログサイトを使いましょう

シンタックスハイライトについては、最近ではGistが標準となっているので、Gistに投稿したうえでそれを埋め込むというのが速度的には有利かもしれません。Gist埋め込みはEmbed GitHub Gistというプラグインを使っています。これに関しては特に問題は起こっていません。

Crayon Syntax Highlighterはとにかく重い。また、投稿画面の拡張も行うので、投稿画面 (post-new.php) にもその影響が波及することになります(これは正直わからなかった)。またサイト全体も目に見えて重くなります。コメントスパム等の絨毯爆撃を食らうとさらに深刻なことになるわけです。ただ設定で回避できるという話もあるので、試してみるのは悪くないかもしれません。

コミュニティは素晴らしい。実際に質問できる人がいる。これ以上に心強いものはありません。みなさんも私が通っている勉強会にいらっしゃいませんか?