2022/02/09

Fastlyについて知らないかもしれない30のこと

いわくら君が書いてくれた通り 、トラベルブックではFastlyを導入しました。Fastlyについて初めて分かったことがたくさんありました。列挙してみたら30個もあったので、一個ずつ紹介してみることにします。

そもそもFastlyとは

そもそもFastlyとはCDNのサービスです。現在では後述するCompute@Edgeを主力としたサーバーレス環境を推していますが、とにかくCDNです。今回は www.travelbook.co.jp ドメイン全てに対して適応し、全てのHTMLページをFastly経由にしました。 もともとVarnishでページをキャッシュしていた部分をFastlyに置き換えることで冗長化・安定化、また、パフォーマンスアップを図ります。 加えて、これまでキャッシュの対象外だったページも、この際TTL付きでキャッシュする、というのが今回やったことです。 詳しくはいわくら君の書いた記事を参考にしてください。

では、「Fastlyについて知らないかもしれない30のこと」を紹介していきましょう。

1. 速い

とにかく速いです。そりゃCDNですから、速いんですが、他のCDNと比べても速いです。 例えば、僕の個人ブログはCloudflareでホストしていますが、手元の環境で比較すると僕のブログが「36ms」と十分速いのに対して、トラベルブックのページは「10ms」とめちゃくちゃ速いです。

2. 料金

料金について、詳しいことは避けますが、想定してたより「安い」というのが印象です。 Fastlyのページにかかれている通り 、最初の10TBが「$0.19/1GB」という従量課金がベースになります。加えて、リクエスト数ごとの料金が追加されます。

従量課金だけではなく「エッセンシャル」「プロフェッショナル」「エンタープライズ」といったある一定のバジェットの含まれたパッケージがあるので、これらも考慮しつつ、プランを決めました。

3. Image Optimizer

同時に提案してもらったのが「 Image Optimizer 」です。オリジンに置いた画像をリサイズしたりリアルタイムで最適化して配信してくれるFastly内のサービスです。今現在、トラベルブックでは同等のことをAWSの「CloudFront + Lambda@Edge」で行っていますが、今後、Image Optimizerも導入するかもしれません。

4. サポート体制

サポートがほんと手厚くて助かりました。「Fastlyを検討しています」という連絡をしたら、すぐに打ち合わせをセッティングしてくれて、セールスエンジニアの方も含めて3人で対応してくれました。契約までも非常にスムーズです。また、技術的な不明点はサポートデスクが対応してくれるのですが、今のところ無料。対応も素晴らしいです。

5. USチーム

後ほど紹介する通り、導入後しばらく立ってから「HTTP/3 + QUIC」の対応を入れました。 その切り替えの際に「万一に備えて、Fastlyのエンジニアが対応できる米国のビジネスアワーに切り替えを実施する」ことを勧められました。 【米国のビジネスアワー】って何時だ?っていわくら君と笑いましたが、とにかくグローバルなはからいもありがたいです。 ちなみに深夜「0時」に作業して、大きな事故なく無事切り替えができました。

6. DNS切り替え

サービスをFastlyに乗せるにはどうするか? いくつか方法がありますが、「DNSのCNAMEを利用する」ことで実現しました。 具体的には、こういうことです。

$ dig cname www.travelbook.co.jp

;; ANSWER SECTION:
www.travelbook.co.jp.   9       IN      CNAME   n.sni.global.fastly.net.

CNAMEの切り替えをすれば、たったそれだけで「Fastly」になるのです。

7. /etc/hosts

といっても、本番適応の前に www.travelbook.co.jp ドメインでFastlyを試したい。 これ、どうやるかというと自分のマシンの /etc/hosts を書き換えるという「なるほど」なやり方を教えてもらいました。

  1. CNAME先のホストにdigをしてIPを取得
  2. そのIPwww.travelbook.co.jp/etc/hostsに追加する
  3. ブラウザで確認

