私の戦闘力は53万です

awsとgcpについて書きます

AWS cloudfront キャッシュチューニングの考え方(webサイト Behaviors)

こんにちは。cloudfrontのキャッシュチューニングについて書いてみます。

f:id:remmemento:20190620000141p:plain
よくあるパターン

cloudfrontの代表的なユースケースとしては、
webサーバの前において静的なファイルをキャッシュさせ、
オリジン(webサーバ等)の負荷を下げるものがあります。
その際、Behaviorsのキャッシュ設定の考え方について聞かれることが多く、
ブログに書いてみることにしまいした。

書いているうちに長くなってしまったので、
良く分からないけど一般的に良い感じにcloudfront設定したいって方のために
最後に設定をまとめています。
設定の考え方を知りたい方はこの記事が参考になると思いますのでぜひ。

cloudfrontの動き(大まかに)

まずcloudfrontの動きをおさらいします。
cloudfrontは下記のように動きます。
f:id:remmemento:20190620000801p:plain
まずは、viewer(クライアント)がhttp(s)リクエストを出します。
例えば、下記のwebsサイトのページを開くのがこれに該当します。
https://cloud-aws-gcp.hateblo.jp/entry/2019/06/19/021733

f:id:remmemento:20190620000908p:plain
cloudforntはこれを受け、オリジン(webサーバ)に同様にリクエストを出します。

f:id:remmemento:20190620001259p:plain
オリジン(webサーバ)はリクエストされたファイルをcloudfrontに返します。

f:id:remmemento:20190620001638p:plain
cloudfrontも同様にレスポンスをviewer(クライアント)に返します。
この時、cloudfrontはレンスポンスをキャッシュし、
次回に同じリクエストが来た場合、同様のレスポンスを返せるようにします。

キャッシュするとどうなるか

ではキャッシュされた状態での流れを見てみましょう。

f:id:remmemento:20190620001729p:plain
同じように、viewer(クライアント)がhttp(s)リクエストを出します。
f:id:remmemento:20190620001914p:plain
キャッシュがあるため、同じリクエストに対し、同じレスポンスをcloudfrontが返してくれます。
これにより、オリジン(webサーバ)にアクセスが行かないので負荷軽減に繋がります。

今の例は、1つのviewerからの例ですが、
これが数十万のアクセスとかであってもcloudfrontは同様に動いてくれます。
cloudfrontがない場合は、全てオリジンに負荷がかかるので、
ありがたみが分かりますね。

どう設定するが正しいの?

上記の概要はわかったけど、cloudfrontには設定値がたくさんあり、
結局どう設定すれば良いのかをよく聞かれます。

個人的な意見も入ってしまうのですが、下記を重視するのが良いです。
・サービスが安定稼働する
・キャッシュ効率が良い(=キャッシュヒット率が高い)

そして、その鍵を大きく握るのが下記です。
・HTTPヘッダ
cookie
・Querystring

なぜかを説明するのに、各要素の技術を理解すると良いので、
まずはHTTPヘッダについて説明します。

HTTPヘッダって何?って方のために

すごくざっくり言うと、http(s)の通信の付属情報のことです。

例えばブラウザで下記のURLを打ち込んだとします。
「www.example.com/index.html」

それは
「www.example.comサーバのindex.htmlファイルをください」と
リクエストしたことを意味します。
ただ、上記情報だけでは不十分なケースもあって、
その場合に、HTTPヘッダを使い付属情報を付け加えます。

例えば「Accept-Language」ヘッダであれば、
「自分はこの言語を受け入れ可能です」と付属情報を伝えることになります。
f:id:remmemento:20190620004234p:plain
このようにHTTPリクエストでの付属情報を取り扱うようなのがおおよそのイメージです
より詳細な情報は下記です。
developer.mozilla.org

cloudfornt設定でのHTTPヘッダ

ではcloudfrontがHTTPヘッダをどう扱うか見ていきましょう。
f:id:remmemento:20190620001729p:plain
先ほどと同様に、viewerからのアクセスで始まります。
これをもう少しHTTPリクエスト中身っぽい
書き方をすると下記のようになります。
f:id:remmemento:20190620005716p:plain
例えば「Accept-Language: ja」に注目してみましょう。
これは上記で説明したように「自分はこの言語(=ja=日本語)を受け入れ可能です」と表しています。
f:id:remmemento:20190620005758p:plain
先ほどと同様にcloudfrontはorigin(webサーバ)にリクエストします。

f:id:remmemento:20190620010213p:plain
オリジンは、言われた通りに日本語のページを返したとしましょう。
そしてcloudfrontは結果をキャッシュします。

でもここで注意です。
「同じリクエストが来たら」の「同じ」とは何を持って「同じ」なのでしょうか。

キャッシュ効率を考える

f:id:remmemento:20190620011149p:plain
上記の1と2を比べてみましょう。

1はhttpヘッダで日本語のファイルが欲しいと言っています。
2はhttpヘッダで日本語か英語のファイルが欲しいと言っています。
この2つのリクエストは、HTTPヘッダが異なりますが、それ以外は同じです。
しかしcloudfrontは2つを「異なるリクエスト」と扱います。

