エキサイトニュースにfastlyを導入した話

※ この記事はエキサイトAdvent Calendar 2018 4日目の記事です。

[エキサイトニュース](https://www.excite.co.jp/news/)を今年10月にリニューアルしました。エンドユーザーからのリクエスト受け口がfastlyを仲介するシステムにしました。

その時の行なった内容を一部紹介します。

　　

# fastly主な特徴
* https://www.fastly.com/
* fastly社が提供するCDN
* ベースがVarnish
* パージを150msecで行う事が可能
* 十分なPOP
* VCLで設定管理
    * ほとんどプログラマブルで管理できる
* 多彩なAPI
    * ほとんどプログラマブルで管理できる 2
* slackで開発者とコミュニケーションが取れるなどサポートが充実


　　

# サイト登録
初回画面にオリジンに使いたいhostとIPを登録するだけです。
![スクリーンショット 2018-11-30 9.00.17.png](https://qiita-image-store.s3.amazonaws.com/0/148086/3f0d1082-9182-c7b5-01e6-05a072d986fa.png)
オリジンとTLSで通信したい場合は、証明書ホスト名と公開鍵を登録するだけ。これだけでfastlyを導入したサービスが出来上がります。

エキサイトではfastlyをreverse proxyとしても利用して入るため、複数のサービスを登録し、パスによってオリジン向き先を切り替えています。





　　

# fastly API
https://docs.fastly.com/api/

かなり豊富なAPIエンドポイントが用意されていて、後に説明するパージやVCL変更もAPI経由で行えるので、エキサイトニュースではもちろん多用しています。
API経由だけでfastlyの設定変更からデプロイ、運用監視も行えます。「プログラマブルで管理できる」とはAPIの充実さがとても大きいとも感じでいます。

####以下便利メモ

**パージAPIについて**
https://qiita.com/Satoshi-Ishii/items/319814068eb2fae86cbf
https://docs.fastly.com/api/purge
パージだけでも豊富なエンドポイント用意していて、システム管理手法が色々膨らみます。

**APIトークン作成について**
https://docs.fastly.com/api/auth#tokens

**APIライブラリについて。個人が作ったサードパーティライブラリもあるみたいです。**
https://docs.fastly.com/api/clients

**Postmanを使ったAPI使用**
https://github.com/philippschulte/fastly-postman
上記をPostmanにインポートするだけで使えます。

**VCL関連のAPI**
https://docs.fastly.com/api/config#vcl

custom VCLをfastlyにアップする

```
POST /service/{service_id}/version/{version}/vcl
Fastly-Key: FASTLYのAPIトークン
Content-Type: application/x-www-form-urlencoded
Accept: application/json

// POST Param
// ・　nameに新しくアップロードしたいvcl名
// ・　mainはメインにするかのフラグ
// ・　contentはvclの中身
```
body内容のcontentは、リクエストヘッダのcontent-typeをx-www-form-urlencodedにしてapiリクエストをすれば、
content内容をエスケープしなくてもそのま上げられます。



# VCL

fastlyの設定を管理する、Varnish製のプログラム言語

以下のサブルーチンからVCLが成り立っていて、キャッシュ設定やその他CDN・reverse proxy設定を各メソッドに明記しています。


**vcl_recv**
リクエストの最初で呼ばれ、どのようにリクエストしてゆくか定義する場所

**vcl_pass**
**キャッシュしない時**にオリジンのバックエンドにリクエストする前の時に呼び出されます。

**vcl_hash**
キャッシュの存在確認の時に呼び出されます。

**vcl_hit**
キャッシュがヒットした時に呼ばれます。

**vcl_miss**
キャッシュが見つからなかった時に呼ばれます。

**vcl_fetch**
オリジンからコンテンツが取得できた時に呼ばれます。

**vcl_deliver**
レスポンスをクライアント側に返す時に呼ばれます

**vcl_error**
エラーが発生した時に呼ばれます。

以下でわかりやすく説明していて参考にさせてもらいました。
https://reboot.makeall.net/2013/02/14/what-vcl-on-varnish-02/
https://qiita.com/koudaiii/items/6a0efa024842cd48e2fb#varnish%E3%81%AE%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E5%87%A6%E7%90%86%E3%83%95%E3%83%AD%E3%83%BC

fastly社福田さんの記事
https://qiita.com/AtsushiFukuda/items/db26ce0f75998e7e4385


**VCLで使える変数や記述方法・正規表現について**
https://reboot.makeall.net/2013/02/15/what-vcl-on-varnish-03/
https://reboot.makeall.net/2013/02/18/what-vcl-on-varnish-04/
https://docs.fastly.com/ja/guides/vcl-tutorials/vcl-regular-expression-cheat-sheet

**custom VCL**
エキサイトニュースでは、コンテンツの出し分け設定・ヘッダ情報作成・cookie作成・キャッシュ設定などを書いています。
また、VCLをgithub管理し、自動でfastlyにアップデートさせるシステムを作り管理しています。
**これもapiの柔軟さにかなり助けられました**

　　
#### fiddle
https://fiddle.fastlydemo.net/

fastly導入当初はVCL修正時、
1. VCLを修正
2. CUSTOM VCLをfastlyにアップ
4. Versionをactiveに設定
5. 確認（ダメなら1から・・）

という順序で確認していたのですが、、なんとも確認フローがちょいとめんどい・・
fiddleを使えばちょっとした修正の挙動確認ができ、fiddleで確認してから、自前のCUSTOM VCLに落とす、というフローができました。すごく便利！


　　


# Varyヘッダとコンテンツ出し分け
https://www.fastly.com/blog/best-practices-using-vary-header
Varyヘッダによって、パスとURL以外にもキー材料を設けられます。

ざっくりに言うと```Vary : User_Agent```とヘッダに設けるとUser_Agent毎にキャッシュキーが判定されキャッシュコンテンツ分けられますが、
デバイス毎にキャッシュ内容を分けられる反面、キャッシュヒット率が著しく低下するため、設定には考慮が必要です。などの事が書かれています。

**キャッシュキーのデフォルトについて**

fastlyでは

* リクエストパスとパラーメタ（```/news/society?_page=2```）+
* ホスト（```www.excite.co.jp```）+
* 謎パラメータ（```#####GENERATION#####```）

という形になっていました。（```/news/society?_page=2/www.excite.co.jp#####GENERATION#####```）


　　

# パージ
https://docs.fastly.com/ja/guides/purging/single-purges.html

管理画面GUIからワンタッチでパージが行えます。


**Surrogate-Key**
https://docs.fastly.com/guides/purging/getting-started-with-surrogate-keys

  * URLに独自のキーを設定できます。例えば、タグのような感じでユーザー情報、ニュース情報、画像情報のようなカテゴライズさせた情報をページにkeyを貼り、特定のkeyのキャッシュのみを削除したりするのに便利です。
  * Surrogate-Keyを設定させる方法は、fastly上のGUIから設定するか、アプリケーション側でレスポンスヘッダーに設定するよう対応します。

エキサイトニュースはコンテンツが自社記事・外部配信からのニュースと更新性が高いため、更新があった記事だけをリアルタイムでパースをかけるシステムを設けています。
また、記事リスト面や「jsでパースさせる枠だけ」といった部分コンテンツにもパースをかけており、更新性を担保しつつ、キャッシュさせているページは無駄にしないようにしています。



　　

# DNS
https://docs.fastly.com/ja/guides/basic-setup/adding-cname-records

fastlyにオリジンを登録したら
```https://<name>.global.ssl.fastly.net/```
というURLが発行されるので、こちらでCDNで配信されたコンテンツの確認を行なえます。

エキサイトニュースではfastlyを本番環境に切り変え時、ssl証明書をfastlyに登録した後、```[name].map.fastly.net```のマップがfastly側から発行されたので、そちらにCNAME設定をしました。

こちらにも詳しく説明されています。
https://qiita.com/AtsushiFukuda/items/f8efe8a1613a7fda9894



　　


# ABテスト
CDNでもABテストは行えます。VCLに簡単に書けるのですごく便利です。

https://www.fastly.com/blog/ab-testing-edge
上記記事がとても参考になります。

一部抜粋しますと

```
# RECV
if (!req.http.Cookie:ABViewMode) {
  # AB判定のクッキーが設定されていない場合
  # view_aが10%の確率でヘッダにセットされます
  if (randombool(10,100)) {
    set req.http.X-ABViewMode = "view_a";
  } else {
    set req.http.X-ABViewMode = "view_b";
  }
} else {
  # クッキーが設定されているときは、クッキーのAB判定をヘッダにセット
  set req.http.X-ABViewMode = req.http.Cookie:ABViewMode; 
}

# FETCH
varyヘッダにAB設定のヘッダを付与させ、コンテンツ出し分けに使う
if (beresp.http.Vary) {
	set beresp.http.Vary = beresp.http.Vary ", X-ABViewMode";
} else {
	set beresp.http.Vary = "X-ABViewMode";
}

#DELIVER
if (!req.http.Cookie:ABViewMode){
  # クッキーにAB設定がなければ、ヘッダのAB設定をセットする
  add resp.http.Set-Cookie = "ABViewMode=" req.http.X-ABViewMode ";";
}
```
のような形でABテストを設定できます。



　　

# オリジンのx-forwarded-for、remote_addrの設定など

https://api.fastly.com/public-ip-list
fastlyでは各リージョンのip範囲をapiで公開しています。

例えばオリジン側のnginxでクライアントIPをログなどに出力したい場合、

```
# qiita投稿時のfastly公開ipリスト
set_real_ip_from  23.235.32.0/20;
set_real_ip_from  43.249.72.0/22;
set_real_ip_from  103.244.50.0/24;
set_real_ip_from  103.245.222.0/23;
...

real_ip_header    X-Forwarded-For;
real_ip_recursive on;
```
上記のようにremote_addrを変更すれば、無事クライアントIPを取得することができます。

apiでipリストが公開されて入るので、システムで管理して動的にACLを管理などもできますね。



　　


# 最後に
fastlyはとても導入しやすく、
メンテ・運用がプログラマブルで管理しやすく、
チーム開発にも属人的になりにくい体制で運用出来るのが嬉しいなと思いました。


エキサイトニュースはまだまだfastly初心者、キャッシュヒット率をもっとあげられるとか、fastlyでできる事はもっともっとあるので、チューニングを今後もしてゆきたいと思います。

何よりもキャッシュによる速度改善が大きな恩恵です。
ユーザーに喜んでもらえるコンテンツにしてゆきたいですね！