Nginxのproxy cacheからfastcgi cacheに移行

fastcgi_cache

今までNginxのキャッシュ機能はproxy cacheを使っていましたが、特にバックエンドをapacheにしているというわけでもないので今回fastcgi cacheに移行しました。こちらの方がシンプル設定でいけるのでおすすめです。

fastcgi cache

proxy cacheでは80番ポートをフロントエンドのキャッシュ用サーバとして静的なファイルを処理させて、8080番ポートをバックエンドサーバとしてPHPの動的な処理をさせていました。
これでも特に問題はありませんが、通常proxy cacheはバックエンドをapacheにして、静的なファイル+キャッシュをnginxに任せるときに利用することが多いようです。
またproxy cacheは2サーバ構成となるためnginxの設定を2server分書く必要がありその分手間がかかります。
そこで今回は1server構成ながらもphpの動的なレスポンスをキャッシュすることができるfastcgi cacheに移行しました。

こちらの記事を参考に設定しました。
Nginx + WordPress + fastcgi_cache with conditional purging
この記事を参考に+今までの設定をミックスして下記のような設定にしました。

Nginx設定

まずnginx.confのhttp部分にfastcgi_cache設定を追加します。

nginx.conf

http {
    
    ## 中略 ##
    
    ##
    # Fastcgi_cache Settings
    ##
    fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:4m inactive=7d max_size=50m;
    fastcgi_cache_key "$scheme$request_method$host$request_uri";
    fastcgi_cache_use_stale error timeout invalid_header http_500;
    
    ## 中略 ##
}

fastcgi_cache_pathではproxy_cacheと同じようにキャッシュ保存場所、キャッシュ階層、確保するメモリ領域など設定します。
上記の記事によるとキャッシュ保存場所は/var/runがいいようです。
このサイトはt1.microのサーバでも余裕で動いているのでメモリ領域も小さくしています。
fastcgi_cache_keyではキャッシュ毎に割り振られるキーを設定します。
この場合キャッシュファイルの中身を見ると、

KEY: httpGETtactosh.com/tag/objective-c/

のようになっていました。
fastcgi_cache_staleではerrorやtimeoutなどサーバーエラーのときでもキャッシュを返すようにさせるみたいです。

次に各vhostの設定を変更します。このブログの場合は/etc/nginx/sites-available/defaultに設定しています。

sites-available/default

server {
	listen   80;
	server_name tactosh.com;
	root /home/ubuntu/tactosh.com;
	index index.php index.html index.htm
	charset utf-8;

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	set $do_not_cache 0;

	# POST requests and urls with a query string should always go to PHP
	if ($request_method = POST) {
		set $do_not_cache 1;
	}

	# Don't cache uris containing the following segments
	if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
		set $do_not_cache 1;
	}

	# Don't use the cache for logged in users or recent commenters
	if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
		set $do_not_cache 1;
	}

	location / {
		try_files $uri $uri/ /index.php?$args;
	}

	location ~ .php$ {
		try_files $uri /index.php;
		fastcgi_pass unix:/var/run/php5-fpm.sock;
		fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
		include fastcgi_params;

		fastcgi_cache_bypass $do_not_cache;
		fastcgi_no_cache $do_not_cache;

		fastcgi_cache WORDPRESS;
		fastcgi_cache_valid  200 1d;
		fastcgi_cache_valid  any 10d;
	}

	location ~ /purge(/.*) {
		fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
	}

	location ~ .*\.(html?|jpe?g|gif|png|css|js|ico|woff) {
		access_log off;
		expires 10d;
	}

	location = /robots.txt { access_log off; log_not_found off; }

	location ~ /\. { deny  all; access_log off; log_not_found off; }
}

fastcgi_cache_bypass, fastcgi_no_cacheの値を$do_not_cacheという変数にして値を切り替えることで、キャッシュする場合としない場合を細かく設定できます。
今までパーマリンクに対応させるためにapacheのmod_rewriteのようにnginxのrewriteを設定していましたが、try_filesを

location / {
	try_files $uri $uri/ /index.php?$args;
}

のようにして代替できるみたいです。
上記の記事の設定では動かなかったので

fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;

を追加したところ動くようになりました。

location ~ /purge(/.*) {
	fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
}

ではngx_cache_purge moduleを利用してキャッシュを記事単位で削除できるようにしています。

Chefのテンプレートにもしていますのでこちらも参考にしてみてください。
vagrant-chef-ubuntu-nginx-wordpress

まとめ

Apacheと併用している場合はfastcgi_cacheは使えませんが、Nginxだけでサーバー構築しているときは,proxy_cacheよりもfastcgi_cacheの方が設定が簡潔になって良いと思いました。パフォーマンスで言うと、abコマンドを適当に打って確認してみた感じだとproxy_cacheもfastcgi_cacheもそれほど大差はありませんでしたが、若干fastcgi_cacheの方がTransfer rateが高めに出たので、できるならfastcgi_cacheにした方がいいのではないでしょうか。