wordpress」タグアーカイブ

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) にもその影響が波及することになります(これは正直わからなかった)。またサイト全体も目に見えて重くなります。コメントスパム等の絨毯爆撃を食らうとさらに深刻なことになるわけです。ただ設定で回避できるという話もあるので、試してみるのは悪くないかもしれません。

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

WordPressのWXR形式とPosterousからのインポート

2013年4月末に終了したPosterousというブログサービスから、借りているVPSに手作業でインストールしたWordPressに記事を移した際に行った事のメモ書きです。もうPosterousというものは世の中にはないけど、手法としては参考になるかもしれないし、自分への覚え書きとして残しておきます。

WordPressでは、インポートやエクスポートの形式として WXR形式 というものをサポートしています。RSS+αといった感じ。用途としてはMovableTypeのMT形式のようなものでしょう。

Posterousもさすがに終了時に記事をZIPでエクスポートできて、その中に wordpress_export_1.xml というファイルがあったので「あぁこれはWXR形式なんだな」と思っていたんだけど、3ヶ月放置してマズイマズイと思ってWordPress設置してWXR形式としてインポートしたら見事にエラーが出た。参った。これは妥当なWXR形式じゃない!

PosterousのエクスポートZIPには、中を見ると1ファイル1記事となったPosterous独自と思えるXML形式があったので、とりあえずメールのような「ヘッダ、空行区切り、ボディ」といった自分独自のテキストファイル変換した

XMLは見づらいし扱いづらいので、メールっぽいUTF-8テキストファイルになれば、後はWordPressが受け付けてくれるWXR形式を作ればいいだけになる。今回は WXR(WordPress eXtended RSS)から記事をインポートする方法 – (DxD)∞ というページを大いに参考にさせてもらった。記事中のWordPress(2013年8月現在バージョン3.6)もWXR形式もバージョンが古いけど、それでもうまく行った。XML文字列をテンプレートにして、渡した独自形式テキストファイルの数だけitem要素を反復するスクリプトを書いて動かした。

タグやカテゴリのインポートは面倒なので諦めることにした。たかだか40くらいの記事だし、元々がタグやカテゴリの設定に不満なこともあって、取り急ぎインポート出来ればいいやと思った次第。タグやカテゴリ、あと欠損した画像とかは手作業でやると割り切りました。

40記事くらいであれば本当に全部手作業でインポートしてもいいかなと思ったけど、WordPressの投稿インターフェースでは過去の投稿日で投稿することはできないことが分かったので、WXR形式を少し真面目に作ることにしました。これはパーマリンクを “/?p=数字” ではなくて “/年/月/ポスト名” などとしている場合に響いてくる話。MySQLのWordPressのデータ構造を読みといて…というのも、テーブル数が少ないWordPressであることもあって出来なくは無さそうかなと思ったけど、WXR形式を作るほうが安全だろうなと思います。

他のPosterous難民は、Posterous終了直前にWordpress.comやTumblrから救済措置としてPosterous APIを使ったインポートツールを提供したので、そっちに移行したか、コンテンツを失ってしまったかのどちらかなのかなと思います。私も一応 post-tumblr.tetsuji.jp というTumblrに一時避難させました。ただ、こちらはしばらくしたら閉鎖したい。あと、PosterousのパーマリンクがWordPressとは相容れない形式だったので、URLとしての一意性を失ってしまったというのは痛い。独自ドメインでやっていたので、そういうリンクを拾おうと思えば拾えるのだけが救い。これをApache+mod_perlで拾って適切なリンクに誘導する方法も考えているので、後々のブログネタにする予定です。

もし将来的にWordPressから別のブログサービスに移行したくなったときも、たぶんWXR形式が役に立つんじゃないかな。永続性の担保されないサービスへデータを預けると、後々大変になるかもという話でした。

4月末から3ヶ月以上アウトプットの場を失っていた感じでしたが、いよいよ復活。これからアウトプットもどんどんしていきます。以前にさかのぼって記事を書いたり、いろいろはかどりそうです。