2月 102017
 

4.1系の最新版の4.1.5がリリースされました。 [changelog] [公式のリリース文]

公式でも触れられてる通りほぼバグフィックス(あとドキュメント周り)なのですが、1点パフォーマンスに影響する修正があります。
個人的に重要かなと思うのは#2106の修正です

バグ修正

workspaceが溢れた場合にpanicしていたのを修正 #1834

Basic認証でAuthorizationのフォーマットがおかしい場合varnishncsaでデコードできない問題の修正 #2148

VCLでバックエンドの比較が動いていなかったのを修正 #2168

VSMの使用状況のカウンタ(vsm_free)がおかしかったのを修正 #2188

ESIのincludeでパスが存在しない場合にpanicしたのを修正 #2197
「http://foo/」の場合は問題なかったのですが「http://foo」の場合は今までpanicしていました
その修正になります。

INT/REAL型において「-」をパースできなかったのを修正 #2167
要は-10とかを扱えるようになりました。

beresp.backend.ipが本来使用できないbackend_errorで呼び出すとpanicしたのを修正 #1865
backend_responseでのみ利用できるように修正されました

CNT_Requestでパニックしていのを修正 #2106

仕様変更

バックエンドへの接続にNagleアルゴリズムを使っていたのをやめました #2134
クライアントとの通信はもともとTCP_NODELAYだったのですがバックエンドとの通信はTCP_NODELAYを指定していませんでした。
キャッシュが出来ないオブジェクト(pass)の場合だと多少効いてくるのではと考えています。

改善

devicedetect.vclをアップデートしました(bot情報)


2月 092017
 

少し前にスロットルでサイトを守る(vmod-vsthrottle)で異常なリクエストに対してスロットルをかける方法を紹介しました。
しかしこれはあくまでリクエストレートのみで、そのリクエストがどれだけ帯域を使うかは制限しません。
例えば特定のリソースについては帯域を制限をかけたい場合や(動画のようにDL中でも再生できるのであれば、必要なビットレート+αでもいいかなとか)
ユーザの属性(課金状況など)や、はたまたリクエストが多いユーザに対しては制限をかけたい場合などいろいろあるかと思います。
今回は帯域制限を行うvmod-tcp(ドキュメント)を紹介します。

環境について

vmod-tcpでの帯域制限はtc-fqのSO_MAX_PACING_RATEを指定することによって実現しています。
これはカーネル3.13からの機能のためUbuntuであればTrusty以降が必要です、CentOSの場合はカーネルを新しくする必要があるかと思います。(7だと3.10なので)

インストール

これはvarnish-modulesの中に入ってる一つなので、インストールについては前回の記事を参照ください。

tc-fqに切り替え

先程も述べたとおりtc-fqの機能を利用しているためこれを使うようにします。
tcについてはこちらが詳しいので参照ください(よくわかるLinux帯域制限)

まず、現在のqdiscを確認します


xcir@gw01:~$ tc qdisc show dev eth0
qdisc pfifo_fast 0: root refcnt 2 bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1

eth0のところは環境によって変更してください(制限したいインタフェース)
今はpfifo_fastを利用していることがわかります。

次にfqに変更します


xcir@gw01:~$ sudo tc qdisc add dev eth0 root handle 1: fq
xcir@gw01:~$ tc qdisc show dev eth0
qdisc fq 1: root refcnt 2 limit 10000p flow_limit 100p buckets 1024 quantum 3028 initial_quantum 15140

qdiscがfqになりました。
これでvmod-tcpによる帯域制限が利用できます。

使い方

すごい簡単です。
例えばdatファイルに対して制限をかけたいのであれば


sub vcl_recv {
  if(req.url ~"\.dat(\?.*)?$"){
    // 1000KB/s(8Mbps)
    tcp.set_socket_pace(1000);
  }
}

とするだけです。

では、実際に動作確認をしてみましょう。


xcir@gw01:~$ curl http://*****:6081/10MB.dat  > /dev/null
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 10.0M  100 10.0M    0     0   944k      0  0:00:10  0:00:10 --:--:--  943k

xcir@gw01:~$ curl http://*****:6081/10MB.dat2  > /dev/null
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 10.0M  100 10.0M    0     0   260M      0 --:--:-- --:--:-- --:--:--  263M

10MBのファイルでやってみましたが、帯域制限に引っかかる方は943KiB/sなので大体8Mbpsでうまく制限されています。(10MB.dat)
対して引っかからない方では263MiB/sと制限されていないことがわかります。

注意事項

割りと簡単に使用できる帯域制限ですが注意する点がいくつかあります。

keep-alive時の制限の持続
この制限はtc-fqを利用してコネクション単位で行われます。
そのためkeep-aliveでコネクションを再利用した場合は当然のことながら制限は持続します。
なので例えば以下のように取得してみると


xcir@gw01:~$ wget http://*****:6081/10MB.dat  http://*****:6081/10MB.dat2
--2017-02-09 20:02:10--  http://*****:6081/10MB.dat
Connecting to *****:6081... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10485760 (10M) [application/x-ns-proxy-autoconfig]
Saving to: '10MB.dat'

100%[============================================================>] 10,485,760   943KB/s   in 11s

2017-02-09 20:02:20 (945 KB/s) - '10MB.dat' saved [10485760/10485760]

--2017-02-09 20:02:20--  http://*****:6081/10MB.dat2
Reusing existing connection to *****:6081.
HTTP request sent, awaiting response... 200 OK
Length: 10485760 (10M)
Saving to: '10MB.dat2'

100%[============================================================>] 10,485,760   942KB/s   in 11s

2017-02-09 20:02:31 (944 KB/s) - '10MB.dat2' saved [10485760/10485760]

