S3 + CloudFront で静的サイトを作成した。オリジンの S3 が更新されたら CloudFront のキャッシュを削除して、常に最新のファイルがキャッシュされている状態を作りたい。
はじめに
CloudFront にはキャッシュを明示的に削除する機能が存在していて、これを Invalidation という。
今回は S3 のバケットが更新されたら、更新されたパスに対応する CloudFront のキャッシュを Invalidate する Lambda Function を作って動かしてみようと思う。
事前調査
軽くググってみ他ところ同じような試みは多く、2016~2019年頃のいくつかの記事に目を通した。個々のファイルのキャッシュを削除するパターン、すべてのキャッシュを削除するパターン、S3の更新をトリガーにするパターン、定期的に削除するパターンなどがあった。
今回はファイルがアップロードされたら、対象のキャッシュだけを削除したいので、S3の更新をトリガーにして個別のキャッシュを削除する ようにする。
やること
- Lambda Function を作成する
- Invalidation を実行するコードをデプロイする
- Lambda から CloudFront へのアクセスを許可する
- S3 が更新されたら Lambda が動くようにする
Lambda の設定
Lambda Function を作成する
Lambda>Functions>Create function- Author from scratch で1から作成する
- Runtime を Python 3.9 に設定
- 作成後、
Configuration>Environment variablesで環境変数名DISTRIBUTION_IDを作成してディストリビューションIDを設定しておく
Lambda から CloudFront にアクセスを許可する
Lambda>Functions> function_name >Configuration>Permissionsを開く- Execution role 内の Role name をクリックして IAM を開く
- Permissions から Add permissions > Attach policies と進む
- CloudFront で検索して
CloudFrontFullAccessにチェックしてAdd permissionsを押す
コードを書く
- 次のコードを Code source にペーストして
Deployを押す
from __future__ import print_function
import boto3
import time
import os
import json
DISTRIBUTION_ID = os.environ['DISTRIBUTION_ID']
def decimal_to_int(obj):
if isinstance(obj, Decimal):
return int(obj)
def lambda_handler(event, context):
print("Received event:" + json.dumps(event, default=decimal_to_int, ensure_ascii=False))
for items in event["Records"]:
path = "/" + items["s3"]["object"]["key"]
print("path:" + path)
client = boto3.client('cloudfront')
invalidation = client.create_invalidation(DistributionId=DISTRIBUTION_ID,
InvalidationBatch={
'Paths': {
'Quantity': 1,
'Items': [path]
},
'CallerReference': str(time.time())
})
S3 が更新されたら Lambda Function が動くようにする
Lmabda>Function overview>+ Add triggerと進み、設定画面へSelect a sourceでS3 (aws storage)を選択するBucketでオリジンを指定- Event type は All object create events を指定して Add
動作確認
- 静的ファイルを更新したときに CloudFront 経由でも更新されているか確認する
- 上手くいっていないとき
- S3 上のファイルは更新されているか
- ログにエラーは出ていないか
CloudWatch のログイベント(成功した場合)
INIT_START Runtime Version: python:3.9.v19...
START RequestId: .... Version: $LATEST
Received evet: {"Records": ...}
path: ....
END RequestId: ...
REPORT RequestID: ... Duration ...


