Varnish入門と仕組み

Varnish入門と仕組み

## Agenda

1. Varnishとは
2. 仕組み
3. 入門
4. ベンチマーク
5. Reference

## Varnishとは

* 2005年に作成
* ライセンス BSD
* cache機能を持つリバースプロキシとして知られるOSSのHTTPアクセラレータの一つ(Squid cache等)

### プロキシについて

* フォワードプロキシ
  * Clientの前段に配置して不特定多数のサイトに代理でアクセスしにいくこと
* リバースプロキシ
  * Serverの前段に配置して不特定多数のクライアントからアクセスしてきたのを代理レスポンス返す

* WHY プロキシ?
	* 負荷分散
	* セキュリティ向上
	* コンテンツキャッシュによる応答の高速化

### どこで使われてる?

* Fastly

https://www.fastly.com/blog/benefits-of-using-varnish

* Cookpad

https://speakerdeck.com/mirakui/high-performance-rails-long-edition?slide=19


* Hatena

大規模サービス技術入門より

* 某アイドルの投票

過負荷に耐えるWebの作り方より

## 仕組み

### Varnishのキャッシュについて

* Varnishはキャッシュサーバであり、OSの性能を重複なく使うように設計されている。
* フロントエンドにVarnishを導入して静的なものをキャッシュ化、動的な処理が必要になる要求はNginxを通じてバックエンドのプログラムに投げられるといった使い方が多い。
* httpでのリクエストが前提のため、バックエンド側で利用されることもある。
* nginx proxy_cacheで同一 URLへ多数のリクエストが来ている状況でキャッシュのライフタイムが切れると、バックエンドへ同じ URL に対して複数のリクエストが飛んでしまい、アプリケーション側に負荷がかかるという現象の対策

```
(https)- Nginx -(http)- Varnish Cache - Application - DB
```

### Varnishのシンプルさ

* ソフトウェアの動作が重くなる原因の一つに、カーネルが実装している機能をソフトウェアがさらに実装するというものがある。例えばディスクキャッシュやメモリキャッシュはカーネルが機能を実装している。
* 通常は一般のソフトウェアがそういった機能を実装するのは重複になる。
* Varnishは、こういった重複がなくカーネルディベロッパーの知識を活用した実装となっている。
* そのため、ディスクへのデータの書き込みをデフォルトでは実施しない。
* 再起動したタイミングでCacheはクリアされる。


## 入門

### Varnishの特徴

* オブジェクトは、（デフォルトでは）mmap によるディスク上のファイルに保存される。また、プロセスの再起動でキャッシュはすべて失われる
* 基本的な設定（Listenするポート番号など）はコマンドラインオプションで与え、プロキシとしてのルールは設定ファイル（VCL）に記述する
* 単体ではログをファイルに書き出す機能を備えておらず、共有メモリ上に書き出す

### doc

https://www.varnish-cache.org/docs/4.1/


### CLI

```
$ brew install varnish
```

* varnishd
	* リバースプロキシ本体
* varnishadm
	* 管理ツール
* varnishlog
	* ロギング
* varnishncsa
	* ロギング
* varnishstat
	* 統計情報を表示
* varnishtop
	* real-time情報を表示

#### varnishlog

Varnishは、マネージメントインスタンスと、チャイルドインスタンスから出来ている。varnishadminはvarnishを操作可能になる。(Banの発行も可能) varnishlogは現在のリクエストの状態を見る事ができる。どのリクエストでバックエンドに行ったかを見る事ができる

* varnishlogの例

```
$ varnishlog -b -o -i TxURL
   14 BackendOpen  b default 127.0.0.1 60939 127.0.0.1 80
   14 TxURL        b /index.html
   14 BackendReuse b default
   14 TxURL        b /test.html
   14 BackendReuse b default
```

#### varnishstat

varnishのヘルスチェックができるvarnishstatは下記の通り。詳細の見方はGettingStaredの記事を参照。