これで、ホスト・ドメイン名そのままでFastlyを試すことができました。

8. インスタントパージ

Fastlyの特徴がインスタントパージです。その名の通りパージ=キャッシュの削除が速いのです。

トラベルブックでは「記事を書いて、それを公開する」というフローが多いのですが、 この際「ページを予めキャッシュしておいて、更新をフックとしてキャッシュをパージする」ということをしています。 インスタントパージのおかげで、「すぐに公開の状態を確認する」ことができます。

9. Purge API

パージは管理画面からもできますが、それ用のAPIもあります。 Fastly APIエンドポイントに対して「PURGE」HTTPメソッドを投げれば、そのURLのキャッシュが削除されます。

PURGE /example/path?parameters

しかしこれ、興味深いのはデフォルトでは「認証なし」でできちゃうので危険です。 なので、キーがないと「PURGE」できないようにしました。

10. サロゲートキー

URLをキーにするだけではなくもう少し凝った「ページの指定」をしたくなります。 例えば、(これはあまりやらないのですが)/topic/ 以下のページをパージするとかです。 Fastlyでは正規表現など文字列によるマッチができないので、この場合、サロゲートキーを使います。

例えば「 https://www.travelbook.co.jp/topic/62892 」のページにはサロゲートキーが2つ設定されています。

  • topic
  • topic-62892

「topic-62892」を指定すれば、この記事単体。「topic」を指定してパージをリクエストすると「topic」をサロゲートキーとして持つページ、つまり/topic/以下のページをパージできます。

ちなみにこれは上記の「PURGE」APIとはまた別のエンドポイントなんですが、これらのおかげで、管理画面のプログラムからリソースを指定しつつパージを実行できるようになりました。

11. ソフトパージ

ソフトパージという仕組みがあって、これを使うと、いきなりキャッシュを削除するのではなく、古い=「STALE」コンテンツとみなすことができます。オリジンからレスポンスを待つ間はSTALEのコンテンツが返り続けるので、その分、オリジンへの負担が軽減されます。

12. Datadogログストリーム

Fastlyへのアクセスは全てログとして取得できます。 ただ膨大かつストリームですので、どこかに流し込まなくてはいけません。 トラベルブックではDatadogにログストリームを流しています。 これがすごく便利。ひとつひとつのリクエスト・レスポンスが一覧とグラフで見ることができます。 支障のないようにGoogle Botのアクセスの詳細をみるとこんな感じです。 下の方にもっとプロパティがあります。

アクセスはプロパティごとに絞り込むことができます。 例えば、上記の場合「fastly_info_state」がHIT-STALE-CLUSTERになっています。 これはキャッシュにヒットせず「失効済み=STALEにヒットして返している」ことを表しています。 他にもHITMISSなどがあります。 それをHIT-STALE-CLUSTERだけに絞ってアクセスを列挙するってことが可能です。 さらにレスポンスタイムでの絞り込みができるので、「100ms以上」のアクセスとかも抽出できます。

実際、導入後はキャッシュの挙動を確認するためにDatadogが活躍しました。 また、例えばですが「同一IPからの過度なアクセス」を把握しやすくなったりしました。

13. Varnish

Fastlyでは、Varnishの2.x系からフォークしたものを独自に手を加えて使っています。 トラベルブックでは、以前からVarnishをページキャッシュに使っています。 そこで、Fastlyを「どでかいVarnish」と捉えるれば、分かりやすかったです。

14. VCL

Varnishは「Varnish Configuration Language = VCL」で設定を書きます。 これがFastlyにも踏襲されていて、Varnishのそれを拡張した「Fastly VCL」で設定を書くことになります。

もちろん管理画面からポチポチと設定できるのですが、基本的にはVCLを使います。 というか管理画面のポチポチした結果は最終的にひとつのVCLに反映されます。

便利なのは、バージョニングされることで、戻すこともできたりします。diffも管理画面で見られます。

