S3 が更新されたら Lambda で CloudFront のキャッシュをパージする

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 sourceS3 (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 ...
タイトルとURLをコピーしました