```
$ varnishstat

Hitrate ratio:        6        6        6
Hitrate avg:     0.7500   0.7500   0.7500

          36         0.00         0.01 client_conn - Client connections accepted
          36         0.00         0.01 client_req - Client requests received
          24         0.00         0.00 cache_hit - Cache hits
          12         0.00         0.00 cache_miss - Cache misses
           3         0.00         0.00 backend_conn - Backend conn. success
           9         0.00         0.00 backend_reuse - Backend conn. reuses
           2         0.00         0.00 backend_toolate - Backend conn. was closed
          12         0.00         0.00 backend_recycle - Backend conn. recycles
          12         0.00         0.00 fetch_length - Fetch with Length
          10          .            .   n_sess_mem - N struct sess_mem
           4          .            .   n_object - N struct object
           6          .            .   n_objectcore - N struct objectcore
           6          .            .   n_objecthead - N struct objecthead
           
```



### Varnish起動

```
$ varnished -f /etc/varnish/default.vcl -s malloc,1G -T 127.0.0.1:6082 -a 0.0.0.0:80
```

* -f 設定ファイル
* -s キャッシュのストレージタイプ
* -T 管理インターフェース(外からでもアクセス出来るようになる)
* -a 受付指定

```
# /etc/default/varnish
# service varnish restart

DAEMON_OPTS="-a :80 \
             -T localhost:6082 \
             -f /etc/varnish/default.vcl \
             -S /etc/varnish/secret \
             -s malloc,256m"
```

### Varnishのリクエスト処理フロー

0. クライアントからVarnishにHTTPリクエストが来る
1. リクエストの内容から判断してどういった振り分けをするか初期処理をする(vcl_recv)
2. キャッシュの有無を確認する(vcl_hash)
3. キャッシュがない場合にバックエンドに問い合わせて応答を待つ(vcl_miss)
	4. バックエンドからの応答が返ってきた際に、そのコンテンツをキャッシュしてよいかどうかを判断する(vcl_fetch)
5. クライアントにコンテンツを返す(vcl_deliver)

#### ざっくりとした処理フロー

