先日農薬入りピザの件でバズったため、1日で3万PVぐらいあったのですが、サイトが落ちていたという報告をPHP界の良心安藤さんからいただきました。たしかに、はてぶでも「見れない→見れた」というブコメを散見しました。
このサイトは生意気にもCloudFlareの有料版を導入している富豪ブログなのですが、CloudFlareがエラー画面を返していたようです。なんでそんなことになったのかということについてですが、再現性がないのでほとんど推測なのですが、こんな感じかと。
- CloudFlareは主に静的なファイル(JS、画像、CSS)だけの配信に使っていた。
- よって、ブログ本体(WordPress)へのアクセスは素通りし、さくらVPS 2GのNginxが対応
- 戦いはNginx+PHP fpm + WordPress VS 3万PV * 60kbの様相を呈する
- Nginx陥落。CloudFlareにはAlways Onlineという機能があり、サイトが落ちた場合は自動的にキャッシュを返すはずだが、そうならなかった
- 結果、CloudFlareにはエラー画面が表示される
しばらく立って、一度もサーバにログインすることなく復活。なぜ?
Nginxが落ちたときはよく504 BadGatewayを返すのですが、それがそのまま出たんでしょうか。CloudFlareのブログに寄せられたコメントによると、500とか503の場合だけキャッシュを返さないという報告が上がっているのですが。
A server reboot will not necessarily kick this in. The Always Online feature will work ONLY with a 502 or 504 error being returned when we try to connect. It will not work for these errors:
500 internal server error
empty reply from server
503 service temporarily unavailable (we did try 503s at one point…but it actually created more problems than it solved).
Dammon Billian
まあ、理由はよくわかりませんが、Nginxがアップアップになって見られなくなったわけです。さくらのVPSは突然制限かけられて激重になるから要注意とかも関係あるかなーとは思いましたが、とにかく3万/1dayに堪えられなかったわけですね。
同じ構成のカイ士伝がさくらVPS512MBで耐えきったことを考えると、チューニングが間違っているのではと思えなくもないですが、そこら辺は今度@wokamotoさんに聞いてみるとして、本題のCloudFlareですよ。
Page rulesを設定してみる
さて、CloudFlareはデフォルト設定だとHTMLやPHPといったファイルへのアクセスはキャッシュしません。しかし、Page rulesという機能を使うと、この設定を個別に上書きすることができます。試しに このサイトでabout以下をキャッシュするようにしてみました。設定はこんな感じ。
- Custom caching
- Cache everything (=全部キャッシュ)するを選択しました。クエリストリングを無視(ex. 外部サイト経由で付加される_utm_sourceを無視するとか)したり、色々できます。
- Edge cache expire TTL
- CloudFlare上のキャッシュ有効期限ですね。これは
Respect all existing header
を採用しました。つまり、「Nginxが返すヘッダーを尊重せよ」ということですね。
さて、Nginxのヘッダーを尊重させるとどういうことができるかというと、アプリケーション側から明示的にキャッシュ有無や有効期限を指定できるということです。
たとえばWordPressには nocache_headers
という関数があって、これを実行するとキャッシュが使われません。管理画面やログイン画面、コメントフォームなどに埋め込まれています。
今回は検証の為に /about/ だけではnocache_headers
を実行し、それ以外(/about/wp-plugins/とか)はCloudFlareのキャッシュが有効になることを確かめます。
add_action(’template_redirect’, function(){ if( is_page(‘about') ){ nocache_header(); } });
PHPのセッション設定ではまる
さて、無事設定できたわけですが、なぜかすべてのページでCache-Control: nocache
が出力されていました。よくよく調べてみると、PHPってセッション使うとこのヘッダーを吐くみたいですね。なので、session.cache_limiter
をpublicに変更。無事ヘッダーも正常なものに変わりました。CF-Cache
っていうのがCloudFlareでのキャッシュステータスですね。
Gianismというソーシャルログイン用のプラグインでセッションを使っているので、それのせいでしょう。ここら辺、今後は考えないとですねー。
ベンチを取ってみる
Apache benchで比較してみました。/aboutも/about/wp-pluginsも対してサイズは変わらない(60kbぐらい)ので、対照実験として良しとします。
# about/wp-plugins(キャッシュあり)を検証 ab -n 1000 -c 100 https://takahashifumiki.com/about/wp-plugins/ -> Requests per second: 116.63 [#/sec] (mean) # about(キャッシュなし)を検証 ab -n 1000 -c 100 https://takahashifumiki.com/about/ -> Requests per second: 22.40 [#/sec] (mean)
結果、/about/wp-plusins(CFキャッシュあり)では秒間116.63リクエスト、/about(CFキャッシュなし)では秒間22.40リクエストとなりました。約5倍ですね。元々のスコアが悪すぎる気もしますが……
とにかく、このPage rulesを使えばさくらのレンタルサーバとかしょぼい共有サーバでもかなりの負荷をかけられることになります。
nocache_headers
みたいな機能はWordPressに限らずどんなアプリケーションでも簡単に実装できるので、とりあえずサイト全体にPage rulesを適用してしまい、管理画面とかログインとか、リクエストがPOSTのときとか、そういうときだけキャッシュさせないようにすると楽チンですね。
というわけで、次回バズったときは耐えられるようがんばります。終わり。
[429] [429] Client error: `POST https://webservices.amazon.co.jp/paapi5/getitems` resulted in a `429 Too Many Requests` response: {"__type":"com.amazon.paapi5#TooManyRequestsException","Errors":[{"Code":"TooManyRequests","Message":"The request was de (truncated...)