今見たら、もう「Version 272」になっていましたね。

15. Terraform

いわくら君の書いた記事 が詳しいですが、VCLを基本とした構成はTerraformで管理しています。トラベルブックでは、管理画面は使用せず、このTerraformを使うことにしています。

16. ACL

ある程度の規模のサイトになると、悪質なエージェントに過度なアクセスをされることがあります。 Fastlyではベーシックな対策としてIPによるブロックができます。 それが「アクセスコントロールリスト=ACL」です。

17. パスベース

www.travelbook.co.jp 全部をFastly経由にしたわけですが、コンテンツによって挙動を変えたいことがあります。 特にキャッシュの設定です。 多くにページに対して、TTLを指定したキャッシュを適用しているわけですが、パスごとにTTLを変えています。 もしくはコンタクトフォームなどはキャッシュしないようにしています。

VCLで記載するわけですが、どうしてもこの方法しかなく、パスを上から正規表現でマッチさせています。

if (req.url.path == "/") {
  # TOP
  set beresp.ttl = ${default_ttl};
} elseif (req.url.path ~ "^/topic/") {
  # まとめ
  set beresp.ttl = ${default_ttl};
} elseif (req.url.path ~ "^/new_path/") {
  # 新しいパス
  set bresp.ttl = ${default_ttl};
  # 直接TTL指定する場合
  set bresp.ttl = 1h;
}

パスベースで設定ができるので、あくまで例ですが、更新頻度が高い/topic/以下はTTLを300sにするとかできます。

18. Stale-While-Revalidate

TTLが過ぎてキャッシュが消滅すると、オリジンからレスポンスが返る間、アクセスが常にオリジンにいってしまいます。 そこで、Stale-While-Revalidateという仕組みが活きています。 キャッシュを生成する間は古いコンテンツ=STALEを返し続けるのです。

Stale-While-Revalidateを理解するために、僕が以前「Varnish」のケースで図を書きました。 それを掲載します。Varnishを「Fastly」に置き換えてください。

トラベルブックではSTALEを保持する時間を長めに取ることで、なるべくオリジンに行かないようにしてパフォーマンス向上を狙います。

19. LRU

困ったのが、キャッシュがTTL手前で消える現象がたまに発生することです。 特にSTALEが消滅するので、Stale-While-Revalidateが意味なくなってしまいます。 サポートに連絡したところ、「LRUでメモリからキャッシュが追い出されている可能性がある」との返答をもらいました。 そこで解決策として、TTLを「3700s以上」に設定するといいかもとのこと。 というのも、TTLが3700s以上のものはディスクに書き込まれるから、消滅しにくいのです。 STALEもまた同じくです。

3700sは1時間ちょいと結構長いのですが、可能なところはTTLを3700sにして様子を見ています。 それでもコンテンツによっては、Stale-While-Revalidateしないものもありますが、 概ねパフォーマンスが出てます。

20. POP

CDNというからには世界の各地にコンテンツを保持し、返却するための拠点があります。 それをFastlyではPOPと呼びます。 これ、Datadogのログで把握できるので、そのキャプチャを貼っておきます。 面白いです。

21. オリジンシールド

Fastlyにはオリジンシールドという仕組みが備わっています。 オリジンの手前にひとつPOP=オリジンシールドを設けて、キャッシュが存在する限りは他のPOPに対してオリジンに代わってオリジンシールドが返却する、という仕組みです。

Users <=> POP / POP / POP <=> Origin Shield <=> Origin

この場合、オリジンに一番近いPOPをオリジンシールドにするのが常套手段です。 トラベルブックはAWSの日本のリージョンを使っているので、tyo-tokyo-jp をオリジンシールドにしています。

22. デバグ

