Varnish
HTTP
cdn
VCL
fastly

FastlyでLoad BalancingしつつオリジンごとにHostヘッダを書き換える

先に結論

directorを使ってset req.backend = autodirector_;のような処理を入れた場合、各サブルーチンからreq.backendの変数を参照することが出来ません。理由としては、最終的にbackendが決定されるのがvcl_missを通りオリジンにリクエストを発行する直前であるため、例えvcl_missの内部であっても変数の値を知ることができません。
これにより、例えばオリジンに2つのS3バケットが設定されており、それぞれのS3バケットにアクセスする際にHostヘッダを書き換えたい場合、実際にset req.backend = autodirector_;によってどちらのbackendが選択されたか知ることが出来ないため、適切なHostヘッダを設定できません。

回避策としてrandombool()を使って類似の振る舞いを実現するのが本記事のゴールです。

FastlyでLoad Balancingを使う

Fastlyで複数オリジンに対してLBさせるには二通りの方法があります。

  • GUIから設定できるAuto load balanceを使う
  • カスタムVCLを使って自分でdirectorを定義する

[参考]
Fastly Load balancing
Load-balancing configuration

GUIからAuto Load Balanceを使う

下記の画像はGUI(Origins->Hosts->CREATE HOST)からAuto load balanceを有効にする際の設定画面です。

  • GUIからAuto load balanceを設定する場合、オプション項目としてWeight(重み付け)のみ設定できます。

スクリーンショット 2017-08-22 22.37.11.png

生成される関連VCLコード

director autodirector_ random {   
   {
    .backend = F_server2;
    .weight  = 100;
   }{
    .backend = F_server1;
    .weight  = 100;
   }
}

sub vcl_recv {
#--FASTLY RECV BEGIN
    # (中略)

    set req.backend = autodirector_;

    # (中略)
}

カスタムVCLを使って自分でdirectorを定義する

  • ↑↑に書いたVCLコードを参考に、カスタムVCLに直接directorの設定を書いてしまえばOKです。
    Tipsとしては、directorの振る舞いとして上記のrandom以外にround-robin, hash, clientの計4種類が利用できますGUIからは変更できません。
  • directorを書いたら、前項の例のように任意の場所で必要な条件を設定してset req.backend = autodirector_;のようにbackendを決定するコードを書いてください。
  • カスタムVCLにdirectorを直接書く場合、GUIからAuto load balanceを有効にする必要はありませんが、利用するオリジンは事前にGUIから設定してください。Show VCLボタンを押して表示されたVCLからbackend名を見つけて正しく設定してください。(前項の例で示すところのF_server1部分)

Load Balancingしつつreq.http.hostを書き換えたい

  • ごく稀なユースケースだとは思いますが、ロードバランスしつつそれぞれのオリジンにアクセスする時にHostヘッダを変更する必要がある場合、一見すると下記のようなコードで動作するように見えますが、期待する動作にはなりません。理由は本記事の先頭に書いた通りです。
director autodirector_ random {   
   {
    .backend = F_server2;
    .weight  = 100;
   }{
    .backend = F_server1;
    .weight  = 100;
   }
}

sub vcl_recv {
#--FASTLY RECV BEGIN
    # (中略)

    set req.backend = autodirector_;

    if (req.backend == F_server1) {
        set req.http.host = "server1.example.com";
    }else if (req.backend == F_server2) {
        set req.http.host = "server2.example.com";
    }

    # (中略)
}

つまり、残念ながらdirectorを使ったロードバランスではこれを実現することができません。

回避策としてのrandombool()

  • FastlyではVCLの中でrandombool()を使うことができます。
    これを代用することでdirectorを使ったロードバランス同様の振る舞いを表現してみます。
sub vcl_recv {
#--FASTLY RECV BEGIN
    # (中略)

    if (randombool(1, 4)) {
        set req.backend = F_server1;
        set req.http.host = "server1.example.com";
    }else{
        set req.backend = F_server2;
        set req.http.host = "server2.example.com";
    }

    # (中略)
}

上記の例では、1/4のリクエストがtrueとなるので、リクエストのうち25%はF_server1、残りはF_server2へ振り分けられることになります。
このようにrandomboolを使うことでdirectorでweightを設定した時と同様の振る舞いが実現できました。
またrandomboolの他にシード値を与えることができるrandombool_seeded()も利用することができます。
randombool_seeded()を応用することでcookieレスなA/Bテストなども実現可能です。