11月 142012
 

Varnishは様々なオブジェクトのキャッシュを行いますが
その際にオブジェクトの保存を何処に行うかを選択することができます。
現在のバージョンでは

malloc
file
persistent

といった3つのストレージが存在しており、それぞれに特徴があります。
今回は特にpersistentに焦点をあてて解説します。

malloc

メモリ上にストレージを置いているVarnishで一番高速なストレージです。
メモリ上に置くため当然のことながらサーバの移設での一時停止や再起動、
workerのpanicでの自動再起動などでキャッシュが吹き飛んでしまいます。

file

ファイル上にストレージを置く、若干遅いストレージです。
ファイルと名前はついているものの永続化を目的としておらず、
ストレージへの書き込み時にflushしません。
読み書きはページ・バッファキャッシュ、つまりOSに任せており
Varnishでは特に制御はおこなっていないため、オーバーヘッドが少なく高速です。
正直HDDを使っているときは、あまりおすすめできません。
しかし、SSDやioDriveといった高速なデバイスの場合は使えるストレージです。

先程も書いた通り、永続化を目的としていないためmallocと同様に再起動でキャッシュが吹き飛びます。
名前に騙されてはいけません。

persistent

永続化を目的としたストレージです。
単一のファイルですが、内部では複数のセグメントを持っており
10~60秒に一回程度でオブジェクトを書き込むセグメントを変更します。
(ざっくりコードを見た感じだとセグメント変更時もflushをしていない)
これにより書き込み途中でpanicで自動再起動したとしても、
壊れたセグメントのみを切り離して再開することができ、安全性が高いストレージです。

私がV3.0.3でテストしたところ以下の条件でもキャッシュを維持しました。
 ・親プロセスも含めたVarnishの停止・開始(V3.0.0の時は消えましたが3.0.1で修正されました)
 ・varnishadmなどの管理ツールからの停止・開始
 ・panicなどでの自動再起動(一部消えることはある)

更にVarnish Software公式のアナウンスによるとfileストレージより高速なため
非常に使ってみたいストレージといえるんじゃないかと思います。

しかしまだPersistentストレージは発展の途上です。
最新バージョンである3.0.3では実用上に?がつく残念な仕様が幾つか存在します。

特にセグメントが一巡した(=指定ストレージサイズを使いきった)際に
最初のセグメントから再利用するのですが、期限切れしていない場合は再起動を行い拡張を試みます。
そのため、現時点では非常に玄人向けのマニアックなストレージではないかなと考えています。

もし私が使うのであれば、
 ・セグメントが一巡することが起因の再起動を起こさせないことが確信できている
 ・最上段ではなく2段目以降
であれば使うかなーっと言った感じです。

じゃぁまったく使えないじゃないかというとそうでもなく
VUG6でのプレゼン資料やVarnish Softwareのブログを見る限り、
persistentの強化を進めていくように感じられますし
現在のtrunk(確認したのは2012/11/13バージョン)では再起動も起こらず期待どおりの動きをしています
恐らく近い将来Varnishの主流のストレージになるのではないかと考えています。
もちろん人柱覚悟でtrunkを使うのも有りだと思います

–参考リンク
Testing the persistent storage engine(Persistentがfileより速いよというベンチ内容)
Release status(VUG6で発表されたV3.1までのリリース内容)
ImprovedPersistence(Persistentストレージのマイルストーンについて)
ArchitecturePersistentStorage(Persistentストレージのアーキテクチャ(古めな内容))


9月 062012
 

Amazon S3 REST-API is necessary to generate signature.
vmod-awsrest generate to Authorization and Date header for Amazon S3.

How to use

VCL


import awsrest;

backend default {
  .host = "s3.amazonaws.com";
  .port = "80";
}

sub vcl_recv{
  awsrest.s3_generic(
  "accessKey",            //AWSAccessKeyId
  "secretKey",            //SecretAccessKeyID
  req.request,            //HTTP-Verb
  req.http.content-md5,   //Content-MD5
  req.http.content-type,  //Content-Type
  "",                     //canonicalizedAmzHeaders
  req.url,                //canonicalizedResource
  now                     //Date
  );
}

Output


15 TxHeader     b Date: Tue, 03 Jul 2012 16:21:47 +0000
15 TxHeader     b Authorization: AWS accessKey:XUfSbQDuOWL24PTR1qavWSr6vjM=

This module set to req.http.Authorization and req.http.Date, bereq is not use.
I recommend call in the vcl_recv.
And, be careful to default settings.
If req.http.Authorization contains, it is not caching. (default setting)

