HEARTBEATS

Amazon CloudFrontとキャッシュについて

   

こんにちは、MSP事業部エンジニアリンググループの佐藤です。

Webサービスを運営していく中で、パフォーマンス改善等々でCDN(コンテンツ・デリバリー・ネットワーク)の導入を検討することは多々あるかと思います。 CDN事業者が提供する分散されたエッジサーバーを介してコンテンツ配信をすることで、Webサーバーやアプリケーションの負荷を軽減したり、DDoS攻撃対策などのセキュリティ面での効果が期待できたりします。 ただし、効果的に利用するためにはCDNのキャッシュをうまくコントロールする必要があり導入に二の足を踏んでいる人もいるのではないでしょうか。

私は前職で大規模なWebサイトの運営・運用に携わっていました。 その中で、Webディレクターなど技術に精通していない運用者たちとも多く話す機会がありました。 更新したはずなのにコンテンツになかなか反映されなかったりしてコンテンツ運用をむずかしくしてしまうという側面がCDNにはあります。 これが彼らはこわく感じてしまうようです。

そこで今回は、AWSが提供するCDNであるAmazon CloudFrontを題材に、次のようなゴールを目指してお伝えしていきます。

  • Amazon CloudFrontについて基本的な情報を知ってもらう
  • キャッシュについて知ってもらう
  • エッジキャッシュの運用について知ってもらう

私が実際に前職でAmazon CloudFrontを導入するにあたってWebディレクター向けに説明した内容を元にしています。 エンジニアの方だけではなく、Webサイト運営に関わる非エンジニアの方にも参考にしていただけると光栄です!

Amazon CloudFrontとは

  • グローバルに分散した 450 以上の Point of Presence (PoP) を経由してデータを配信することで、自動化されたネットワークマッピングとインテリジェントなルーティングにより、レイテンシーを低減します。
  • トラフィックの暗号化やアクセス制御によりセキュリティを向上させ、AWS Shield Standard を利用して追加料金なしで DDoS 攻撃を防御することができます。
  • 統合されたリクエスト、カスタマイズ可能な料金オプション、AWS オリジンからのデータ転送にかかる費用をゼロにすることで、コストを削減。
  • AWS のコンテンツ配信ネットワーク (CDN) エッジで実行するコードを、サーバーレスコンピューティング機能を使ってカスタマイズし、コスト、パフォーマンス、セキュリティのバランスをとることができます。

https://aws.amazon.com/jp/cloudfront/ より

以上をざっくりと要約すると、以下のようになります。

  • レイテンシーが低減し、パフォーマンスが向上する
  • 450以上のPoPを経由することで可用性が向上する
  • セキュリティが向上する
  • AWSの他のサービスに組み合わせることでコスト削減効果が見込める
  • サーバーレスコンピューティングが活用できる

なお、こちらの情報は当記事執筆時点(2023年3月31日)での情報となります。PoPの数は年々増加しています。 私が把握している限りでは2020年3月の時点では216だったので、3年で倍以上に増えていることになります。

また、2019年3月14日に公開されている最新のサービスレベルアグリーメント(SLA)は月間稼働率99.9%以上ということです。 詳細については公式のドキュメントを参照ください。

キャッシュとは

キャッシュとは一度読み込んだ内容・情報を一時的に保存しておき、再利用できるようにする仕組みおよびそのデータのことを指します。 なお、英語では「Cache」と書き、現金を意味する「Cash」とはつづりが違います。

キャッシュの種類

