CloudFrontでS3のコンテンツをキャッシュし、Lambda@Edgeでブラウザキャッシュを有効にする

仕事でCloudFrontに触る機会があったので復習兼メモ書きです。AWS始めたてなので色々間違ってる部分があるかもしれませんがご了承ください。

CloudFrontとは 

CloudFrontは一言で言うとCDNで、S3などのオリジンサーバーに存在する静的コンテンツをキャッシュして、効率的かつ高速に配信してくれるサービスです。

Amazon CloudFront は、ユーザーへの静的および動的ウェブコンテンツ (.html、.css、.js、イメージファイルなど) の配信を高速化するウェブサービスであり、CloudFront ではエッジロケーションと呼ばれるデータセンターの世界規模のネットワークを通じてコンテンツが配信されます。CloudFront を使用して提供されているコンテンツをユーザーがリクエストすると、そのユーザーはエッジロケーションにルーティングされます。エッジロケーションでは最も低いレイテンシー (遅延時間) が提供されるので、コンテンツは可能な最高のパフォーマンスで配信されます。

ここでキャッシュという言葉が出てきましたが、そもそもキャッシュとは、一度表示したデータを保存しておき、次に同じデータを表示する際に処理を省略することで、表示を高速化する仕組みのことです。

aimstogeek.hatenablog.com

キャッシュには大きく分けてサーバー側でキャッシュするものと、ブラウザ側(ローカル)でキャッシュするものの2つがあり、CloudFrontが担当するのは前者にあたります。CloudFrontを使う際に後者を有効にするには追加で設定が必要なのですが、こちらについては後程説明します。

S3バケットに対してCloudFrontを使ってみる

では実際の設定方法について見ていきます。とはいっても他に解説されているサイトがたくさんあると思うので基本的にその通りにやるだけです。自分は下記サイト様を参考にしました。 www.wakuwakubank.com

以下がCloudFrontを設定しない場合のレスポンスで、 f:id:Kanchi0914:20201110233535p:plain

以下はCloudFrontを設定した場合のレスポンスです。 f:id:Kanchi0914:20201110233614p:plain

レスポンスヘッダーに X-Cache , Via などが追加されていることがわかると思います。 X-Cache の値が Hit from cloudfront となっている場合、CloudFrontがキャッシュしていることを示しています。 CloudFront キャッシュ動作確認 - Qiita

わかりやすいようにgif動画にしてみました。一度目のリクエストには多少時間がかかっていますが、二度目のリクエストはキャッシュされており即返ってきているのがわかると思います。 f:id:Kanchi0914:20201110234053g:plain

一点気を点けなければいけない点として、S3のリージョンを ap-northeast-1 とかに設定していると、リクエスト時に以下のエラーが返ってくる場合があります。

<Error><Code>TemporaryRedirect</Code><Message>Please re-send this request to the specified temporary endpoint. Continue to use the original request endpoint for future requests.</Message><RequestId>A4DBBEXAMPLE2C4D</RequestId>

以下でわかりやすく解説されていますが、これはCloudFrontのドメイン名にリージョン名を追加することで解決します。単純に更新が反映されるまでしばらく待つのでも問題はないみたいです。 dev.classmethod.jp

Lambda@Edgeでブラウザキャッシュを有効にする

CloudFrontを通すことでサーバーキャッシュが有効になりましたが、ブラウザキャッシュまで有効になったわけではありません。キャッシュの項で貼ったリンクを読んで頂ければわかるのですが、そもそもブラウザキャッシュを有効にするためにはレスポンスヘッダーに Cache-ControlExpires が含まれている必要があり、オリジンサーバーにEC2のApacheやNginxなどを指定している場合はそれらで設定できますが、S3の場合はCloudFrontが勝手に設定してくれたりはせず、追加で設定を行う必要があります。

serverfault.com

S3のオブジェクトに対してブラウザキャッシュを有効にする(=レスポンスヘッダーに Cache-Control が含まれるようにする)方法としては、まずオブジェクトのメタデータを編集する方法があります。例えば以下のように設定することで f:id:Kanchi0914:20201110234521p:plain

以下のようにヘッダーに Cache-Control が含まれるようになります。 f:id:Kanchi0914:20201110234548p:plain

この設定はオブジェクト単位であり、全てのオブジェクトに同じメタデータを追加したい場合は逐一設定するか、何かしらのスクリプトを組み一括で操作するしかありません(AWSコンソール上から一括で設定する方法はない)。

いずれにせよ面倒ですが、別の方法としてLambda@Edgeを使うことで、すべてのS3オブジェクトに対してヘッダーを設定することができます。

Lambda@EdgeとはAWS Lambda の拡張機能で、CloudFront が配信するコンテンツをカスタマイズする関数を実行できるコンピューティングサービスです(出典:公式ドキュメント)

なお選択できるリージョンがバージニア北部(us-east-1)に限られるなど色々制約があったりはします。使い方については以下の記事が詳しいと思います。 qiita.com

実際に使うには、AWSコンソールからLambda>関数の作成 を選び、設計図からcloudfront-modify-response-header というものを選びます。(リージョンがバージニア北部となっていることに注意してください。ほかのリージョンだと検索候補に出てきません) f:id:Kanchi0914:20201110234820p:plain

実行するロールを選びます。 f:id:Kanchi0914:20201110234842p:plain

関数コードには以下のように記述します。cache-control ヘッダが存在しない場合に設定するコードです。比較のためmax-age=7200としています。

'use strict';

exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;

    if(!response.headers['cache-control'])
    {
        response.headers['cache-control'] = [{ 
            key:   'Cache-Control', 
            value: 'max-age=7200' 
        }];
    }

    callback(null, response);
};

アクション>Lambda@Edgeへのデプロイからデプロイします。 CloudFrontイベントにはビューアーレスポンスを設定してください。 f:id:Kanchi0914:20201110234948p:plain

なおこのとき、実行ロールによっては、以下のエラーが表示されることがあります。

関数の実行ロールは、edgelambda.amazonaws.com サービスプリンシパルによって引き受け可能である必要があります。

f:id:Kanchi0914:20201110235026p:plain

ドキュメントをよく読まなかったせいで少し悩んだのですが、以下にちゃんと書いてあります。 docs.aws.amazon.com

実際には実行するロールの信頼関係が以下のようになっていなければなりません。edgelambda.amazonaws.com が欠けていると上記エラーが表示されます。 f:id:Kanchi0914:20201110235251p:plain f:id:Kanchi0914:20201110235332p:plain

メタデータを削除し、CloudFrontのキャッシュを削除してから再度試してみると、期待通りにヘッダーが追加されていることがわかります。 f:id:Kanchi0914:20201110235536p:plain

CloudFrontの設定画面を見ると以下の通りにLambda関数が設定されています。逆に言えばこの画面からでもLambdaを設定することができます。 f:id:Kanchi0914:20201110235456p:plain

おわりに

せっかくなんでAWSの資格取れればいいなぁと思ってたまに勉強したりはしているのですが、やはりただ知識を詰め込むのではなく実際に手を動かすのが一番勉強になりますね。ちなみに書籍では以下の本を買ったんですが割とわかりやすかったです(アフィリンクとかではありません)。
AWS認定資格試験テキスト AWS認定 ソリューションアーキテクト-アソシエイト | 佐々木 拓郎, 林 晋一郎, 金澤 圭 | コンピュータ・IT | Kindleストア | Amazon