download here.
libvmod-awsrest


Reference site
PHP で Amazon S3 の REST API を使用 #1
Authenticating REST Requests

日本語はこっち
VarnishでAmazon S3の認証ヘッダを作るVMODを作ってみた


8月 212012
 

Varnish3.0.3が公開されました。
今回の変更は多くのバグフィックスとツール系の機能拡充とドキュメントの整備です。

公式リリースノート varnish-cache-3.0.3

バグフィックス

・ストリーミングと正規表現の評価においてクラッシュの要因となるバグを複数修正しました。
・ESIとGZIPにおいて壊れたオブジェクトを返却するケースが存在する問題を修正しました。
・ESIのremoveタグ内にHTMLのコメントがあるケースにおいて不適切な削除を行なっていたのを修正
・ban lurkerのスリープする条件の変更(回収した際は設定値・回収しなかった場合は1秒)
・多くのクラッシュの要因になるバグの修正
・その他いろいろ修正

ツール系の機能拡充

varnishstat

・json出力オプションの追加(-j)

varnishtest

・resp.bodyでレスポンスボディの評価が可能に

varnishncsa

・VCL中に”Key:Value”の形式でログを出力すると%{VCL_Log:Key}xで取得可能に


■VCL
  sub vcl_recv{
  std.log("hoge:mage");
}

[root@localhost ~]# varnishncsa -F "%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\" \"%{VCL_Log:hoge}x\""
192.168.1.199 - - [21/Aug/2012:08:46:27 +0900] "GET http://192.168.1.199:6081/ HTTP/1.0" 200 5 "-" "Wget/1.12 (linux-gnu)" "mage"


・%{format}tの対応


[root@localhost ~]# varnishncsa -F "%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\" \"%{%Y%m%d%H%M%S}t\""
192.168.1.199 - - [21/Aug/2012:08:51:54 +0900] "GET http://192.168.1.199:6081/ HTTP/1.0" 200 5 "-" "Wget/1.12 (linux-gnu)" "20120821085154"

・formart指定中に\tと\nの指定が可能に


[root@localhost ~]# varnishncsa -F "%h AAA\tBBB\nCCC"
192.168.1.199 AAA       BBB
CCC

cli

・backend.listが追加されました
 バックエンドの状態を確認することができます


■定義
director default random{.retries = 5;
  {.weight = 5;.backend={.host="127.0.0.1";.port="81";}}
  {.weight = 5;.backend={.host="127.0.0.1";.port="82";}}
}
■Admin
backend.list
200
Backend name                   Refs   Admin      Probe
default[0](127.0.0.1,,81)      1      healthy    Healthy (no probe)
default[1](127.0.0.1,,82)      1      healthy    Healthy (no probe)

・backend.set_healthが追加されました
 バックエンドの状態を強制的に変更できます


■定義
backend default {
  .host = "127.0.0.1";
  .port = "81";
}

■状態をsickに変更
backend.set_health default sick


■定義
director default random{.retries = 5;
  {.weight = 5;.backend={.host="127.0.0.1";.port="81";}}
  {.weight = 5;.backend={.host="127.0.0.1";.port="82";}}
}

■状態をsickに変更(一つだけ)
backend.set_health[0] sick

■状態をsickに変更(全部)
backend.set_health sick


パラメータの変更

・http_range_support (BOOL default:ON)
 rangeリクエストのサポートですがEXPERIMENTALから正式になりました。
 ただ3.0.0からデフォルトでONになっていたので、特に気にすることはないと思います。

・send_timeout (sec default:600)
 送信時のタイムアウト時間が60秒から600秒に変更されました。
 idle_send_timeoutでアイドルの設定ができるようになった影響で増えてると思います。

・diag_bitmap (bitmap default:0)
 ban-lurkerのデバッグ用の指定が増えました(0x00080000)

・redhat用のsysconfigでVARNISH_MIN_THREADSが1から50に
 デフォルト値がよくなりました

パラメータの追加

通信に関わるパラメータ

・idle_send_timeout (sec default:60)
 送信時にアイドル状態(データの送信ができなくなった)になった場合のタイムアウト時間です

内部での正規表現の呼び出しについての制限

・pcre_match_limit (1~UINT_MAX default:10000)
 呼び出し回数の制限

・pcre_match_limit_recursion (1~UINT_MAX default:10000)
 再起回数の制限

ドキュメントの整備

その他変更

