Help us understand the problem. What is going on with this article?

varnish4でrestAPIのキャッシュをやってみる

More than 3 years have passed since last update.

nginxで動くrestAPI用のリバースプロキシとしてvarnishを試したときのメモです。
「とりあえず動けば、、、」という感じなので各設定の解説とかはないです。
(いろんな詳しい記事を参考にさせて頂いたのですが、元ネタのURLをメモっておらず。。)

環境

  • nginx, varnishの相乗り
  • varnishは4.0.3を使用

コマンド等メモ

起動・停止・再起動

$ service varnish start|stop|restart

ログ

  • デフォルトではファイルに出力されないので、コマンドで確認
  • 運用時はファイルに出すこと
$ varnishlog
  • コマンドのオプションでフィルタリングできるので色々調べると便利

管理コンソール

ほとんど使わなかった。。。

$ varnishadm

設定ファイルの構文チェック

$ varnishd -C -f /etc/varnish/default.vcl

キャッシュ

  • デフォルトでキャッシュするステータスコード
200: OK
203: Non-Authoritative Information
300: Multiple Choices
301: Moved Permanently
302: Moved Temporarily
307: Temporary Redirect
410: Gone
404: Not Found

プロキシ設定

varnishとnginxの相乗り想定なので、ポートを変える程度。
varnishは80ポートでリクエストを受けて、nginxは8080でリクエストを受ける。

varnish設定変更

  • varnishのLISTEN PORT変更
$ sudo vi /etc/sysconfig/varnish

VARNISH_LISTEN_PORT=80
  • バックエンド設定変更
$ sudo vim /etc/varnish/default.vcl

backend default {
  .host = "127.0.0.1";
  .port = "8080";
}

nginx設定変更

$ sudo vi /etc/nginx/conf.d/hoge.conf

upstream backend {
  ip_hash;
  server 127.0.0.1:8080;
}

…

server {
  …

  listen      8080;

Varnish4.0.3

インストール

  • 基本的には下記の手順でOK
$ wget http://dl.fedoraproject.org/pub/epel/6/x86_64/jemalloc-3.6.0-1.el6.x86_64.rpm
$ rpm -ivh jemalloc-3.6.0-1.el6.x86_64.rpm

$ rpm --nosignature -i https://repo.varnish-cache.org/redhat/varnish-4.0.el6.rpm
$ yum install -y varnish

EC2では、デフォルトのリポジトリをdisableする必要があった。
(そのままでもいけるという説もあり、別作業の変更がダメだったのかもしれません)

$ yum --enablerepo=varnish-4.0 --disablerepo=amzn-main,amzn-updates install varnish

VCL設定

varnishの挙動を設定するファイル。
C言語でいろいろ書ける。

デフォルトの/etc/varnish/default.vclを編集して、主に下記2点の対応を行った。

  • URLのクエリパラメータ正規化
    • http://api/books?a=hoge&b=mogehttp://api/books?b=moge&a=hogeは同じ内容キャッシュしないとダメだが、そのままでは別のキャッシュになってしまう
    • 4.X系ではboltsortがstd(標準モジュール)に組み込まれた
  • キャッシュの削除については、URLの正規表現でまとめて削除できるsmart banを使用
    • BANメソッドでリクエストが来た時に、そのURL(クエリパラメータを除く)をキーとするキャッシュを削除する使い方を想定

設定サンプル

※元々のメモから削除した箇所があるので、そのままでは動かない可能性があります。。

vcl 4.0;
import std;

# This is a basic VCL configuration file for varnish.  See the vcl(7)
# man page for details on VCL syntax and semantics.
#
# Default backend definition.  Set this to point to your content
# server.
#
backend default {
  .host = "127.0.0.1";
  .port = "8080";
}

sub vcl_recv {
    # キャッシュ削除
    if (req.method == "BAN") {
        ban("obj.http.X-URL ~ ^" + req.url + "(\?.*)?$");
        return (synth(200, "Banned."));
    }

    # 問答無用でクッキー削除(クッキーがセットされていてもキャッシュされてるっぽいので不要?)
    #if (req.http.Cookie) {
    #    unset req.http.Cookie;
    #}

    # GETリクエストならキャッシュ
    if (req.method == "GET") {
        return(hash);
    } else {
        return(pass);
    }
}

sub vcl_hash {
    # urlをハッシュキーにする前に正規化する
    set req.http.X-Cache-Key = req.url;

    # boolパラメータを0, 1に統一する
    set req.http.X-Cache-Key = regsuball(req.http.X-Cache-Key, "(?i)(\?|&)([^=]+)=true", "\1\2=1");
    set req.http.X-Cache-Key = regsuball(req.http.X-Cache-Key, "(?i)(\?|&)([^=]+)=false", "\1\2=0");

    # URIの末尾に"/"がついていた場合は除外する
    set req.http.X-Cache-Key = regsuball(req.http.X-Cache-Key, "\/\?", "?");
    set req.http.X-Cache-Key = regsuball(req.http.X-Cache-Key, "\/$", "");

    # クエリパラメータのソート
    set req.http.X-Cache-Key = std.querysort(req.http.X-Cache-Key);

    hash_data(req.http.X-Cache-Key);
    return (lookup);
}

sub vcl_backend_response {
    # キャッシュ対象外の処理
    if (
        bereq.url ~ "^\/$" ||
        bereq.url ~ "^\/index\.(html|php)" ||
        bereq.url ~ "^\/phpinfo.php" ||
        bereq.url ~ "^\/ocp.php"
    ) {
        set beresp.uncacheable = true;
        return (deliver);
    }

    # バックエンドからのレスポンスが200系でなければTTLを短くする
    if (beresp.status > 300 && beresp.status != 304) {
        set beresp.ttl = 10s;
    }

    # 圧縮されてない場合圧縮する
    set beresp.do_gzip = true;

    # デバッグ用にキャッシュキーをレスポンスヘッダにセットして返却する
    set beresp.http.X-Cache-Key = bereq.http.X-Cache-Key;
}

sub vcl_deliver {
    ## Varnishのキャッシュ情報をレスポンスヘッダーに付与
    if(!resp.http.X-Served-By) {
        set resp.http.X-Served-By = server.identity;
        if (obj.hits > 0) {
            set resp.http.X-Cache = "HIT";
        }
        else {
            set resp.http.X-Cache = "MISS";
        }
     // キャッシュのヒット回数
        set resp.http.X-Cache-Hits = obj.hits;
    }
    else {
        # append current data
        set resp.http.X-Served-By = regsub(resp.http.X-Served-By, "$", ", ");
        set resp.http.X-Served-By = regsub(resp.http.X-Served-By, "$", server.identity);
        if (obj.hits > 0) {
            set resp.http.X-Cache = regsub(resp.http.X-Cache, "$", ", HIT");
        }
        else {
            set resp.http.X-Cache = regsub(resp.http.X-Cache, "$" , ", MISS");
        }
        set resp.http.X-Cache-Hits = regsub(resp.http.X-Cache-Hits, "$", ", ");
        set resp.http.X-Cache-Hits = regsub(resp.http.X-Cache-Hits, "$", obj.hits);
    }

    ## remove Varnish header
    unset resp.http.Via;
    unset resp.http.X-Varnish;
    unset resp.http.X-Served-by;
    unset resp.http.X-URL;
}
teratsyk
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away