FINISHED --2017-02-09 20:02:31--
Total wall clock time: 22s
Downloaded: 2 files, 20M in 22s (945 KB/s)
xcir@gw01:~$

制限がかからないはずの10MB.dat2についても制限がかかっているのがわかります。

なので、例えば先程の場合であればdatファイルをダウンロードした後(8Mbps)にコネクションを再利用して画像などのリソースをダウンロードする場合は8Mbpsとなります。
なにかリセットする方法がないかなと0を指定しても特に解除されなかったため、vcl_recvの先頭で十分な大きさの指定をしておくと良いと思います。


sub vcl_recv {
//8Gbps
tcp.set_socket_pace(1000000);
  if(req.url ~"\.dat(\?.*)?$"){
    // 1000KB/s(8Mbps)
    tcp.set_socket_pace(1000);
  }
}

このようにして試してみると


xcir@gw01:~$ wget http://*****:6081/10MB.dat  http://*****:6081/10MB.dat2
--2017-02-09 20:03:40--  http://*****:6081/10MB.dat
Connecting to *****:6081... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10485760 (10M) [application/x-ns-proxy-autoconfig]
Saving to: '10MB.dat'

100%[============================================================>] 10,485,760   943KB/s   in 11s

2017-02-09 20:03:51 (945 KB/s) - '10MB.dat' saved [10485760/10485760]

--2017-02-09 20:03:51--  http://*****:6081/10MB.dat2
Reusing existing connection to *****:6081.
HTTP request sent, awaiting response... 200 OK
Length: 10485760 (10M)
Saving to: '10MB.dat2'

100%[============================================================>] 10,485,760  47.7MB/s   in 0.2s

2017-02-09 20:03:51 (47.7 MB/s) - '10MB.dat2' saved [10485760/10485760]

FINISHED --2017-02-09 20:03:51--
Total wall clock time: 11s
Downloaded: 2 files, 20M in 11s (1.81 MB/s)

と、意図したどおりに動作します

TLS終端を別MWで行いlocal接続しているケースに効かない
Varnishはhttpsを喋れないのでTLS終端をnginxやhaproxyやhitchなどで行いローカルで接続しているケースもあると思いますが
その場合はloインタフェースもtc-fqを適用する必要があります。

http/2利用時
http/2は複数のストリームが1コネクションに入っています。
そのため特定のリソース(=特定のストリーム)のDL時のみを帯域制限をかけたい場合には使用できません。
この場合全体での制限になります。
もちろん、特定の行動をしたクライアントの帯域を絞りたいという用途には利用できます。


12月 042016
 

VarnishCacheはOSSですがPHKとVarnishSoftwareなどが主となって行っています。
個人や組織は当然ながら資金なしで活動できるわけではなく、VarnishSoftwareの場合はVarnishに多数の機能が追加されているVarnishPlusをサブスクリプション販売したいたりします。
ではPHKはどんな感じで資金を得ているかというとVarnish Moral License(VML)というので資金を得ています。
VMLで資金を得てその範囲でPHKが様々な新機能や改修を行っていたりするのですが、少し前のVarnish5の開発で資金が足りなくなってきました。
そのためVarnishの開発時間を取るためにVML購入を求める記事が公式にあがっていましたが
(Send us (more) money!)
今回、自分が実際にVMLを500EURほど購入してみたので、どんな感じのフローでやったのかなどを紹介します。

そもそもVMLとは

先程も述べたとおりPHKがVarnish関連の活動をするための資金です。
VMLのページに書いてあるとおりなのですが

Software development may be open, and the result shared with an Open Source Software licence, but the actual hours of programming are not gratis.
Just like everybody else, I need money for mortgage, kids and food, money I make by doing things with computers, for people who are willing to pay for that.
One of the things I do for money, is develop Varnish, and the Varnish Moral License is the vehicle I invented for the paperwork.

(意訳)ソフトウェアの開発はオープンで、その成果物はOSSとして共有されますが、その開発に費やされる時間は無償ではありません。皆さんと同じように、私は住宅ローンを、子供を養う、食べていくためにコンピュータを使って稼ぐ必要があります。私が稼ぐ一つの手段がVarnishの開発で、VMLはそのために開発した仕組みです。

ちなみに、現在VMLを継続的に購入しているユーザは

  • Fastly
  • VarnishSoftware
  • UPLEX

の3社です。

ちなにみVMLを購入することで何かしらのサービスやサポートが受けられたりするわけではありません。
(当然、欲しい機能をリクエストして優先されるわけでもありません)
支払った金額の分だけPHKがVarnishに時間を費やすことで開発が前に進む感じです。
VMLのFAQについてはここにあるのでぜひ目を通してみてください。

実際にVMLを購入したときの手順

購入希望のメールを送る
まずはPHK宛にメールを送ります。
メアドはここにあるものです。

含まれる必要がある内容は

  • 購入するラインセンス額(How much do you want to pay for your license ?)
  • それは定期購入(期ごと、月ごと)か1回のみか(Do you want to pay it monthly or quarterly ? (or onetime ?))
  • Webページに記載される名前(What name should appear on the web-page ?)
  • 請求書に記載される名前(What name should appear on the invoice ?)

です。
自分が実際に送ったメールはこんな感じです。
vml1
ついでにこの記事を書きたかったので書いてもいいかを聞いています。

