CloudFrontでS3のコンテンツをキャッシュし、Lambda@Edgeでブラウザキャッシュを有効にする
仕事でCloudFrontに触る機会があったので復習兼メモ書きです。AWS始めたてなので色々間違ってる部分があるかもしれませんがご了承ください。
CloudFrontとは
CloudFrontは一言で言うとCDNで、S3などのオリジンサーバーに存在する静的コンテンツをキャッシュして、効率的かつ高速に配信してくれるサービスです。
Amazon CloudFront は、ユーザーへの静的および動的ウェブコンテンツ (.html、.css、.js、イメージファイルなど) の配信を高速化するウェブサービスであり、CloudFront ではエッジロケーションと呼ばれるデータセンターの世界規模のネットワークを通じてコンテンツが配信されます。CloudFront を使用して提供されているコンテンツをユーザーがリクエストすると、そのユーザーはエッジロケーションにルーティングされます。エッジロケーションでは最も低いレイテンシー (遅延時間) が提供されるので、コンテンツは可能な最高のパフォーマンスで配信されます。
ここでキャッシュという言葉が出てきましたが、そもそもキャッシュとは、一度表示したデータを保存しておき、次に同じデータを表示する際に処理を省略することで、表示を高速化する仕組みのことです。
キャッシュには大きく分けてサーバー側でキャッシュするものと、ブラウザ側(ローカル)でキャッシュするものの2つがあり、CloudFrontが担当するのは前者にあたります。CloudFrontを使う際に後者を有効にするには追加で設定が必要なのですが、こちらについては後程説明します。
S3バケットに対してCloudFrontを使ってみる
では実際の設定方法について見ていきます。とはいっても他に解説されているサイトがたくさんあると思うので基本的にその通りにやるだけです。自分は下記サイト様を参考にしました。 www.wakuwakubank.com
以下がCloudFrontを設定しない場合のレスポンスで、
以下はCloudFrontを設定した場合のレスポンスです。
レスポンスヘッダーに X-Cache
, Via
などが追加されていることがわかると思います。
X-Cache
の値が Hit from cloudfront
となっている場合、CloudFrontがキャッシュしていることを示しています。
CloudFront キャッシュ動作確認 - Qiita
わかりやすいようにgif動画にしてみました。一度目のリクエストには多少時間がかかっていますが、二度目のリクエストはキャッシュされており即返ってきているのがわかると思います。
一点気を点けなければいけない点として、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-Control
か Expires
が含まれている必要があり、オリジンサーバーにEC2のApacheやNginxなどを指定している場合はそれらで設定できますが、S3の場合はCloudFrontが勝手に設定してくれたりはせず、追加で設定を行う必要があります。
S3のオブジェクトに対してブラウザキャッシュを有効にする(=レスポンスヘッダーに Cache-Control
が含まれるようにする)方法としては、まずオブジェクトのメタデータを編集する方法があります。例えば以下のように設定することで
以下のようにヘッダーに Cache-Control
が含まれるようになります。
この設定はオブジェクト単位であり、全てのオブジェクトに同じメタデータを追加したい場合は逐一設定するか、何かしらのスクリプトを組み一括で操作するしかありません(AWSコンソール上から一括で設定する方法はない)。
いずれにせよ面倒ですが、別の方法としてLambda@Edgeを使うことで、すべてのS3オブジェクトに対してヘッダーを設定することができます。
Lambda@EdgeとはAWS Lambda の拡張機能で、CloudFront が配信するコンテンツをカスタマイズする関数を実行できるコンピューティングサービスです(出典:公式ドキュメント)
なお選択できるリージョンがバージニア北部(us-east-1)に限られるなど色々制約があったりはします。使い方については以下の記事が詳しいと思います。 qiita.com
実際に使うには、AWSコンソールからLambda>関数の作成 を選び、設計図からcloudfront-modify-response-header というものを選びます。(リージョンがバージニア北部となっていることに注意してください。ほかのリージョンだと検索候補に出てきません)
実行するロールを選びます。
関数コードには以下のように記述します。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イベントにはビューアーレスポンスを設定してください。
なおこのとき、実行ロールによっては、以下のエラーが表示されることがあります。
関数の実行ロールは、edgelambda.amazonaws.com サービスプリンシパルによって引き受け可能である必要があります。
ドキュメントをよく読まなかったせいで少し悩んだのですが、以下にちゃんと書いてあります。 docs.aws.amazon.com
実際には実行するロールの信頼関係が以下のようになっていなければなりません。edgelambda.amazonaws.com が欠けていると上記エラーが表示されます。
メタデータを削除し、CloudFrontのキャッシュを削除してから再度試してみると、期待通りにヘッダーが追加されていることがわかります。
CloudFrontの設定画面を見ると以下の通りにLambda関数が設定されています。逆に言えばこの画面からでもLambdaを設定することができます。
おわりに
せっかくなんでAWSの資格取れればいいなぁと思ってたまに勉強したりはしているのですが、やはりただ知識を詰め込むのではなく実際に手を動かすのが一番勉強になりますね。ちなみに書籍では以下の本を買ったんですが割とわかりやすかったです(アフィリンクとかではありません)。
AWS認定資格試験テキスト AWS認定 ソリューションアーキテクト-アソシエイト | 佐々木 拓郎, 林 晋一郎, 金澤 圭 | コンピュータ・IT | Kindleストア | Amazon