LoginSignup
1
5

More than 3 years have passed since last update.

AWS S3にデプロイしたら自動的にCloudFrontのキャッシュを無効化(invalidation)するNode.js Lambda

Posted at

概要

静的コンテンツをAWS S3に置いて、CloudFrontを前段に立てる構成になっている状態で、S3を更新してもCloudFrontのエッジサーバーのキャッシュは自動で更新されずキャッシュの有効期限中は反映されない
そのキャッシュを手動で無効化する運用フローになっているのを自動化する手順のメモ
ロールを作るところからnode.jsのLambdaでやる構成での記事がなかったように思うので、残しておく

構成

最初はデプロイスクリプトから CloudFrontのAPIを直に叩いて実現しようかと思った

しかしLambdaがS3の追加削除イベントをトリガーにできるようなので、タイミングとしてそちらのほうが適切であり、更にAWS SDKを使った方が楽だと分かったのでそのように構成した
スクリプトはフロントエンド エンジニアが管理しやすいようにNode.js javascriptにした

手順

IAM ロールを作る

LambdaがCloudFrontのCreateInvalidationを叩けるように適当な名前でIAM Roleを作る

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "cloudfront:CreateInvalidation",
            "Resource": "*"
        }
    ]
}

Lambdaのロールに適用する

上で作ったロールを適用

スクリーンショット 2020-02-24 16.11.15.png

Lambdaのトリガーを追加する

反応させたいS3 Bucketごとに、 すべてのオブジェクト作成イベントオブジェクトの削除(すべて) の2つのトリガーを作る

スクリーンショット 2020-02-24 16.20.10.png
スクリーンショット 2020-02-24 16.20.20.png

Lambdaのコードを書く

インライン編集、Node.jsを選ぶ
スクリーンショット 2020-02-24 15.49.30.png


const AWS = require("aws-sdk");
const cloudfront = new AWS.CloudFront({ apiVersion: "2019-03-26" });

// @see https://kotororo.net/2018/07/%E9%9D%99%E7%9A%84%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84%E3%82%92%E6%9B%B4%E6%96%B0%E3%81%97%E3%81%9F%E3%81%A8%E3%81%8D%E3%81%AB%E5%8D%B3%E6%99%82%E5%8F%8D%E6%98%A0%E3%81%95%E3%82%8C%E3%82%8B%E3%82%88%E3%81%86%E3%81%ABcloudfront%E3%81%AEinvalidation%E3%82%92%E8%B5%B0%E3%82%89%E3%81%9B%E3%82%8Blambda%E9%96%A2%E6%95%B0/
exports.handler = async event => {
  const paths = getPathsFromEvent(event);
  const s3BucketToCloudFrontDistributionId = {
    "S3のバケット名": "CloudFront Distribution ID",
  };
  const distributionId = s3BucketToCloudFrontDistributionId[getS3BucketFromEvent(event)];
  const params = {
    DistributionId: distributionId,
    InvalidationBatch: {
      CallerReference: new Date().getTime().toString(),
      Paths: paths
    }
  };

  // @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CloudFront.html#createInvalidation-property
  await cloudfront
    .createInvalidation(params, function(err, data) {
      if (err) {
        console.log(err, err.stack);
      } else {
        console.log(data);
      }
    })
    .promise();
  console.log("invalidate: " + paths.Items[0]);
};

function getPathsFromEvent(event) {
  console.log(JSON.stringify(event));
  return {
    Quantity: 1,
    Items: ["/" + event.Records[0].s3.object.key]
  };
}

function getS3BucketFromEvent(event) {
  return event.Records[0].s3.bucket.name;
}

※ 複数の開発環境に反応させるために、 s3BucketToCloudFrontDistributionId というマッピングにしてあるので、自分の環境の内容を入れる

動作確認

S3に追加削除して、そのパスがInvalidationされていたら成功

いくつまで一度に無効化できるのか?

もともと * でinvalidationしていたのだが、今回の変更でS3のイベントが各々のオブジェクトの追加削除ごとに発生し、invalidationも1パスずつリクエストされるようになった
もし大量のイベントが発生したときに問題なく動くのか気になり、いくつまで一度にinvaidationできるのか調べた

FAQ によると以下の通りなので、通常の使用であれば問題なさそうだった

Q.無効リクエスト数には制限がありますか?

オブジェクトを個別に無効にする場合は、進行中のディストリビューションごとに最大 3,000 個のオブジェクトまで、一度に無効化リクエストを作成できます。これは、最大 3,000 個のオブジェクトに対する 1 つの無効化リクエスト、1 つのオブジェクトに対する最大 3,000 個のリクエスト、または 3,000 個のオブジェクトを超えないその他の任意の組み合わせとすることができます。

* ワイルドカードを使用している場合、最大 15 個の無効化パスのリクエストを一度に作成できます。また、進行中のディストリビューションごとに最大 3,000 個の個別のオブジェクトを同時に作成することができます。ワイルドカードの無効化リクエストの制限は、オブジェクトの個別の無効化の制限とは無関係です。この限度を超過すると、以前のリクエストが終了するまで、余分な無効リクエストはエラーを返します。

困ったこと

保留したこと

  • Lambdaのインラインコードをgit・S3管理すること
  • 請求への注意・請求アラームをつけること

静的コンテンツ自体をそんなに頻繁に更新しないので保留した

参考

1
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
5