2つのレスポンスが同じであったとします。
その場合、cloudforntはキャッシュの枠を2つ分消費します。
でも例えば、日本語しか提供できないようなサイトだと
レスポンスの結果は「Accept-Language」に依存しないですよね。

その場合、cloudfrontからすれば「Accept-Language」の内容を無視した方が良いことになります。
f:id:remmemento:20190620012053p:plain

上記のように「Accept-Language」をキャッシュのパターンから無視すれば、
キャッシュの枠の消費は1つ分で済みます。

このように、レスポンスの結果に影響を与えないHTTPヘッダは無視すること
キャッシュ効率をあげるコツです。

この無視することを、cloudfrontはオリジンにhttpヘッダを転送しないことで実現します。
f:id:remmemento:20190620012519p:plain
viewerがcloudfrontにリクエストを送ります。

f:id:remmemento:20190620012854p:plain
cloudfrontは同様にオリジンにリクエストを送るのですが、
この時、レスポンスの結果に影響を与えないHTTPヘッダを切り捨てます
(=切り捨てるよう設定します)

サービス安定稼働を考える

では、もう一つの大事な観点を考えます。
先ほどの例では、常に日本語のページを返すのが前提でした。
しかし「Accept-Language」により異なる結果を返したいときはどうでしょうか。
f:id:remmemento:20190620021313p:plain
上記の例では「Accept-Language」の結果により、日本語、英語で返すファイルを変えています。
このようなケースで「Accept-Language」を無視したらどうなるか。
f:id:remmemento:20190620021538p:plain
例えば、日本語のリクエストが来たとします。
f:id:remmemento:20190620214730p:plain
cloudfrontはリクエストをオリジンに転送します。
ただし「Accept-Language」は転送されません。
オリジン側の設定で、「Accept-Language」がない場合、日本語を返すと設定していたとしましょう。
その場合はcloudfrontは日本語のページをviewerに返し、自らもキャッシュします。

f:id:remmemento:20190620021841p:plain
この状態で英語のリクエストが来たとします。
cloudfrontの日本語のページがキャッシュされているので、
cloudfrontは日本語のページを返します。
(「Accept-Language」は無視される設定のため)

このように、サイトの作りにより、
cloudfrontでの設定が変わってきます。

cookie、querystring

cookie、クエリストリングもHTTPヘッダと同様の考え方をします。
なるべくレスポンス結果に依存しない情報は切り捨てる。
でも、切り捨てる情報を間違うと、本来表示されるべき情報が表示されなくなってしまう。
面倒ですね。さらに面倒なのは、これらをテストもしないといけなことです。

結局どうするか

ここまで読んでくださった方、ありがとうございます。
そもそもHTTPヘッダよう分からんとか、検証が面倒と言う方向けに
「分からないんだったらこれで設定しておいたら良いのでは」を考え下記にまとめました。
簡単な例と概要を説明します。
f:id:remmemento:20190728095748p:plain

まず、Behaviorsの見方ですが、
Precedenceの数が小さい方からルールが適応されます。
上記画像例ですと、リクエストパスが「*.jpg」(「/img/img01.jpg」,「top.jpg」等)の場合が1番最初に適応されます。

さて、AWSの設定としては「cloudfrontを挟んだせいで、表示がおかしくなる」を避けないといけません。
そのため基本はDefault設定(緑部分)でリクエストを素通しします。
(=キャッシュしないよう設定します)

そして、上記で説明してきたように、
ヘッダ、cookie、クエリストリングを考慮・検証するのは大変なので、
画像ファイルのようにレスポンスがヘッダ、cookie、クエリストリングに依存しない
ファイルのパスをDefaultの上に設定していきます(赤字部分)。
赤字部分についてはファイルをキャッシュさせるよう設定します。

では下記に詳細設定値を記載します。

Behaviorsの設定( Default以外)

f:id:remmemento:20190728093712p:plain

Cache Based on Selected Request Headers

whitelist:(Host,Authorizationを設定)
解説:OriginにHost,Authorizationヘッダのみ転送する
   Hostは宛先のFQDNを指します。Hostヘッダは必須のため指定します。
   Authorizationは認可情報を指します。権限系でエラーが出ないよう設定します。

Object Caching:Customize

Minimum TTL:300
Maximum TTL:300
Default TTL:300
解説:cloudfrontでのファイルのキャッシュ時間を指定します。
   ファイルのキャッシュはサーバ側でも設定できるのですが、
   cloudfrontの設定と混在し分かりにくくなるので、
   よほど特殊な事情が無い限り、上記で全TTLの値を揃えておくのが良いです。

Forward Cookies:None

Query String Forwarding and Caching:None

解説:jpgやpng等の画像ファイルはパスが決まれば一意にコンテンツが決まるため
   CookiesやQuery Stringの値に依存しないことが一般的かと思います。
   そのためオリジンには転送しません。

Behaviorsの設定( Default)

PrecedenceでDefaultは一番最後に適応されるルールとします。
下記を全てALLに設定します。
・Cache Based on Selected Request Headers
・Forward Cookies
・Query String Forwarding and Caching

終わりに

ここまで読んで頂きありがとうございます。
書きながら長くなってしまい分かりにくいかもと感じました。
指摘等コメント頂けると幸いです。