Amazon CloudFrontに限らず、CDNを利用したWeb配信におけるキャッシュには大きく以下の3つが登場します。

  1. ブラウザキャッシュ
    ブラウザがすでに取得済みのデータを一時的にデバイス上に保存したもの。
    次に同じページにアクセスした際、参照することで表示を早くしたり通信量を削減させたりします。 ブラウザの強制リロードで削除ができるキャッシュはこちらになります。 アクセスさせるたびに内容が変更になるような動的なコンテンツに対してはキャッシュをさせないようにするなど対策をするべきです。

  2. エッジキャッシュ
    CDNのエッジサーバーがすでに取得済みのデータを一時的にエッジサーバー上に保存したもの。
    オリジンとなるWebサーバーに代わってブラウザにレスポンスすることで、Webサーバーや関連するシステムの負荷を下げたり、地理的にブラウザにより近い場所からすばやくデータを届けることで表示を早くします。 アクセスさせるたびに内容が変更になるような動的なコンテンツに対してはキャッシュをさせないようにするなど対策をするべきです。 しかし、更新が反映されることに時間差が許容できるのであればキャッシュさせることで上記の利点を享受できます。 逆にこのエッジキャッシュをうまくコントロールできていないと「サイトを更新した、ブラウザも強制リロードした、なのに表示に反映されない」の原因になります。

  3. サーバーキャッシュ
    オリジンとなるWebサーバーが過去に作成したデータを一時的にサーバー上に保存したもの。
    Webアプリケーションの実行頻度を減らすことでWebサーバーや関連するシステムの負荷を下げたり、アプリケーションの実行をスキップすることですばやくデータを届け表示を早くしたりします。 一般的にはデフォルトでは一切キャッシュしないので明示的に設定をすることになります。 キャッシュさせる場合の注意点はエッジキャッシュと同様になります。

エッジキャッシュの状態

ここからはAmazon CloudFrontのエッジキャッシュについて説明していきます。

エッジキャッシュには大きく以下の3つの状態が存在します。

  • キャッシュが存在しない
  • 有効なキャッシュが存在する
  • 無効なキャッシュが存在する

このうち無効なキャッシュとは有効期限(TTL)が切れたキャッシュのことを指します。

エッジキャッシュの状態ごとのAmazon CloudFrontの振る舞い

※以下は簡素化して記載しているため詳細な振る舞いを知りたい場合は公式ドキュメントを参照してください。

キャッシュが存在しない場合

クライアントからリクエストがあるとエッジサーバーは以下の振る舞いをします。

  • リクエストを受け付けたエッジサーバーは近くのエッジサーバー(リージョナルエッジサーバー)にキャッシュの有無を確認する
  • Amazon CloudFrontに設定された条件に従ってオリジンサーバーにリクエストを転送しデータを取得する
  • 取得したデータをクライアントにレスポンスする
  • 取得したデータを設定された条件に従ってエッジサーバーが保持(キャッシュ)する

有効なキャッシュが存在する場合

クライアントからリクエストがあるとエッジサーバーは以下の振る舞いをします。

  • リクエストを受け付けたエッジサーバーは近くのエッジサーバー(リージョナルエッジサーバー)にキャッシュの有無を確認する
    • 自分自身にキャッシュがある場合はそのままレスポンスする
  • キャッシュを持っているエッジサーバーにリクエストを転送しデータを取得する
  • 取得したデータをクライアントにレスポンスする
  • 取得したデータを設定された条件に従ってエッジサーバーが保持(キャッシュ)する

この状態が多くなることを「キャッシュヒット率が高い」といいます。 CDNの運用をする場合、なるべくキャッシュヒット率が高い状態を目指すことが費用対効果を最大化するうえで大きな指標のひとつとなります。

無効なキャッシュが存在する場合

クライアントからリクエストがあるとエッジサーバーは以下の振る舞いをします。

  • Amazon CloudFrontに設定された条件に従ってオリジンサーバーにリクエストを転送しオリジンの状態を確認する
  • オリジンサーバーの状態がキャッシュと同じ場合(304 Not Modified)
    • キャッシュしているデータをクライアントにレスポンスする
    • キャッシュデータのTTLを更新する
  • オリジンサーバーの状態がキャッシュと違う場合(200 OK)
    • 取得したデータをクライアントにレスポンスする
    • 取得したデータを設定された条件に従ってエッジサーバーが保持(キャッシュ)する