![Kobito.GQ2agX.png](https://qiita-image-store.s3.amazonaws.com/0/18843/2d510cb3-aeb2-7d59-ebc1-073e09c2645b.png "Kobito.GQ2agX.png")


### VCL(Varnish Configuration Language)

Varnishの設定はVCLという独自言語で記述する

[VCL Reference](https://www.varnish-cache.org/docs/trunk/reference/vcl.html)

* 次のフロー(VCL request flow)を参考にするとよい
  
[VCL Basics - The Varnish Book](https://www.varnish-software.com/static/book/VCL_Basics.html)


* BackendとなるApplicationの設定

```
/etc/varnish/default.vcl

vcl 4.0;

backend default {
    .host = "www.varnish-cache.org";
    .port = "80";
}
```

* Varnishはデフォルトの設定ではクッキーが設定されているコンテンツに関してはcacheしない。
* 明示的にcacheするPathを記載するとcacheの対象になる

```
sub vcl_recv {
	if (req.url ~ "^/パターン") {
		unset req.http.Cookie;
	}
}
```

* 全部Cacheさせて明示的にApplication側で`HTTP_HEADER "X-VAENISH-CACHE: PASS"`を入れることで動的コンテンツのみCacheしないようにすることも出来る

```
sub vcl_fetch {
	if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*"){
		set beresp.ttl = 120 s;
		return (hit_for_pass);
	}
	if (beresp.http.X-VARNISH-CACHE == "PASS") {
		set beresp.ttl = 0 s;
		remove beresp.http.X-VERNISH-CACHE;
		return (hit_for_pass);
	}
	return (deliver);
}
```

* その他にもHTTPのMethodでifを使うことも出来る
* returnで返却しているのが、次の状態(pipe/pass/lookup)である。
* returnで返すのは-1等の値ではなく、次の状態への状態遷移を記述する。
* ループは書けない
* if文を書く事ができる。
* ifの中身は、次のような比較や正規表現もサポートされている
  * このif文の分岐によって、クライアントから送られてきたHTTPヘッダ、デバイス種類、Methodを用いで分岐し、キャッシュをさけたり、キャッシュのタイムアウトを調整したり、BANにしたり等の様々の操作を行うことができる。


```
sub vcl_recv {
    if (req.restarts == 0) {
        if (req.http.x-forwarded-for) {
            set req.http.X-Forwarded-For =
                req.http.X-Forwarded-For + ", " + client.ip;
        } else {
            set req.http.X-Forwarded-For = client.ip;
        }
    }
    if (req.request != "GET" &&
      req.request != "HEAD" &&
      req.request != "PUT" &&
      req.request != "POST" &&
      req.request != "TRACE" &&
      req.request != "OPTIONS" &&
      req.request != "DELETE") {
        /* Non-RFC2616 or CONNECT which is weird. */
        return (pipe);
    }
    if (req.request != "GET" && req.request != "HEAD") {
        /* We only deal with GET and HEAD by default */
        return (pass);
    }
    if (req.http.Authorization || req.http.Cookie) {
        /* Not cacheable by default */
        return (pass);
    }
    return (lookup);
}
```

### VCLで使えるfunction 

VCLで使えるfunctionは次の通り

0. regsub(str, regex, sub)
0. regsuball(str, regex, sub)
0. ban_url(regex)
0. ban(expression)
0. purge;
0. return(restart)
0. return()
0. hash_data()

* regsub/regsuballがURLの書き換え系
* ban_url, ban, purgeがキャッシュのクリア系
* return 次の状態遷移の為のfuntion. hash_dataはhash inputに文字を足す為のもの.

## Varnishとプロセス

* Varnishは大きくわけると、Managementプロセスと、Child/cacheプロセスに分かれる。
* Child/cacheプロセスがキャッシュを行い、Managementプロセスがそれを管理する
  * varnishadmコマンドで管理コマンドを発行できる。
  * 反映は、Varnishを再起動する事無く行える。（尚、varnishadmはリモートからでも操作できるように設定可能）

アーキテクチャの図は次のURLのProcess Architectureの項を参考

[Tuning The Varnish Book](https://www.varnish-software.com/static/book/Tuning.html)


## キャッシュクリアの方法

[Cash Invalidation](https://www.varnish-software.com/static/book/Cache_invalidation.html)

Varnishのキャッシュをクリアする方法は次の4つがある

1. ttlの設定時限設定
2. purge オブジェクト１つに対するキャッシュクリア
3. ban 正規表現を使ったキャッシュクリアの予約(SmartBan)
4. set req_hash_always_miss = true あるURLに対するキャッシュのクリア
5. purge method(varnish v4から)

* 一括でキャッシュをクリアできるのは、SmartBanのみ
  * それ以外だと、ttlの時限設定(デフォルトでは、キャッシュがクリアされるのが2分の時限設定)

### SmartBan方式

 BANの方式はキャッシュをすぐにクリアするのではなく、対象のキャッシュをBANにしておき、次にそのURLがリクエストされた際に、バックエンドのサーバーに読みにいくといった挙動をする。
  
### SmartBanの設定とテスト

BANというMethodのリクエストが送られてきた時に、/subディレクトリ以下の全てのhtmlと、/test.htmlのキャッシュをbanに入れる、つまり次回アクセスされたときに、バックエンドに取得するような設定になっている。

```
sub vcl_recv {
if (req.request == "BAN") {
  ban("req.url ~ ^/sub/.*.html");
  ban("req.url ~ ^/test.html");
  error 200 "Banned.";
}
```

### キャッシュの破棄のコマンド

キャッシュの廃棄はdefault.vclで設定可能。上記の書き方の場合、次のコマンドで、キャッシュが破棄される。（注：refresh.htmlは何でも良い）尚、banに関しては後で述べるvarnishadmで発行することも可能

```
$ curl -X BAN http://127.0.0.1:6081/refresh.html
```

### ttlのデフォルト値の変更

キャッシュのデフォルト値の変更は、/etc/sysconfig/varnishの次の項目を設定すれば変更できる

```
# Default TTL used when the backend does not specify one
VARNISH_TTL=120
```

### キャッシュ対象によるタイムアウトの変更

キャッシュの時間をコンテンツによって変化させたい場合は、default.vclを設定する。

[Varnish Book:VCL Basics](https://www.varnish-software.com/static/book/VCL_Basics.html)

* 次の例はjpgファイルを強制的に60sでタイムアウトさせる

```
sub vcl_fetch {
        if (req.url ~ "\.jpg$") {
                set beresp.ttl = 60s;
        }
}
```

### purge method(varnish v4)

[Purging and banning](https://www.varnish-cache.org/docs/trunk/users-guide/purging.html)

v4から purge METHOD追加されてたようです！

```
acl purge {
        "localhost";
        "192.168.55.0"/24;
}

sub vcl_recv {
        # allow PURGE from localhost and 192.168.55...

        if (req.method == "PURGE") {
                if (!client.ip ~ purge) {
                        return(synth(405,"Not allowed."));
                }
                return (purge);
        }
}
```

## サイジング

* Varnishは、キャッシュするオブジェクトにそれぞれ1kbのオーバーヘッドがかかる。
* スレッド数等の他のパラメータも掲載されている。

[Tuning The Varnish Book](https://www.varnish-software.com/static/book/Tuning.html)



## ログについて

* ログファイルはデフォルトでは書かれない。

次のURLのLog dataを参照

[Getting started - The Varnish Book](https://www.varnish-software.com/static/book/Getting_started.html)



## ベンチマーク

Golang製のHTTP load testing tool

https://github.com/tsenart/vegeta

![Kobito.mtvTGg.png](https://qiita-image-store.s3.amazonaws.com/0/18843/16f78847-45a9-0770-c976-46f5246b5748.png "Kobito.mtvTGg.png")


```
brew install vegeta
```

* localhostへ秒間100リクエストリクエストを5秒間行う

```
$ echo "GET http://localhost:3000/" | ./vegeta attack -rate=100 -duration=5s | ./vegeta report
```

簡単に試せます

## Reference

* 過負荷に耐えるWebの作り方
* サーバ/インフラを支える技術
* 大規模サービス技術入門
* [The benefits of using Varnish](https://www.fastly.com/blog/benefits-of-using-varnish)
* [High Performance Rails long edition](https://speakerdeck.com/mirakui/high-performance-rails-long-edition)

### Varnish本番運用に向けて
 
* [Varnishの多段](http://blog.xcir.net/?p=1751)
* [Rails and Varnish](https://www.slatestudio.com/blog/p/caching-in-rails-4-applications)
* [Rails and Varnish2](http://ghost.thekindof.me/using-varnish-v4-as-a-reverse-proxy-cache-with-rails/)
* [Varnish gem](https://github.com/russ/lacquer)

### キャッシュの無効化の記事

[Varnish Book: Cache invalidation](https://www.varnish-software.com/static/book/Cache_invalidation.html)
[Purging and banning](https://www.varnish-cache.org/docs/3.0/tutorial/purging.html)
[Bans and purges in Varnish](https://www.varnish-software.com/blog/bans-and-purges-varnish-30)

### Varnishの全体像 

[Getting started](https://www.varnish-software.com/static/book/Getting_started.html)

### Varnish Command Line Interface

[Varnish Book:VCL Basics](https://www.varnish-software.com/static/book/VCL_Basics.html)
[Varnish CLI](https://www.varnish-cache.org/docs/trunk/reference/varnish-cli.html)

### Varnishの設定

[Varnish Book: Tuning](https://www.varnish-software.com/static/book/Tuning.html)
[Sizing your cache](https://www.varnish-cache.org/docs/3.0/tutorial/sizing_your_cache.html)
[varnishd](https://www.varnish-cache.org/docs/3.0/reference/varnishd.html)