ちなみに今回500EUR(6万円ぐらい)を送金しています。もっと少なくてもいいのでしょうか?
これは海外送金の難しいところで自分はそこまで詳しくないのですが、
海外送金の場合だと受け取るのにもお金が必要で、FAQにもあるのですが100EUR以下の場合は手数料のほうがかかってしまうらしいです。
また、当然ながらPHK側の事務コストが発生しますのでそのあたりを勘案して考えると良いかなと思っています。
(メールで「私が避けたいのは5ドルの請求書を200枚も書くような状態に陥ることだ」と言ってました)
もし、VMLは煩雑なので他の方法がないかといえばWishlistもあります。
実際、自分はVMLの海外送金がめんどくさくて何度かリリースがあったタイミングでWishlistを消化してました。
なぜ今回VMLを購入したかというと公式サイトにVMLについて書いてあったのと、Wishlistに買えるものがなかったからです。

請求書が来る
請求書はPDFで送られてきます。(自分の場合は1週間ぐらいできました)
vml2
マスクしてる箇所は住所とか電話番号が書いてあった感じです(一応公開してOKって確認とりました)

海外送金をする
最初なんでPaypalが使えないんだろうと思ったんですが
以前に資金集めを行った際に使っていろいろあったようです。
ということで銀行で海外送金をする必要があります。
海外送金のめんどくさいところが、500EURを送ろうとしても実際に500EURが相手に渡るかわからないところです。
中継銀行で手数料が引かれたりすることもあります。
海外送金できる銀行はたくさんあるのですが(ゆうちょでもできます)
中継手数料などを先に支払ってしまえる銀行を探したところ楽天や三井住友などがあったので今回は三井住友銀行を使いました。
vml3
これ書いた後に大変だったのが、銀行側で「æ」に対応するキーがなくてなんて入力していいかがわからなかったことですw
aeで書いて着金したのでこれで大丈夫みたいです。

たしか翌週に三井住友から明細書が自宅に送られてきました。
vml4

これで送金は完了です。

で、翌月ぐらいにVMLの明細のところに名前がのります。
vml5

まとめ

Varnish5がリリースされてhttp2に実験的なサポートをするなどしましたが、まだまだバギーで改善が必要です。
今回少額ながらVMLを購入しましたが少しでもその改善に役に立てば嬉しいですし、この記事を公開することでVarnishを使っている方でVMLの購入を検討してくれると嬉しいなと思います。


12月 022016
 

4.1系の最新版の4.1.4がリリースされました。 [changelog] [公式のリリース文]
主にバグ修正とドキュメントの改善ですが
1件すごくタイムリーなバグ修正が存在します。
また、今回のリリースで4.0.xがEOLになりました。

バグ修正

バックエンドの現在の接続数のカウンタが機能していないのを修正 #2011

ラウンドロビンDirector利用時にクラッシュすることがあるのを修正 #2024

フェッチ時にワークスペース・ストレージ確保失敗するとリークする可能性があったのを修正しました

return(pipe)時にstd.cache_req_bodyが機能しないのを修正 #1881

vmod_named利用時にVBE_Deleteでpanicすることがあるのを修正しました #2008
コードの修正を見た感じnamedに限らずに動的にバックエンド定義をUpdateしていくようなDirectorの場合に問題が起きそうです。

エラーなどで起動できなかった場合にvsmファイルが残っていたのを修正しました #2115

keepalive時に複数のrangeリクエストが来た場合に先頭リクエスト以外をしばらくブロックすることがあるのを修正しました #2035

バックエンド選択時に稀にpanicするのを修正しました #2027

時刻が逆行(STEP)した場合に子プロセスが再起動する場合があるのを修正しました #1874
うるう秒もありますのでこのあたり注意しておいたほうが良いと思います。
条件としてはセッションがクローズ(close)されるタイミングの時刻がセッション開始時刻(open)が早くなっていた場合です。
Varnishのこの時間の精度はナノ(clock_gettime/CLOCK_REALTIME)もしくはマイクロ秒(gettimeofday)なので
うるう秒で23:59:58->59->59->00のように59を繰り返す場合(STEP)でリクエスト自体を1秒未満で処理していると引っかかる可能性があります。
そのため、今回のうるう秒でSTEP動作をする可能性がある場合は強くアップデートを勧めます。
なお5.0.0も影響を受けます。今のところ5.0.1がリリースされる気配はないのでSLEWで動かすか独自で現時点のmasterのものをビルドするかをおすすめします。(4.0.xは影響なさそうです)
また、この問題の解決のためにclock_stepといパラメータが追加されています。

機能強化

varnishhistの表示にy軸が追加されました
5.0.0のhistと同じ表示です。

仕様変更・追加

varnishtestで外部のコマンドを実行するfeature cmdが追加されました。
すでにあるshellコマンドとの違いですが、shellの場合はその実行結果でテストが成功・失敗しますが
feature cmdの場合はテスト自体が実行されるかされないかです。
混在環境(OSやCPU)で特定環境のみのテストを行う際に便利に使えると思います。

varnishtestのresp.msgはresp.reasonに変更されました

clock_stepが追加されました
時刻同期で一気に時刻が変更(STEP)された場合の許容秒数です。
デフォルトは1秒です。

thread_pool_reserveが追加されました
重要なタスクのために予約されるスレッド数です。
例えば、クライアントの処理でスレッドを使い尽くした場合でもreserveされているスレッドがあればバックエンドの問い合わせが可能です。
この値は引き上げるとクライアント処理で使われるスレッド数が少なくなるため注意する必要があります。
あくまで一気にリクエストが来てスレッドが足りなくなった場合に発動するものなので、普段から適切なスレッド数を指定していればそこまで問題無いと思います。
デフォルトは0(auto)で、thread_pool_minの5%が自動的にセットされます。

パッケージ構成が変更されました
5.0の構成とおなじになりました。(varnish = varnish + libvarnishapi + varnish-doc)
そのためそのままapt-get installで更新しようとすると失敗します。
一度4.1.3までのバージョンをremoveしてアップデートするのをおすすめします