Fastlyが返却するレスポンスをデバグするのに、便利なのがリクエストヘッダに「Fastly-Debug:1」を付加する方法です。 このフラグが立つと通常では見られない値を見ることができます。

  • fastly-debug-digest
  • fastly-debug-path
  • fastly-debug-ttl

Fastlyを導入してるサイトなら、意図的に無効にしていない限りFastly-Debug:1付きでアクセスするとデバグできたりします。

23. 他サイト調査

事前に、Fastlyを導入している・してそうなサイトを徹底的に調べました。 レスポンスヘッダを見たり、ChromeのdevToolのNetworkをみたりです。 また、以下の記事も非常に参考になりました。

24. デバイス判定

何も考えずにキャッシュさせると、スマホでPC用のページが出たり、PCでスマホのページが出てしまったりします。 キャッシュキーにデバイスの情報、つまり「mobile」「desktop」を含めないといけません。 トラベルブックのバックエンドサーバーでは、単純にUser-Agentの文字列を見て、マッチさせて判定しているので、 同じ仕組みをVCLで記載しました。

if (req.http.User-Agent ~ "iPhone" || (req.http.User-Agent ~ "Android" && req.http.User-Agent ~ "Mobile")){
  set req.http.X-device-type = "mobile";
} else {
  set req.http.X-device-type = "desktop";
}

バックエンドと同じ判定ロジックになるので、齟齬がなくなるのです。

25. ヘッダの正規化

返却するボディのエンコード(gzip、brotliなど)によってキャッシュが変わります。 それを要求するのがリクエストのAccept-Encodingヘッダなのですが、これが表記によっては同じgzipを返してほしいのに、いくつか存在してしまいます。

Accept-Encoding: gzip
Accept-Encoding: gzip, br, deflate
Accept-Encoding: gzip,br,deflate

これではキャッシュが複数存在してしまい、効率が非常に悪い。 Fastlyはこのあたりをいい感じに正規化してくれます。

26. Brotli

Fastlyがコンテンツの圧縮しています。そこでBrotliを入れました。 この効果については いわくら君の記事 が詳しいです。

27. HTTP/3 + QUIC

「攻めたポイント」としてBrotliに加えて、HTTP/3(QUIC)を導入しました。 これらは標準では使うことができず、どちらも「Limited Availability」です。 僕が「使いたい!」とメールしました。

こちらも いわくら君の記事 を参考にしてもらいたいのですが、 なんというか、HTTP/3をしているメディアサイトは少ないので、先端行ってる感がしていいですね。

28. Signal Sciences

アプリケーションレベルのWAFはデフォルトでは付きません。 しばらく前、Fastlyは「Signal Sciences」という会社を買収したのですが、 そこの会社の仕組みを使ってWAFを組んでいます。

29. Early Hints

トラベルブックでは運用していませんが、Fastlyだと、VCLに書くだけで、すぐさま「103 Early Hints」ができます。

sub vcl_recv {
 if (req.url ~ "^/topic/") {
   h2.early_hints("link: </images/logo.png> rel=preload");
 }
}

「103 Early Hints」についてはCloudflareでも試してみたので、そちらも参考にしてください。

30. Compute@Edge

VCLに置き換わるものとしてCompute@Edgeに注目しています。これなら、JavaScriptやRustで書けます。 またサーバーレスの環境としても優秀で、これまでVCLだけではできないこともできるようになりそうです。

例えばですが、 Signed ExchangesのCompute@Edge実装 なんてものもあり、ユースケースにピッタリだなって思いました。

Compute@Edgeについて、詳しくは以下に書きました。

まとめ

ふぅ。これで30個紹介しました。 だいぶ出し尽くした感あります。これから導入を考えている人の助けになると嬉しいです。 これからもトラベルブックではFastlyを使い倒したいと思います。

エンジニア募集中

トラベルブックでは一緒にFastlyを使っていくエンジニアを募集中です。 それに限らず、フロント、バックエンドともにエンジニア来てください。 興味のある方はぜひ、応募を。