EL6用RPMの用意
・DNSディレクターにおいてポートの指定がない場合はデフォルトで80を使うようになった
・libvarnishapiの変更
・ABIが変わったのでVMODのリビルドが必要
・内部関数の定義が変わってるので一部のインラインCやVMODが動かなくなる可能性アリ(HTC_Readが個人的に痛い)

多くのバグ修正を含んでいるため適用するのが良いかと思います。


8月 042012
 

急に、LDAPにアクセスしてみたくなったので、勉強がてら作ってみました。

よく使われそうな、シンプルなLDAPの認証はもちろん
特定のグループにだけ許可のようなことも可能です。
VCLはロジックがかけるので、親和性が高いんじゃないかなと考えています。

シンプルなLDAPを使ったBASIC認証


import ldap;

sub vcl_error {
  if (obj.status == 401) {
    set obj.http.WWW-Authenticate = {"Basic realm="Authorization Required""};
    synthetic {"Error 401 Unauthorized"};
    return(deliver);
  }
}

sub vcl_recv{

if(req.url ~ "^/member/"){
        if(!(req.http.Authorization &;amp;&;amp; ldap.simple_auth(
          true, //V3プロトコルで接続するか
          "cn=Manager,dc=ldap,dc=example,dc=com", //バインドアカウント(User)
          "password", //バインドアカウント(Pass)
          "ldap://192.168.1.1/ou=people,dc=ldap,dc=example,dc=com?uid?sub?(objectClass=*)", //LDAP接続先
          ldap.get_basicuser(), //認証したいアカウント(User)
          ldap.get_basicpass()  //認証したいアカウント(Pass)
        ))){
                error 401;
        }
}

グループとユーザで制限してみる


import ldap;

sub vcl_deliver{
  //LDAPを閉じる
  ldap.close();
}

sub vcl_error{
  if (obj.status == 401) {
    set obj.http.WWW-Authenticate = {"Basic realm="Authorization Required""};
    synthetic {"Error 401 Unauthorized"};
    return(deliver);
  }
}

sub vcl_recv{

  if(req.url ~ "^/member/"){
        //LDAPに接続
        if(!(req.http.Authorization &;amp;&;amp; ldap.open(
          true, //V3プロトコルで接続するか
          "cn=Manager,dc=ldap,dc=example,dc=com", //バインドアカウント(User)
          "password", //バインドアカウント(Pass)
          "ldap://192.168.1.1/ou=people,dc=ldap,dc=example,dc=com?uid?sub?(objectClass=*)", //LDAP接続先
          ldap.get_basicuser(), //認証したいアカウント(User)
          ldap.get_basicpass()  //認証したいアカウント(Pass)
        ))){
                error 401;
        }
        //グループの照合
        if(!ldap.compare("cn=test,ou=people,dc=ldap,dc=example,dc=com","memberUid")){ldap.close();error 401;}
        //ユーザの照合
        if(!ldap.require_user("uid=hogehoge,ou=people,dc=ldap,dc=example,dc=com")){ldap.close();error 401;}
        //パスワードの照合
        if(!ldap.bind()){ldap.close();error 401;}
        ldap.close();
  }
}

ちなみにget_basicuserとget_basicpassを使うと
BASIC認証を行った時に送られてくるAuthorizationヘッダからユーザIDとパスを取得する事ができます。

なおシンプルな認証で使うsimple_authは内部ではopenとbindとcloseを呼び出しています。

ダウンロードはこちら(vmod-ldap)


7月 242012
 

先日、社内で勉強会をした時に発表した資料(ちょっと改変)です。
最初はVMODを作るときに知っておくと楽じゃないかなぁ的なポイントを解説しようと思ったんですが
そもそもVMODのワンポイントなんか社内的に誰得極まりないので
公式でリスト化されてるVMODを紹介してみました。
サービス側がこれ使ってみたいってのがあればいいなぁと考えています。

結構口頭で説明してるとこがあるので、そこは申し訳ないです。
いろいろ他の人で面白い発表も聞けたのでまた第二回とかやりたいですね


7月 112012
 

hiro_yさんからこんな質問を受けたので


AWSの勉強がてら作ってみました。

S3のREST-APIのAuthorizationヘッダは、日付やリソースの場所などを改行で結合して
HMAC-SHA1でハッシュ化して、BASE64エンコードする必要があります。
HMAC-SHA1については、Varnish公式が公開しているvmod-digestを使うことでできるのですが
出力をBASE64にすることができないので、コードを拝借して今回のVMODを作ってみました。
ちなみに改行を扱うことについても、インラインCかVMODを使う必要があります。

使い方


import awsrest;

backend default {
  .host = "s3.amazonaws.com";
  .port = "80";
}

sub vcl_recv{
  awsrest.s3_generic(
  "accessKey",            //AWSAccessKeyId
  "secretKey",            //SecretAccessKeyID
  req.request,            //HTTP-Verb
  req.http.content-md5,   //Content-MD5
  req.http.content-type,  //Content-Type
  "",                     //canonicalizedAmzHeaders
  req.url,                //canonicalizedResource
  now                     //Date
  );
}

上記のように呼び出すとreq.http.Authorizationとreq.http.Dateに生成したヘッダがセットされます。


15 TxHeader     b Date: Tue, 03 Jul 2012 16:21:47 +0000
15 TxHeader     b Authorization: AWS accessKey:XUfSbQDuOWL24PTR1qavWSr6vjM=

セットされるのがbereqではないのでvcl_recvで呼び出すことを推奨します。

またAuthorizationを生成するのでもしキャッシュを行いたい場合は適切にVCLを記述してください。
デフォルトのVCLではreq.http.Authorizationを含む場合はpassするためです。

僕の方でも簡単な動作チェックを行なっていますが、もし変な動きをしたら教えて下さい。

downloadはこちら(libvmod-awsrest)


参考サイト
PHP で Amazon S3 の REST API を使用 #1
Authenticating REST Requests


6月 132012
 

Difficult to access the POST/GET/Cookie value in Varnish.
I want more easily to access it.
Therefore, I tried to make a VMOD to parse.

Feature

  • Support POST/GET request and Cookie header.
  • Support application/x-www-form-urlencoded and multipart/form-data Content-type in POST request.
  • Can be getting a list of key.
  • Support Varnish 3.0.1, 3.0.2 , 3.0.3-rc1

How to use

For example, how to set the response header from the POST key of hoge.
It’s a simple.


import parsereq;

vcl_recv{
  //please write "parsereq.init();" to 1st line in vcl_recv.
  parsereq.init();
}
vcl_deliver{
  set resp.http.hoge = parsereq.post_header("hoge");
}

Future plans

  • urlencode/decode
  • change the value and set to bereq
  • refactoring

I hope that this code is of help to you.

download here.
vmod-parsereq


6月 102012
 

この前のエントリでPOSTを扱うVMODを作ってみたというのを上げたのですが
あの後、使ってくれた人とかからインタフェース変えるとイイヨーイイヨーと言われたので
req.http.*に格納するのではなくvmod_curlのように良い感じに使いやすくしてみました。

それだけじゃつまらないということで、おまけでGETとCOOKIEに対応してみました。

今回の特徴は以下です。
・application/x-www-form-urlencodedとmultipart/form-data両方に対応
・GET・POST・COOKIEに対応
・格納されているキーの一覧を取得することができます。
 インラインCと併用することですべてのGET・POST・COOKIEのキーに対して特定の値
 (攻撃っぽいコードなど)が含まれているかなどのチェックが容易です

使い方はこんな感じです。
たとえばPOSTでhogeというキーを取得してレスポンスする場合はこんな感じです。


import parsereq;

vcl_recv{
  parsereq.init();//必ずvcl_recvの先頭で宣言する
}
vcl_deliver{
  set resp.http.hoge = parsereq.post_header("hoge");
}

前に比べてわかりやすくなったんではないかと思います。
また今回は値はurlencodeされていません。生の値です。
そのため改行を含む可能性がある場合はresp.http.*などに含めると
意図しない結果が起きるので注意が必要です。
ココらへんはencode/decodeメソッドを用意して解決する予定ですので
少し待ってもらえるとありがたいです。

関数はこんな感じです。

VOID init()
 初期化を行う。
 必ずvcl_recvの先頭で呼び出す必要があります。

INT errcode()
 初期化の結果を受け取る


(成功) 2   content-typeが無いかサポート外
(成功) 1   パースに成功
(失敗) -1  sess_workspaceの残りサイズがなくなってメモリ確保に失敗
(失敗) -2  content-lengthが無い
(失敗) -3  読み取れるサイズがcontent-length以下

STRING post_header(STRING)
STRING get_header(STRING)
STRING cookie_header(STRING)

 指定したキーの値を取得する

STRING post_body()
STRING get_body()
STRING cookie_body()

 POST,GET,COOKIEの生データを取得

STRING post_read_keylist()
STRING get_read_keylist()
STRING cookie_read_keylist()

 呼び出すごとに格納されているキーを取得する


//req
/?name1=a&name2=b

//vcl
vcl_deliver{
  set resp.http.n1 = parsereq.get_read_keylist();
  set resp.http.n2 = parsereq.get_read_keylist();
  //nothing
  set resp.http.n3 = parsereq.get_read_keylist();
}

//return
n1: name2
n2: name1

VOID post_seek_reset()
VOID get_seek_reset()
VOID cookie_seek_reset()

 前述のXXX_read_keylistで、何処まで取得したかをリセットする。
 リセットしてXXX_read_keylistを呼び出すとまた最初から読み出す。

今後リリースされる3.0.3についても
rc1において正常動作を確認していますので多分動くと思います。
基本的に3.0.1以降であれば動きます。

今後の予定は
・エンコード・デコードロジック追加
・値の変更した後にリビルドしてそれを使う
・コードが若干カオスってるので綺麗にする
・etc…
です

downloadはこちら


6月 052012
 

まだChangelogが出ていないので気づいている範囲での変更点です。
リリースになったらまた書きます。

ドキュメント改善

 今まで記述ミスなどがあったのですがかなり書きなおされています。

varnishstatでjson出力オプションの追加

 -jでJSON形式で出力します。

新しいパラメータが増えました

通信に関わるパラメータ
 idle_send_timeout (sec default:60)
  送信時にアイドル状態(データの送信ができなくなった)になった場合のタイムアウト時間です

内部での正規表現の呼び出しについての制限
 pcre_match_limit (1~UINT_MAX default:10000)
  呼び出し回数の制限
 pcre_match_limit_recursion (1~UINT_MAX default:10000)
  再起回数の制限

パラメータの変更

http_range_support (BOOL default:ON)
 rangeリクエストのサポートですがEXPERIMENTALから正式になりました。
 ただ3.0.0からデフォルトでONになっていたので、特に気にすることはないと思います。

send_timeout (sec default:600)
 送信時のタイムアウト時間が60秒から600秒に変更されました。
 idle_send_timeoutでアイドルの設定ができるようになった影響で増えてると思います。

個人的に気になる変更

HTC_Read関数の呼び出し方が変わりました
 基本的に普通の使い方をしていれば全く影響はありませんが、
 インラインCやVMODでゴリゴリpipeline周りをいじろうとしている場合、
 バージョンアップによって動かなくなる可能性があります。
 たとえばいままで以下のように呼び出していた場合変更する必要があります。


~3.0.2
rsize = HTC_Read(sp->htc, buf, buf_size);

3.0.3~
rsize = HTC_Read(sp->wrk, sp->htc, buf, buf_size);

 僕が知っている限りVFW(Varnish Firewall)が影響を受けると思います。

 また昨日公開しているvmod_parsepostも影響を受けるのですが
 既に修正しています。

Varnish Cache 3.0.3 rc 1


6月 042012
 

Varnishは基本的にPOSTデータを解釈してなにかしらの処理をすることができません。
そこまで不便はないとは思うのですが、たとえば特定のキーワードがPOSTに入ってたら
Varnishではじきたいといったことは普通は出来ません。(インジェクションっぽいクエリとか)
そのためVarnishでPOSTを手軽に扱うためのVMODを作ってみました。

以下のような特徴・機能があります。
 ・application/x-www-form-urlencodedとmultipart/form-data両方に対応
 ・パースした値をreq.http.*に格納する
 ・指定されたヘッダにパースしていない生データを格納可能
 ・multipartのデータはurlencodeして格納
 ・multipartの生データは(扱いづらいので)application/x-www-form-urlencodedと同じ形式に直して格納
 ・multipart時のファイルについても格納

こんな感じで利用します。


//VCL
if(parsepost.parse("x-raw",true,"p_",true,true) == 1){
  std.log("raw: " + req.http.x-raw);
  std.log("submitter: " + req.http.p_submitter);
  std.log("submitter2: " + req.http.p_submitter2);
}

//response
12 VCL_Log      c raw: submitter=abcdef&submitter2=b
12 VCL_Log      c submitter: abcdef
12 VCL_Log      c submitter2: b

また特性上セッションワークスペースとスタックを多く使います。
そのためエラーが出る場合は
Varnishのsess_workspaceの値の引き上げとulimit -sの値の引き上げを行なってください。
またhttp_req_sizeを大きめにしておくと結果としてメモリの節約になります

あと生データは大きくなりがちなので使い終わったらunsetをおすすめします
そうしないとバックエンドにフェッチする際のリクエストに含まれてヘッダ大きすぎエラー(413 Request Entity Too Large/400 Bad Request)が出る可能性もあります。

ダウンロードはこちらから