varnish&nginx
varnishとnginxを使っているサーバをマイグレーションすることになり、今まで外部のLBを通していたのを直接処理することになった。 そこで問題になるのはSSLの扱い。varnishはSSLを扱えないため(作者はopensslのコードはクソだ、と一刀両断している)、httpsのフロントエンドとしては使えない。 そこで順序を入れ替え、nginxをフロントエンドに、varnishを挟んでunicornに投げるようにした。
具体的には以下の通り。
旧構成:LB→varnish→nginx→unicorn
新構成:nginx→varnish→unicorn
varnishはUNIX socketに対応していないので(予定はあるようだが)、unicornを適当なポートで待ち受けさせてやらなければならない。 静的ファイルはフロントエンドのnginxで直接捌くためvarnishにキャッシュさせることはできなくなるが、nginx単体で充分なパフォーマンスが出るので考えなくていいだろう。
nginxを80で待ち受けさせて、静的ファイル以外をバックエンドのvarnishに投げ、varnishがさらにバックエンドのunicornに投げる。 これで問題なく動作しているかに見えたが、実は大きな罠があった。 varnishはGETリクエストしかキャッシュしないため、POSTについてはそのままバックエンドに丸投げになるはずが、間にnginxが挟まったことで全てのリクエストがGETになってしまう現象が発生した。 恐らくvclの記述がまずいのだと思うが、とりあえずnginxの方でGETリクエストのみをvarnishに投げ、それ以外は直接unicornに投げるように変更して解決した。 以下はその設定。
/etc/nginx/sites-available/default
upstream varnish {
server localhost:8080;
}
upstream unicorn-rails {
server localhost:3000;
}
server {
listen 80;
server_name rails-app-sample.larus.jp;
root /var/www/rails-app/current/public;
error_log /var/www/rails-app/current/log/nginx-error.log;
location ~ ^/assets|system/ {
expires 1y;
add_header Cache-Control public;
add_header Last-Modified "";
add_header ETag "";
}
location = /favicon.ico {
root /var/www/rails-app/current/public/assets;
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
if ($request_method = GET) {
proxy_pass http ://varnish;
# httpの後のスペースは本来は省く
}
proxy_pass http ://unicorn-rails;
# httpの後のスペースは本来は省く
}
}
/etc/varnish/default.vcl
backend rails_app {
.host = "127.0.0.1";
.port = "3000";
.connect_timeout = 30s;
}
sub vcl_recv {
if (req.http.host == "rails-app-sample.larus.jp") {
set req.backend = rails_app;
return (lookup);
}
}
なお、以後スケールする場合はnginxをロードバランサにしてvarnish+unicornの組を増やせば良い。
しかしvarnishの作者はDJBの同類っぽくてやや不安・・・