FastlyのカスタムVCLを使ってみる



# FastlyとVCL(Varnish Configuration Language)
FastlyのCDNはVarnishと呼ばれるOpen Sourceのリバースプロキシソフトウェアを独自にカスタマイズしたソフトウェアを使って構築されているのですが、このVarnishの挙動をコントロールする設定言語をVCL（Varnish Configuration Language)と呼びます。

（実はFastlyのWebポータルのGUIを通じて設定された各種設定も、設定された内容に基づいてFastlyのプラットフォーム上でVCLが生成されています。）

FastlyではGUIを通じて設定を作成する以外に、直接自分でVCLを記載する方法として、Snippet と カスタムVCLと呼ばれる2つの機能を提供しています。

どちらのオプションでも概ね同じことが実現出来ますが、ちょっとした処理を特定のサブルーチンの下に直接記述したい場合はSnippetの方が適しています。

Custom VCLは、複数のサブルーチンにまたがる複雑な処理を記載したい場合や、特定の動作をするサービスを複数作成する必要がある場合に、カスタムVCLで設定を定義したファイルをローカルで管理し、そのファイルを対象の複数サービスに適用するといったような利用ケースが考えられます。

まずはSnippetで試してみて、それで何か支障がある場合はCustom VCLの利用を検討する、といった流れがよいでしょう。

この記事ではカスタムVCLを利用する手順と注意事項について説明します。

なお、カスタムVCL機能でアップロードするVCL（カスタムVCLと呼びます）は、Webポータルで設定した内容と合成されひとつのVCLファイルに纏められます。纏められたVCLファイルはGenerated VCLと呼ばれ、設定画面の右上のリンクから内容を確認することが出来ます。

カスタムVCLファイルのみで全ての設定が完結するわけではありませんのでご注意下さい。


```
特に記載がない限り本記事の記載内容はデフォルト設定での挙動となります。
Fastlyの正式なサポート情報は以下のサイトでご確認下さい。
https://docs.fastly.com/
https://docs.fastly.com/ja/guides/vcl/mixing-and-matching-fastly-vcl-with-custom-vcl
```


- - - 
## 作業の流れ
最初に大まかな作業の流れを説明します。
・配信設定の作成（DomainやBackendなどを設定します）
・カスタムVCLを記載してアップロード
・Show VCL から生成されたVCLを確認し、問題がなければActivate

## カスタムVCL利用時の注意事項
カスタムVCLを利用するにあたってまず以下の点に注意して下さい。

### Fallback(Default) TTL
Cache-ControlヘッダーなどでTTLが指定されていなかった場合に適用されるFallback TTLはGUI上で設定可能ですが、カスタムVCLを利用した場合はGUIで設定した値ではなくカスタムVCL内で指定した値が適用されます。
後述のboilerplate.vclの43行目の`set beresp.ttl = 3600s;`の箇所が該当します。

### ボイラープレートの利用
カスタムVCLをアップロードする際には、ボイラープレートと呼ばれるVCLのテンプレートに必要な設定を追記し、追記した設定を含むボイラープレートVCL全体をアップロードする必要があります。