その他変更

ドキュメントの改善やRaceコンディションで起きる幾つかのバグが修正されています。


7月 132016
 

Varnish4.1.3がリリースされました。
バグフィックスが主でアップデートを強く進めますが、他にも結構使える新機能が増えています。

ダウンロードはこちら
changes

新機能・変更

varnishncsaにバックエンドに問い合わせにいったものだけを表示するオプションが追加されました(-b)
今までは幾つか絞込の指定をしないと取れなかったのですが-bだけで済むので便利です
また同時にクライアントで絞り込んで表示するオプション(-c)も追加されています。(デフォルト動作)
ちなみに-b -cを同時に指定することも出来ます。

VSMを開放する際にどれだけ待機するかのパラメータが追加されました(vsm_free_cooldown)
今までは60秒でハードコードされていました。デフォルト値も60秒です。
通常の使い方ではいじることはないと思いますが、かなり激しいログの書き込みをするとかの環境だといじる機会はあるかもしれないです。

varnishlogの出力でバックエンドのトランザクションが開始する際にBackendStartが入るようになりました
クライアントのReqStartと対になるものです。

varnishncsaの-Fで指定できるものが増えました
3つ増えました。
 Varnish:side
 先ほど紹介した-b -cオプションに伴って増えたものです。
 バックエンドで絞込をしている場合はbを、クライアントからの表示の場合はcを出力します。
 -b -cを両方指定している場合に使うとどちらのログなのかがわかって良いと思います。
 Varnish:vxid
 vxidを出力します
 VSL:tag VSL:tag[field]
 指定したタグ(TimeStampなど)を出力します。
 基本vsl-queryと同じ指定ですがヘッダ指定は出来ません(VSL:ReqHeader:User-Agentみたいなのは出来ない)
 これできるといいなーと思うのでp-r書こうかな・・
 ちなみに複数引っかかる場合は最初のを出力します。
 例えばVSL:TimeStampを指定した場合は毎回Startのタイムスタンプが表示されます

TCP Fast Openをサポートしました
デフォルトはoffです(tcp_fastopen)

varnishtestに新しい同期用の命令を追加しました(barriers)
semaより使いやすいです

varnishstatで12桁以上の数値を出力する場合は丸めるようにしました#1855
CURRENTの表示でK/M/G/Tのように単位がつけられないカウンタは” %12ju”だったのですが12桁を超える場合は1000で割って” %9ju…”という表示になります。
コードを見た限りではxml/jsonで出力するときには影響しないのでそこまで気にすることはないかなと思います。

バグ修正

今回はかなりバグ修正が多いので幾つかピックアップして紹介します。
特にESI周りの修正が多いのでESIを使っている場合はアップデートすると良いと思います。

varnishncsaで-Lオプションが受け付けられないようになってたのを修正しました#1994

たまにAgeとAccept-Rangesヘッダが複数レスポンスされることがあるのを修正しました#1955

同名のVCLでvarnishをstop->startを繰り返すとその後segfaultを繰り返す事があったのを修正しました。#1933
dlopenのリファレンスカウンタが信用ならなかったみたいです。
vcl名は変わらないのですが、コンパイルしたvgc.soのパスに現在時刻(ナノ秒)が付与されるようになりました。
バッドアイデアとメッセージにあるので割と苦悩した感じがあります。

vcl_init/finiでstd.log/std.syslogを利用するとクラッシュするのを修正しました#1924

VSMのサイズが小さくてオーバーラン検出に問題が合ったのを修正しました#1873
varnishncsa等でオーバーランした際にもクラッシュしなくなりますが
オーバーランしたらvsmサイズを増やしたほうが良いでしょう。
それでも困るようであれば今回追加されたcooldownの調整も考えると良いかも

-Cオプション利用時にテンポラリで使用したディレクトリを消していなかったのを消すようにしました#1869

POSTリクエストをpipeで繋いだ場合に1分待たされることがあるのを修正しました#1806
4.1からはPOSTリクエストのデフォルトの動作がpassになっているので通常の場合は問題ないのですが
以前からの設定を使いまわしてる人などは割りとハマるかもしれないです。

バックエンドの接続数を示すカウンタを復活させました#1725
バックエンド周りのコード修正した際に意図せず消えたカウンタを復活させました。
確かに消えてました・・気づかなかった・・

次は5.0.0かなー
楽しみですね


3月 072016
 

Varnish4.1.2がリリースされました。
バグフィックスが主ですが、一部新機能があります。

ダウンロードはこちら
changes

また2016-03-09 12:00 CET(日本時間 同日20時)からリポジトリの場所が変更になります。
これは去年アナウンスされていたVarnish Cache project autumn cleaningの一環になります。
また、まだドキュメント側は更新されていませんがバグ報告についてもgithubに移行するはずです。

機能追加/改善

REAL型同士で算術演算子を使えるようになりました
4.1.1まではREAL型同士は+以外は使えなかったのですが新たに-*/がサポートされるようになりました。(1.1 – 0.9とかが出来るように)

absoluteURIでhttpsからはじまる場合でもHostをパース出来るようになりました
言葉にするより表のほうがわかりやすいと思いましたので動作パタンを表にしました。

  http://example.com/foo https://example.com/foo
Version req.http.host req.url req.http.host req.url
~4.1.1 example.com /foo https://example.com/foo
4.1.2~(with feature +https_scheme) example.com /foo example.com /foo

PROXYプロトコルに4.1で対応したしといった感じかと思います。
なお、標準ではoffになっているのでfeature +https_schemeを指定する必要があります。