エッジキャッシュの確認方法

ここまででエッジキャッシュの状態についてはおおむね理解できたのではないでしょうか。 実際にブラウザで参照しているデータがAmazon CloudFrontのエッジキャッシュから取得したものであるかどうかは以下のレスポンスヘッダを確認することで判断できます。 ブラウザの開発者ツールなどで確認できますが、ここではその詳細には触れないので必要であれば別途調べてみてください。

  • ヘッダ名:x-cache
  • レスポンス値
    • Miss from cloudfront : キャッシュがなくオリジンから取得した
    • Hit from cloudfront : キャッシュから取得した
    • RefreshHit from cloudfront : キャッシュの有効期限が切れていたが変更がなくキャッシュから取得した
    • Error from cloudfront : エッジサーバーかオリジンでエラーが発生した

エッジキャッシュの条件

次にAmazon CloudFrontのキャッシュ条件の設定について紹介します。 キャッシュの持ち方をコントロールすることで、キャッシュ効率を高くしつつ、運用しやすいWebサイトを構築できます。

大前提として、Amazon CloudFrontのキャッシュ条件はパスパターンをキーとします。 1つのAmazon CloudFrontディストリビューションにつき最大で250パターンまで設定できます。 パスにはワイルドカードを使用でき、大文字/小文字は区別されます。 正規表現は使えません。 また、パスパターンの最大長は255文字となります。 各パターンに対して以下のような要素を対象にキャッシュの管理を区別できます。

  • リクエストメソッド
    • GET / HEAD / OPTIONS ※左記以外のメソッド(POST等)はキャッシュできません
  • TTL
    • 0秒〜100年まで秒単位で設定可能
  • クエリ文字列
    • 指定するとオリジンに転送します ※オリジンでクエリを受け取る必要がある場合には必ず指定します
    • 例:/item/?id=100の場合idを指定することで、idに渡された値で区別してキャッシュします
  • リクエストヘッダ
    • 指定するとオリジンに転送します ※オリジンでリクエストヘッダを受け取る必要がある場合には必ず指定します
    • Amazon CloudFrontはリクエストを受け取ると内容を元に独自のヘッダを生成できます
      • 例:user-agentの代わりにCloudFront-Is-Mobile-Viewerなどを利用することでデバイスの判定処理をAmazon CloudFrontにまかせ、キャッシュヒット率の向上が狙えます
        ※user-agentヘッダを転送するとuser-agent文字列ごとにキャッシュを管理することになり、キャッシュ効率が悪くなります

エッジキャッシュの削除

有効期限が満了していないエッジキャッシュも個別に削除可能です。 例えばコーポレートサイトのロゴ画像など、ほとんど更新しないファイルは一般的にはTTLを長く設定してキャッシュヒット率を最大化します。 このようなときに「ロゴデザインが変わったので差し替えたい!」といったケースなどで利用します。

指定できるのはパスパターンのみで、キャッシュの条件のキーと同様にワイルドカードが指定できます。 クエリ文字列を転送する設定の場合、パスパターンにはクエリ文字列も含める必要があります。 先程の例だと、/item//item/?id=1/item/?id=100はそれぞれ別のパスパターンとなります。 ワイルドカードを利用して/item/*を指定することでまとめて削除できます。

なお、削除のパフォーマンス(速度など)に対するSLAはないので、必ずしもすぐに消えるわけではないようです。 また、削除リクエストは指定したパスパターンごとに課金が発生するため、うまくキャッシュを管理してなるべく個別の削除は行わない設計をすることが大事です。

最後に

CDNのキャッシュは、実装するインフラエンジニアやバックエンドエンジニアだけでなく、マークアップエンジニアやWebディレクターも一緒に設計し、一緒に運用することで効果が最大化できます。

もうキャッシュこわくない!

参考URL:https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/HowCloudFrontWorks.html