現在のボイラープレートは以下の通りですが、今後変更の可能性もあるので以下のサイトから最新版をダウンロードしてご利用下さい。
[https://developer.fastly.com/learning/vcl/using/#custom-vcl](https://developer.fastly.com/learning/vcl/using/#custom-vcl)

```boilerplate.vcl
sub vcl_recv {
#FASTLY recv

  # Normally, you should consider requests other than GET and HEAD to be uncacheable
  # (to this we add the special FASTLYPURGE method)
  if (req.method != "HEAD" && req.method != "GET" && req.method != "FASTLYPURGE") {
    return(pass);
  }

  # If you are using image optimization, insert the code to enable it here
  # See https://developer.fastly.com/reference/io/ for more information.

  return(lookup);
}

sub vcl_hash {
  set req.hash += req.url;
  set req.hash += req.http.host;
  #FASTLY hash
  return(hash);
}

sub vcl_hit {
#FASTLY hit
  return(deliver);
}

sub vcl_miss {
#FASTLY miss
  return(fetch);
}

sub vcl_pass {
#FASTLY pass
  return(pass);
}

sub vcl_fetch {
#FASTLY fetch

  # Unset headers that reduce cacheability for images processed using the Fastly image optimizer
  if (req.http.X-Fastly-Imageopto-Api) {
    unset beresp.http.Set-Cookie;
    unset beresp.http.Vary;
  }

  # Log the number of restarts for debugging purposes
  if (req.restarts > 0) {
    set beresp.http.Fastly-Restarts = req.restarts;
  }

  # If the response is setting a cookie, make sure it is not cached
  if (beresp.http.Set-Cookie) {
    return(pass);
  }

  # By default we set a TTL based on the `Cache-Control` header but we don't parse additional directives
  # like `private` and `no-store`. Private in particular should be respected at the edge:
  if (beresp.http.Cache-Control ~ "(?:private|no-store)") {
    return(pass);
  }

  # If no TTL has been provided in the response headers, set a default
  if (!beresp.http.Expires && !beresp.http.Surrogate-Control ~ "max-age" && !beresp.http.Cache-Control ~ "(?:s-maxage|max-age)") {
    set beresp.ttl = 3600s;

    # Apply a longer default TTL for images processed using Image Optimizer
    if (req.http.X-Fastly-Imageopto-Api) {
      set beresp.ttl = 2592000s; # 30 days
      set beresp.http.Cache-Control = "max-age=2592000, public";
    }
  }

  return(deliver);
}

sub vcl_error {
#FASTLY error
  return(deliver);
}

sub vcl_deliver {
#FASTLY deliver
  return(deliver);
}

sub vcl_log {
#FASTLY log
}
```

Fastly側のGUIで設定した各種設定はボイラープレート内の#FASTLY recv, #Fastly hitなどの箇所に挿入されて最終的なVCLが生成されます。ボイラープレートにコードを追記する際にはこの箇所を削除しないように注意して下さい。


例えばモバイルユーザーからリクエストの場合にヘッダーを付与するコードを追記してみます。

この場合処理はタイミングはユーザーのリクエストを受け付けたタイミングで行われるべきなのでvcl_recvの下にコードを以下のように追記します。

```
  sub vcl_recv {
  #FASTLY recv

  ### Added Special Header for mobile user ###
  if( req.http.User-Agent ~ "(Android|Applebot|BlackBerry|Googlebot-Mobile|IEMobile|iPhone|iPod|SymbianOS|Windows Phone)") {    
  set req.http.X-Mobile_Device = "true";  
  }

  if (req.request != "HEAD" && req.request != "GET" && req.request != "FASTLYPURGE") {
  return(pass);
  }

  return(lookup);
  }
```
このコードはHTTPリクエストのUser-Agentのヘッダーにモバイルを示す文字列が含まれる場合、リクエストに`X-Mobile_device: true`ヘッダーを追加する、という意味になります。


## カスタムVCLのアップロード

それではカスタムVCLをアップロードしてみます。繰り返しになりますがアップロードするファイルは追記したコードを含むボイラープレート全体です。
vcl_recvを変更したからといってvcl_recvのみをアップロードしても正常に処理は行われませんので注意して下さい。

1. FastlyのWebポータルにアクセスして**Deliver**をクリックして下さい
2. serviceから変更を行いたい**Service**を選択して下さい
3. **Edit Configuration**ボタンをクリックして、**Clone active**をクリックして下さい
4. **Custom VCL** タブをクリックして下さい


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128569/c9179bb5-c7db-0283-edb9-78a33e953cda.png)


5. **Upload Your First VCL**ボタンをクリックして下さい。Upload a VCL file が表示されます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128569/f3c7ee16-d4eb-a7bb-d107-e2110e74f52c.png)

6. **Name**フィールドに適当な名前を入れて下さい
7. **Upload file**をクリックし、ボイラープレートをベースに設定を追記したカスタムVCLファイルを選択して下さい。
8. **Create**ボタンをクリックして下さい

UploadしたカスタムVCLファイルはView Source、Download、 Delete、または Edit することが出来ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128569/db9fd2f3-6aa4-7e83-b769-0056621516cb.png)


UploadしたカスタムVCLを含むServiceのバージョンをActivateすればカスタムVCLの適用は完了です。


## 完成したVCLの確認
カスタムVCLとFastly側で生成されたVCL両方を含むVCLは、ページの上部の Show VCL をクリックすることで確認することが出来ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128569/c89ec97b-5028-6f2c-73d7-4c7ef14cf080.png)

カスタム VCL と UI での設定を併用した場合、設定内容の組み合わせにより UI で設定した内容(例、fallback TTL や hash key など)が Generated VCL に反映されないケースがあります。

最終的な挙動は Show VCL をクリックして表示される Generaed VCL で制御されるので、設定内容が意図した通りに行われているか確認、検証を十分に行なって下さい。


# その他の機能
以上がFastlyでカスタムVCLを利用する手順となります。まずは簡単な処理を追加してみて動作を確認し、少しずつ複雑な処理を追加していくのがよいと思います。

FastlyではカスタムVCLを複数のファイルに分けて記載したり、Fastlyサーバーから様々な情報を取得しつつ条件に基づいて処理を行ったりとより高度な処理を実施することも出来ます。

機能の詳細や注意事項についてはFastlyの公式サポートページをご参照下さい。
https://docs.fastly.com/