VMODでACLが扱えるようになりました
vmodtool.pyを改善して4.0/4.1でソースツリーを共有出来るようにしました
おもにVMOD作成者向け

vmodtool.pyを改善して1つのディレクトリから複数のvmodを作成コンパイル出来るようにしました
varnish-modulesというのが少し前に公開された(Varnish謹製のVMOD集)のですが、これのための対応といった感じです

バグ修正(一部抜粋)

Content-LengthがないHTTP/1.0かつPOST/PUTした場合すぐ400を返すように変更(#1843)
今まではchunkedとして扱ってしまって結果としてtimeoutを待ってましたが400をすぐ返すように変更しました。

フェッチ時(VFP_Push)にworkspaceがオーバーフローした場合panicしていたのをFetchErrorに変更しました(#1739)
panicするほどのエラーではないということで変更です。

hit-for-pass利用時にbodyが空っぽになるケースがあったのを修正(#1858)
本来不要なIMSの評価を行っていたため結果としてbodyが空っぽになるケースがあったのでhit-for-pass時のIMSの評価を辞めました。

ESI利用時にメモリリークするケースがあったのを修正(#1860)
これはAddressSanitizer(ASAN)で検出したのです。(他にも#1852を検出しています)
ASANについては以下を見てみると面白いと思います
CAN WE RUN C CODE AND BE SAFE?

ban-listを回収(ban-lurker)してる際にpanicするケースがあるのを修正しました(#1863/#1864)


2月 032016
 

Varnish4.1.1がリリースされました。
ドキュメントの修正や、バグフィックスが主ですが幾つかの仕様変更があります。
4.1.0を利用している場合はアップデートをおすすめします。

ダウンロードはこちら
changes

機能追加

varnishncsaに-fオプションが追加されました
これは単純に-Fで指定するフォーマットをファイルから読み込みができるというだけです。
エスケープ等が面倒な時はよいかもしれないです。

仕様変更

varnishncsaはデーモンで動作する際に-wオプションが必須になりました
-wはファイルに出力するオプションで、通常デーモンで動作させる場合は指定しているものなので特に影響はないかと思います。

If-None-Match(INM)とIf-Modified-Since(IMS)リクエストが同時に来た場合IMSを無視します
この動作自体はRFC7232の3.3に記述されている通りです
(A recipient MUST ignore If-Modified-Since if the request contains an If-None-Match header field)

ステータスコードが1xx, 204, 304の場合はContent-Lengthヘッダをレスポンスしません
これらのステータスコードはbodyを含みませんので不要です。

VCLをネストしてincludeする際のカレントディレクトリをそれぞれのVCLのディレクトリに変更しました
いまいち一文で書くとわかりづらいのですが用はこういうことです。


/etc/varnish/default.vcl
/etc/varnish/common/base.vcl
/etc/varnish/common/acl/internal.vcl
/etc/varnish/common/acl/admin.vcl

例えばVCLをこのように配置していて相対パスでincludeする場合


~4.1.0
■/etc/varnish/default.vcl
vcl 4.0;
include "./common/base.vcl";

■/etc/varnish/common/base.vcl
vcl 4.0;
include "./common/acl/internal.vcl";
include "./common/acl/admin.vcl";

4.1.0までは基点となる/etc/varnish/を基準とした相対パスで書く必要がありましたが、
4.1.1からは


4.1.1~
■/etc/varnish/default.vcl
vcl 4.0;
include "./common/base.vcl";

■/etc/varnish/common/base.vcl
vcl 4.0;
include "./acl/internal.vcl";
include "./acl/admin.vcl";

このようにincludeが記述されたVCLのディレクトリを基準として読み込みます。
割と便利だと思います

hit-for-passもgrace動作するようになりました(#1818)

vcl_dir, vmod_dirにコロン区切りで複数のディレクトリを指定できます

varnishreplayが削除されました
少し前からコンパイルされないようになっていたのですがコード自体も削除されました。

バグ修正(一部抜粋)

大きなファイルを扱う際に遅くなっていたのを修正(#1798)

IPv6アドレスのパースに失敗するのを修正(#1801)

PROXYプロトコルを利用した際にvarnishlogが正しく表示されないケースがあったのを修正しました(#1804)
Proxyプロトコルのvxidが0になっていてその影響で-g request, sessionがうまく動きませんでした。

INMリクエストでETagを比較する際に弱い比較関数を使用するようにしました(#1816)
RFC7232に準拠した形です。

vsl-queryでフィールドをfloatsで評価しようとした場合に評価できないのを修正(#1845)
4.1.0からのデグレですが、具体的には”timestamp:resp[2] > 1.”のような絞り込みができなくなってました。


12月 052015
 

この記事はVarnish Cache Advent Calendar 2015の4日目の記事になります。

VCLには様々な変数の型がありまして、それぞれの型の間では暗黙的に変換されるものと、vmod_stdを使用して明示的に変換するものがあります。
型が結構多いのでいまいち何を使うか忘れることが多いので図にしてみました。
types
なお、ここでは特に変換にかかわらない型については取り上げていません(BLOB,ENUM,HTTP,PRIV_CALL,PRIV_VCL,PRIV_TASK,PRIV_TOP,PROBE,VOID)

ちなみに各型から暗黙的にSTRINGへ変換すると以下のようになります。

変換例
BYTES 2.000
DURATION 2.000
REAL 2.000
BOOL false
BACKEND default
INT 1
IP 192.168.1.1
TIME Fri, 04 Dec 2015 18:31:12 GMT
HEADER example.net

また、STRING_LISTですが基本的にはSTRINGと特に違いはないのですが、
これを引数として受け付ける関数の場合は型変換を意識しないとハマります。
例えばstd.logというログを出力する関数があるのですが、これは引数がSTRING_LISTです。
それを意識して以下のようなVCLを書くとエラーになります。


std.log(std.duration("10w",0s));


Command failed with error code 106
Message from VCC-compiler:
Wrong argument type.  Expected STRING_LIST.  Got DURATION.
('input' Line 31 Pos 31)
std.log(std.duration("10w",0s));
------------------------------#-
('input' Line 31 Pos 1) -- ('input' Line 31 Pos 30)
std.log(std.duration("10w",0s));
##############################--
Running VCC-compiler failed, exited with 2

これはdurationから一気にSTRING_LISTに暗黙的に変換できないからです。
そこでどうやるかというと、一度STRINGを経由させてあげればよいので


std.log("" + std.duration("10w",0s));

このように空文字を結合すると良いです。

まとめ
変数がどのように型変換が出きるかを知っておくと、その型では出来ない演算を変換した先でやって戻すみたいなことが出来ます。
覚えておくと結構便利なので、頭の片隅に置いておくと良いかなと思います。


12月 022015
 

この記事はセカイエ Advent Calendar 2015Varnish Cache Advent Calendar 2015の2日目の記事になります。
かなり前の話(8月)になるのですがリノコという定額リフォームサービスのサイトがTV東京のワールドビジネスサテライト(以下WBS)に取り上げられました
ありがたいことにたっぷり取り上げていただきました。その際に少しだけVarnishを使って負荷対策をお手伝いしたのでその時のことを書きます。
(なおここでとった対策は緊急ということで外しています。)

TV放映される上で負荷対策として検討したこと

  1. スケールアップ・アウト
  2. トップページ及びそこから回遊されるページの静的化
  3. Varnish導入

動き的には1を検討してみてその結果たりなさそうという判断を行い2,3を並列で行った感じです。

スケールアウト
リノコではクラウドを利用しているわけで当然スケールアウトを考えるわけですが
残念がらコード側があまりスケールするような仕組みになっておらず、例えば10倍サーバを投入してもスケールアップ・アウト出来ない箇所がボトルネックになって無駄になってしまう状態でした。
そのため最低限のスケールアップ・アウトを行いました 。

トップページ及びそこから回遊されるページの静的化
現地メンバーにお願いしましてコードを修正することでページの静的化することで、負荷に耐えるように修正を加えようとしました。
このアプローチ自体は間違っているとは考えていませんが残念がら切り戻しています。
単純にバグってしまったということです。敗因は明らかで単純に時間がなかったということにつきます。
今回のTV放送を知ったのが放映前日で、この作業をはじめたのが放映6時間ぐらい前だったからです。(そして切り戻し判断は放映2時間ぐらい前です)

Varnish導入
リノコのサイトでは幸いな事にキャッシュを行うことができる・出来ないの判断が比較的分かりやすかったため、試しに設定を書いてみて投入してみました。
この試みは割とうまく行ったためそのままTV放送を迎えました。

結果について
まずはどの程度のリクエストが来たかですが
image2015-11-29 16-43-13
割とビビるリクエストが着ているのがわかります。
でサイトが落ちたかどうかということなのですが半分落ちました。
この半分というのはVarnishでキャッシュが可能でキャッシュが出来たページについては特に問題なくレスポンスが出来て
キャッシュが不可能なページやキャッシュは可能だが深い階層で未キャッシュのページは落ちた感じです。
負荷対策を行う上で重要なのは、かけることが出きるコストを正確に把握し、何処の優先度を上げて何処を下げるかだと思っています。
特に今回のケースでは対策に費やされる時間がほぼなかったため、最小の対策で最大の成果を得る必要がありました。
今回はTV放映時にサイトを全部落とさない、特にLPとそこから回遊しそうなページについて落とさないを目標としました。
そういう意味では今回の対策はうまく行ったと考えています。

そもそもなぜVarnishを使ったか
どうせお前がVarnish好きだからじゃないかという指摘もあるかもしれませんが
今回のケースではVarnishを使う以外の選択肢はありませんでした。
もう少し時間があれば他の手段も取れましたがその場合でも良い選択肢だと確信しています。
『幸いな事にキャッシュを行うことができる・出来ないの判断が比較的分かりやすかった』
と書きましたがこのルールは以下でした

  • 特定のパス以下のURLはキャッシュ不可
  • クッキー中に特定キーが入っていた場合はキャッシュ不可
  • 同一URLでUAによってコンテンツの出し分けを行っている(PC/スマホ)
  • etc

例えば1URLで最大どの程度のパタンがあるかというと

  • キャッシュ可能+PC
  • キャッシュ可能+SP
  • キャッシュ不可+PC
  • キャッシュ不可+SP

と4パタンがありました。
まさにVarnishの設定言語であるVCLの得意とすることで、設定は47行でした。(実質コードですが)
しかもこの対策を行う上でサイト側のコードは修正しておりません。
このような場合において最低限の対策で効果を得るにはVarnishは最適だと考えています。

まとめ(負荷対策の重要性)
メディアに取り上げられるというのはいわばボーナスステージだと僕は考えています。
しかし、見に来ていただいた方が実際に興味を覚えてもらうためにはまずサイトを見れる状態を作らなければなりません。
普通に考えて初めてサイトを見に行って重くて見れなかった、そもそも見れなかったとかであれば、すぐに記憶から薄れ再訪することはないでしょう。
そのためサイトをなるだけ見れる状態にするのを考えるべきでしょう。

また、負荷のかかり方も様々です。

  • 事前にわかるかどうか
    • 事前連絡がある
    • 事前連絡がない(予測不能)
    • イベントなどでそもそもわかる(エイプリルフールやセール等)
  • 負荷の上がり方
    • 一瞬で上がってその後落ち着く(TV)
    • 一気に上がりつつその後もじわじわ上がる(ネットメディアなど)

どんなパタンがあるだろうとさくっと書いてみましたが(サイトの特性によってここは変わると思います)それぞれの組み合わせでどのような対策を行うか考えてみるのも良いと思います。
例えば事前連絡があるのであれば、インスタンスをもりもり多めにあげて(AWSであれば)ELBの暖気申請をして・・・みたいな対策も打てます。
逆に事前連絡がないのであれば、オートスケールするまでの時間までを如何に稼ぐかという話になってくるかもしれません。
更に一瞬で上がっていつ起こるか予測不能であればその場合でもどのコンポーネントを落とさないようにするかなどの事前の設計・対策が重要でしょう。
もちろん、どのパタンにおいてもスケールするようにコード修正するとかクラウドの機能を使う/業者の選定も大事です。
また、どのパタンでもキャッシュ可能なコンテンツをキャッシュすることは有効な手段の一つだと思います(Varnishでもなんでもいいですが)


12月 012015
 

この記事はVarnish Cache Advent Calendar 2015の1日目の記事になります。

何回か散発的に取り上げたことはあるのですがVSL-Queryの使い方についてまとまっては書いていなかったので書いてみます。
varnishlogのフォーマットの説明についてはv3の頃の記事ですがVarnishのログにアクセスしてみよう!を参考にしてください。

Varnishは他のミドルウェアでよくあるようにアクセスログをファイルに直接書き込むということはせずにリングバッファな共有メモリ(VSM)に出力します。
その際に出力されるログ(VSL)は非常に多岐に渡ります

  • ヘッダ(クライアントからのリクエストヘッダやバックエンドからのレスポンスヘッダなど)
  • ヘッダへの操作
  • どのアクション(vcl_recvなど)が呼ばれて何をリターン(hashなど)したか
  • std.logで出力した任意のログ
  • などなど

標準ではoffにされていますがonにすることでVCL中のどこのブロックを通過したかもトレースすることも可能です。
varnishncsaやvarnishlogなどの各種ツールはこのログを読んで出力します。
vsl
もちろんそのまま使うことも多いのですが、例えばエラーがでた・遅いリクエストだけを抽出したいということがあります。
その際に利用するのがVSL-Queryというもので、要はVSLに対して絞り込みを行う式を作成できます。


varnishncsa  -q "respstatus >= 400"

例えばこうすることでステータスコードが400以上のものを絞り込んで出力することができます。
今回はVSL-Query全般についての記事を書こうと思います。

VSL-Query

VSL-Queryは3つの要素からできています。

  • ログのグループ化
  • トランザクションの階層化
  • クエリ評価する
ログのグループ化

グループの種類は4種類あります。

raw
ログ1行を表します(何もグループ化されていない状態)
rawの場合はReqURLなどトランザクションに関わるデータとヘルスチェック等の非トランザクションデータの両方が含まれています。
ログを見た際にvxidが0のものは非トランザクションデータです。

vxid
複数行のログをHTTPトランザクション毎にグループ化します。
例えばクライアントからのトランザクションを処理する際にバックエンドまで問い合わせを行った場合は
クライアント<->VarnishとVarnish<->バックエンドの2トランザクションに分かれます。
なお、このデータにはraw時に含まれていたトランザクションにかかわらないデータ(ヘルスチェックなど)は含まれていません。
ちなみにvxidとはVarnish Transaction IDの略でトランザクションを識別するためのIDです。
X-Varnishヘッダで挿入される数値についてもそれです。

request
複数のHTTPトランザクションをまとめてリクエスト毎にします。
ESIやrestartを利用した場合はrequest内にrequestが含まれる事があります。
基本的にこの粒度で見ていればクライアント・Varnish・バックエンドでの流れがわかるので便利です。

session
複数のリクエストをまとめてセッション毎にします。
複数のリクエストが含まれるケースはkeep-aliveが有効な場合です。

grp
図にするとこんな感じです。

トランザクションの階層化

vsl-queryではログのグループ化を行うと同時に階層構造を作成します。
例えばVarnishからバックエンドへのトランザクションはその前にクライアントからVarnishへのトランザクションがあります。当然ですがそれは親子関係です。
トランザクション間の親子関係を作っているので、当然ながらログ一行ずつをグループとしているrawやトランザクション単体をグループとしているvxidでは階層化は行われません。
requestとsessionにおいて階層化が行われます。
実際に例で考えてみましょう。

グループ分けをrequestでVarnishがクライアントのリクエストを受付して、バックエンドに問い合わせして返却するケースでの階層は以下になります。


[Lv1] +ClientとVarnishのやりとり
[Lv2] | +VarnishとBackendのやり取り

次にグループ分けをsessionにしてそのセッションからのリクエストが2つあった場合は以下になります。


[Lv1] +セッション
[Lv2] | +ClientとVarnishのやりとり
[Lv3] | | +VarnishとBackendのやり取り
[Lv2] | +ClientとVarnishのやりとり
[Lv3] | | +VarnishとBackendのやり取り

実際のログだとどのように出力されるかというと(グループ分け=request)


*   << Request  >> 44645623
-   Begin          req 44645581 rxreq
-   Timestamp      Start: 1448894183.500204 0.000000 0.000000
-   Timestamp      Req: 1448894183.500204 0.000000 0.000000
-   ReqStart       ****** 47457
-   ReqMethod      GET
...
-   VCL_return     fetch
-   Link           bereq 44645624 pass
-   Timestamp      Fetch: 1448894183.708612 0.208408 0.208408
-   RespProtocol   HTTP/1.1
...
-   Timestamp      Resp: 1448894183.710162 0.209958 0.001442
-   ReqAcct        881 0 881 1994 7476 9470
-   End
**  << BeReq    >> 44645624
--  Begin          bereq 44645623 pass
--  Timestamp      Start: 1448894183.500314 0.000000 0.000000
--  BereqMethod    GET
-

こうなります。
なおレベルは1スタートとなります。

クエリ評価

グループとクエリの関係
何に対してクエリ評価を行うかというと、先ほどグループ化した中でqueryを評価して、一致するものがあればそのグループを出力します。
つまりグループがrawの場合は1行単位で評価するので


# varnishlog -graw -q "respstatus >= 400"
  61146578 RespStatus     c 404
  35190583 RespStatus     c 404
  52614757 RespStatus     c 404
  52656165 RespStatus     c 400

条件にひっかかる行だけが出力されます。

逆にsessionで評価した場合


# varnishlog -gsession -q "respstatus >= 400"
*   << Session  >> 60621569
-   Begin          sess 0 HTTP/1
-   SessOpen       ******* 62077 :80 ******* 80 1448897491.204370 99
-   Link           req 60621570 rxreq
-   Link           req 60621571 rxreq
-   VSL            store overflow
-   End            synth
**  << Request  >> 60621570
--  Begin          req 60621569 rxreq
...
--  RespStatus     200
--  RespReason     OK
...
--  End
**  << Request  >> 60621571
...
--  RespStatus     404
--  RespReason     Not Found
...

同一セッション内で404を返しているリクエストがあるためステータス200で返してるリクエストも引っかかってきています。
たとえば400以上のステータスコードを吐いてるリクエストのみを取得するのであればグループはrequest若しくはvxidが適切でしょう。
このようにグループ分けは狙ったログを出力する際には非常に重要です。

クエリの文法
クエリは次のように書きます。
[レコード] [演算子] [被演算子]
例: respstatus >= 400

レコードは次のように書きます。
{階層レベル}タグリスト:レコードのPrefix[フィールド]
これは全て指定する必要はなく、最低限タグだけを指定すればOKです。
フルで指定するとこのような指定になります
{2+}Timestamp:Resp[2]
1つずつ説明します。

階層レベル ( {2+}Timestamp:Resp[2] )
先ほど説明したトランザクションの階層化におけるレベルになります。
レベルは次のような指定の仕方があります。

指定方法/サンプル 説明
{2} Lv == 2
{2+} Lv >= 2
{2-} Lv <= 2

ちなみに{0+}も指定できます。この場合は全てのレベルと一致します

タグリスト ( {2+}Timestamp:Resp[2] )
タグの一覧はこちらになります
また、複数のタグを以下のように指定できます

指定方法 サンプル 説明
[tag],[tag] respheader,reqheader カンマ区切りでタグをを完全一致で指定します。
*[tag名の一部] *header 後方一致するタグを指定します。
[tag名の一部]* resp* 前方一致するタグを指定します。

[,]と[*]は同時に指定可能です。
また、[*]を途中で挿入することもできません。


#OKパタン
req*,resp*
#NGパタン
*eq*

レコードのPrefix ( {2+}Timestamp:Resp[2] )
データが「:」で区切られている場合はそれをキーとして使えます。
例えばTimestampでのイベントラベルやReqHeaderなどのヘッダ名に対して使用できます。
例:ReqHeader:User-Agent #リクエストヘッダのUser-Agentを指定

フィールド ( {2+}Timestamp:Resp[2] )
データがスペースで区切られている場合はそれを区切り文字として1スタートで指定できます。
例えばレスポンスが完了するまでにかかった時間を指定したい場合は以下のようにします。
例:Timestamp:Resp[2] #Timestamp:Respの第2フィールドを指定

演算子
演算子は以下のものが使用できます

タイプ 演算子
数値(int,float) == != < <= > >=
文字列 eq ne
正規表現 ~ !~

被演算子
以下が使用できます

タイプ サンプル
int 100
float 0. 0.8
string hoge
regex (iPad|iPhone)

なおregexはpcre(3)を利用しているので、例えば大小文字を無視する場合は以下のようにします


(?i)wget

文字列や正規表現は基本的に[‘]か[“]でquoteするのが良いかと思いますが、以下の文字のみで構成する場合はquoteは不要です


a-z A-Z 0-9 + - _ . *

論理演算子
先ほどの[レコード] [演算子] [被演算子]を一つの式として、論理演算子と組み合わせることで複数の条件を指定できます。
使用可能なのは以下です。

指定方法 説明
not [expr] exprが一致しない場合に真
[expr1] and [expr2] expr1と2が両方とも真の場合に真
[expr1] or [expr2] expr1か2のどちらかが真の場合に真

もちろん[expr1] and not [expr2]や[expr1] or not [expr2]の指定も可能です。

その他
他には以下の様な指定が可能です。

指定方法/サンプル 説明
respstatus タグ名だけを指定するとそのタグが存在する場合に真となる
(fetcherror ~’no backend’ and respstatus == 503) or respstatus == 400 ()を式中に含めることが可能です

実際のサンプル

自分がよく使うqueryの一部を紹介します

vcl_log:[key] ~ 任意の文字列
std.log(“key:文字列”)でデバッグ用の文字列を仕込んで、それでマッチさせます。
場合によっては数値を利用して比較演算子と組み合わせるのも便利です。

reqheader:Host ~ [ホスト名]
ホスト名を絞って表示したい場合

respstatus >=400
エラーを起こしたもの

最後に

これ書いてる時にバグっぽいのを見つけたので調べてバグならチケット切ってきます(その部分の記述は削りましたw)