はじめに
S3とCloudFrontで静的サイトを構築した状態で、オリジンのS3が更新されたらCloudFrontのキャッシュを削除するLambdaを実装します。
キャッシュの削除は、全てのキャッシュを削除する
パターンと、更新したファイルのキャッシュのみを削除
するパターンの2パターンのコードを作成します。
S3を作成
Cloudfront作成
設定は、以下以外は全てデフォルトです。
- オリジンドメインは、先程作成したS3を選択
- S3 バケットアクセスは、
Origin access control settings (recommended)
を選択- コントロール設定を作成する(設定はデフォルトでよい)
- キャッシュポリシーは、開発中は、
CachingDisabled
にしておく - ビューワープロトコルポリシーは、
Redirect HTTP to HTTPS
ディストリビューション作成が成功すると、ポリシーをコピーし、S3のバケット権限に反映させましょう。
そして、s3に適当な画像をアップロードし、表示されるか確認しましょう。
aws-icon.png
表示されることを確認しました。
Lambdaを作成
Lambdaを作成します。Python3.9を指定し、他はデフォルトのままにします。
IAMロール
LambdaのIAMロールにcloudfront:CreateInvalidation
の権限をリソースを"arn:aws:cloudfront::xxxxxxxxxxx:distribution/*"
に絞り、権限付与します。
xxxxxxxxxxx
は、アカウントIDです。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "arn:aws:logs:ap-northeast-1:xxxxxxxxxxx:*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:ap-northeast-1:xxxxxxxxxxx:log-group:/aws/lambda/delete-cache-of-cloud_front-in-S3:*"
]
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"cloudfront:CreateInvalidation",
"logs:CreateLogGroup"
],
"Resource": [
"arn:aws:cloudfront::xxxxxxxxxxx:distribution/*",
"arn:aws:logs:ap-northeast-1:xxxxxxxxxxx:*"
]
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:ap-northeast-1:xxxxxxxxxxx:log-group:/aws/lambda/delete-cache-of-cloud_front-in-S3:*"
}
]
}
コード
コードの内容ですが、S3の更新頻度によって2パターンに分かれると思います。
-
S3の更新頻度が多い、または更新時のファイル数が多い
場合- →1回の更新で、全キャッシュ削除します。
-
S3の更新頻度が低い、または更新時のファイル数が少ない
場合- → 更新ファイルごとに、そのパスのみのキャッシュを削除します
1.のパターンでは、パスごとに削除すると、Lambdaの実行回数が多く、同時実行数の上限に達したり、料金が高くなることがあります。
2.のパターンでは、パスごとに削除しても1.のデメリットがありません。また、削除する必要のないキャッシュは残るメリットがあります。
1.のパターンのコードはこちらです。
環境変数には、CloudFrontのディストリビューションIDをDISTRIBUTION_ID
として設定しています。
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))
client = boto3.client('cloudfront')
invalidation = client.create_invalidation(DistributionId=DISTRIBUTION_ID,
InvalidationBatch={
'Paths': {
'Quantity': 1,
'Items': ['/*']
},
'CallerReference': str(time.time())
})
開発中は、ログを残すため、print
を使っていますが、本番時は削除してかまいません。
2.のパターンのコードはこちらです。
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を指定
あとは、Lambdaのコンソール画面からトリガーを追加
をクリックし、S3のイベント通知を設定します。
オプションのSuffix
は、イベント通知する拡張子を指定できます。
これで、S3にファイルをアップロードしたり、削除すると、Lambdaが起動し、cloudfrontのキャッシュを削除します。