BETA

S3にファイルがアップロードされた時にCloudFrontのキャッシュを削除してみる

投稿日:2018-11-08
最終更新:2018-11-08

やりたいこと

S3に置いた静的HTMLをCloudFrontを通してアクセスする、という形で配信しているシステムがあり、運用する中で静的HTMLを置き換えた時にすぐに切り替わるようにしたいということで、CloudFrontのキャッシュを都度削除する必要がありました。
ただし、毎日切り替わる代物なのでその都度手動でCloudFrontの特定Bucketに対してInvalideするのは面倒だなーと思ったので、それならファイルを置いた時に自動的にInvalidateが実行されるようにしたいというのが目的です。

どうやるか

さて、やり方ですが今回はLambdaを使って実現しました。

Lambdaのイベント発火条件として「S3のObjectCreatedByPut」を使います。 これでS3の指定したBucketにObjectが生成・更新された際にLambdaの関数が動いてくれます。

そして、以下のようなLambda関数を作成します。

package main

import (
    "time"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-sdk-go/service/cloudfront"
    "github.com/aws/aws-sdk-go/aws/session"
)

type S3 struct {
    Bucket string
    Key string
}

func invalidate(events events.S3Event) (string, error) {
    // S3のPUTイベントからレコード情報を取得
    s3 := &S3{}
    for _, event := range events.Records {
        s3.Bucket = event.S3.Bucket.Name
        s3.Key = event.S3.Object.Key
    }

    // awsへのセッションを作成
    s, err := session.NewSession()
    if err != nil {}

    // awsセッションからcloudfrontへのセッションを作成
    cf := cloudfront.New(s)

    // cloudfrontのdistributionのリストを作成
    o, err := cf.ListDistributions(&cloudfront.ListDistributionsInput{})
    if err != nil {}

    for _, item := range o.DistributionList.Items {
        for _, origin := range item.Origins.Items {

            // distributionのドメイン名がS3で作成されたレコードの所属するバケット名と一致するか確認
            if *origin.DomainName == s3.Bucket {
                callerReference := time.Now().Format(time.RFC3339)
                quantity := int64(1)
                filePath := "/" + s3.Key
                items := []*string{&filePath}

                // Invalidationするためのインプット情報を作成
                cii := &cloudfront.CreateInvalidationInput{
                    DistributionId: item.Id,
                    InvalidationBatch: &cloudfront.InvalidationBatch{
                        CallerReference: &callerReference,
                        Paths: &cloudfront.Paths {
                            Quantity: &quantity,
                            Items: items,
                        },
                    },
                }

                // Invalidationを実行
                _, err = cf.CreateInvalidation(cii)
                if err != nil {}
            }
        }
    }

    return "Done", nil
}

func main() {
    lambda.Start(invalidate)
}

簡単に説明すると、発火したイベント詳細を引数として受け、そこからバケット名を取得してCloudFrontのDistoributionのドメイン名と合致するものを探します。

見つかれば、そのDistributionに向かって CreateInvalidation 関数を使ってinvalidationを実行するだけです。非常に簡単ですね。

こんなにも簡単に作成出来てしまうのだからLambdaの力は凄いですね。 贅沢を言うとGoの設計テンプレートが全く無いので、AWSの中の人には頑張って追加して欲しいところ・・・

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@syossan27'の技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!