【VueSlsApp】アプリをデプロイするたびにCloudFrontのキャッシュを自動的に削除してみた

こんにちは。thiroyoshiです。

今回はgithub actionを使ってCloudFrontのキャッシュを自動削除する話です。以前書いた話で、CloudFrontを使うようにアプリを修正したので、それを補完する話になります。

CloudFrontがキャッシュを持ってくれることはすごくありがたいのですが、アプリを新たにデプロイする時には毎回キャッシュを削除しないと、古いアプリのまま使われてしまいます。

そのために、アプリをデプロイするたびにキャッシュを自動的に削除します。主に使うツールは、以下の二つです。

・github action
・AWS CLI

実はいつもgithub action使ってデプロイしていて、今回workflowのymlをリポジトリに追加してあります。CI/CDの全体像はぜひリポジトリをご覧ください。

そもそもCloudFrontのキャッシュ削除は、正式には”ファイルの無効化”という

公式のドキュメントは以下です。

CloudFrontのキャッシュを削除することを、公式にはファイルの無効化と言ってます。無効化は英語でinvalidationというので、常にコマンドではXX-invalidationsのようになってます。

マネジメントコンソールでは以下の画面です。

コメント 2020-05-04 061826

ファイル無効化は、無効化リクエストを作成する、という言い方をするようで、以下のような画面で削除するパスを指定して、無効化のリクエストをします。

Inkedコメント 2020-05-04 061513_LI

結構細かくパスも指定できますが、今回はアプリをリリースする話なので、すべて消すように"/*"設定します。

これをgithub actionでAWS CLIのコマンドから実行していきます。

AWS CLIを使ったキャッシュ削除コマンドの要件を整理する

キャッシュ削除は、AWS CLIの ↓ のコマンドでができます。

コマンドの仕様を見ると以下のようになっています。

 create-invalidation
--distribution-id <value>
[--invalidation-batch <value>]
[--paths <value>]
[--cli-input-json <value>]
[--generate-cli-skeleton <value>]

つまり、このコマンドを使うにはDistribution ID(CloudFrontを識別するID)が必要になります。またここでは明示されてないですが、"--paths"も必須です。

Distribution IDはデプロイするたびに変わるので、github actionの設定に書き込もうとすると、CloudFrontを一度デプロイしてから編集する必要があり面倒です。そのため、これもAWS CLIで取得するようにします。

しかし、Distribution IDを取得しようとしたとき、このアプリの構成上初回のデプロイではCloudFrontが存在しないので、Distribution IDが取得できません。

↑ の記事にあるように、VueSlsAppの本体のあとにCloudFrontをデプロイすることになるからです。そのため、Distribution IDが取得できなかった場合を考慮した例外処理が必要です。

つまり、要件をまとめると以下のようになります。

・AWS CLIコマンドの"create-invalidation"を使用してキャッシュ削除する
・キャッシュ削除範囲は、すべてのパス(/*)
・Distribution IDもAWS CLIコマンドを使用して取得する
・コマンドはgithub actionで毎回実行する
・初回デプロイ時にはDistribution IDが取得できなくてもgithub actionを失敗にしない(かつ、取得できなかったことがわかるメッセージを出す)

結果を先に示すと、以下のようなコマンドになりました。

json=`aws cloudfront list-distributions --query DistributionList.Items[]`
Id=`echo $json | jq -r '.[] | select(.Comment=="vueslsapp") | .Id'`
if [ -n "$Id" ] ;then aws cloudfront create-invalidation --distribution-id $Id --paths "/*";else echo "Not found the Distribution."; fi

人によっては、1行目と2行目を合わせてワンライナーにすると思います。ワンライナーってかっこいいのですが、私はワンライナーの可読性の低さがあまり好きではないので意図的に分けています。

では、以下から順番にコマンドを見ていきます。

CloudFrontのDistributionListを取得する

まずDistribution(CloudFrontの単位)のリストを取得します。

よく見ると、これにはec2のコマンドにあるような"--filter"オプションがありません…。そのため"--query"オプションを使って可能な限り必要なリストのみを取得します。この辺りは以下の記事が参考になりました。

それをもとにすると、以下のようなコマンドになります。

aws cloudfront list-distributions --query DistributionList.Items[]

実行結果は長いですが、以下のようになります。

thiroyoshi@thiroyoshi-W10:~$ aws cloudfront list-distributions --query DistributionList.Items[]
[
   {
       "Status": "Deployed",
       "Comment": "vueslsapp",
       "ViewerCertificate": {
           "SSLSupportMethod": "sni-only",
           "Certificate": "arn:aws:acm:us-east-1:XXXXXXXX:certificate/XXXXXX",
           "CertificateSource": "acm",
           "MinimumProtocolVersion": "TLSv1",
           "ACMCertificateArn": "arn:aws:acm:us-east-1:XXXXXX:certificate/XXXXXX"
       },
       "Origins": {
           "Quantity": 1,
           "Items": [
               {
                   "Id": "dev-vueslsapp.thiroyoshi.com",
                   "S3OriginConfig": {
                       "OriginAccessIdentity": "origin-access-identity/cloudfront/XXXXXXXXXXX"
                   },
                   "DomainName": "dev-vueslsapp.thiroyoshi.com.s3.amazonaws.com",
                   "CustomHeaders": {
                       "Quantity": 0
                   },
                   "OriginPath": ""
               }
           ]
       },
       "Enabled": true,
       "WebACLId": "",
       "DefaultCacheBehavior": {
           "MaxTTL": 86400,
           "TrustedSigners": {
               "Enabled": false,
               "Quantity": 0
           },
           "ViewerProtocolPolicy": "redirect-to-https",
           "SmoothStreaming": false,
           "DefaultTTL": 3600,
           "ForwardedValues": {
               "QueryString": true,
               "Cookies": {
                   "Forward": "none"
               },
               "QueryStringCacheKeys": {
                   "Quantity": 0
               },
               "Headers": {
                   "Quantity": 0
               }
           },
           "MinTTL": 0,
           "TargetOriginId": "dev-vueslsapp.thiroyoshi.com",
           "AllowedMethods": {
               "CachedMethods": {
                   "Quantity": 2,
                   "Items": [
                       "HEAD",
                       "GET"
                   ]
               },
               "Quantity": 2,
               "Items": [
                   "HEAD",
                   "GET"
               ]
           },
           "Compress": false
       },
       "CustomErrorResponses": {
           "Quantity": 2,
           "Items": [
               {
                   "ErrorCachingMinTTL": 0,
                   "ResponseCode": "200",
                   "ErrorCode": 403,
                   "ResponsePagePath": "/index.html"
               },
               {
                   "ErrorCachingMinTTL": 0,
                   "ResponseCode": "200",
                   "ErrorCode": 404,
                   "ResponsePagePath": "/index.html"
               }
           ]
       },
       "Aliases": {
           "Quantity": 1,
           "Items": [
               "vueslsapp.thiroyoshi.com"
           ]
       },
       "CacheBehaviors": {
           "Quantity": 0
       },
       "Id": "AAAAAAAAAAA",
       "ARN": "arn:aws:cloudfront::XXXXXXXXXXX:distribution/AAAAAAAAAAA",
       "DomainName": "dzk869wnnytdm.cloudfront.net",
       "IsIPV6Enabled": true,
       "PriceClass": "PriceClass_200",
       "Restrictions": {
           "GeoRestriction": {
               "RestrictionType": "none",
               "Quantity": 0
           }
       },
       "HttpVersion": "HTTP2",
       "LastModifiedTime": "2020-04-25T21:35:03.593Z"
   }
]

大事なのは、上記の結果のなかの "Id": "AAAAAAAAAAA"の部分を抽出することです。

上記の結果は要素数が一つの配列です。今はDistributionが一つだからなのですが、複数になると要素数が増えるので目当てのDistributionを抽出する必要があります。この抽出のための"--filter"が使えなかったので次の手順のコマンドに任せます。

そこで次の行に結果を渡すため、jsonという変数に結果を代入しています。

json=`aws cloudfront list-distributions --query DistributionList.Items[]`

目当てのDistribution IDを抽出する

先の手順で得られたものは、jsonの形式になっていますので、jqコマンドを使っていきます。

私の環境はWindowsですが、あとでgithub actionに組み込むので、WSL(Windows Subsystem for Linux)でUbuntuを起動し、そこでコマンドチェックしていきました。

できたコマンドは以下の通りですが、ポイントは"select"を使っているところです。

echo $json | jq -r '.[] | select(.Comment=="vueslsapp") | .Id

jqコマンドにはselectクエリがあり、条件で指定の配列の要素を抽出できます。すごい。

どんな条件でもいいと思いますが、私の場合Distributionのコメントとしてアプリ名を入れていたので、今回はそれを抽出条件にしました。もっとスマートにやるなら、Tagsとか使えるといいかもしれませんが、今回はこれで十分でした。

ちなみに、jqコマンドについてお世話になったのは以下の記事です。

最後に、次のコマンドに抽出できたDistribution IDを渡す必要があるので、変数に代入していきます。

Id=`echo $json | jq -r '.[] | select(.Comment=="vueslsapp") | .Id'`

キャッシュ削除をリクエストする(Distribution IDがなければメッセージ出力)

AWS CLIでキャッシュ削除を実行していきます。

しかし、もし上記まででCloudFrontがデプロイされていなければ、変数$Idが空文字になります。そのため、コマンド実行前に空文字判定を入れてしまえば初回のCloudFrontがない場合の対処もできます。bashの空文字判定は以下のようにしてできます。

if [ -n "$Id" ] ;then <do something>;else <do something>; fi

"-n"は対象が空文字でなければTrueを返します。つまり、thenのあとにAWS CLIコマンドを、elseのあとにメッセージ出力を入れます。

結果、以下の通りになります。

if [ -n "$Id" ] ;then aws cloudfront create-invalidation --distribution-id $Id --paths "/*";else echo "Not found the Distribution."; fi

ここまで作ってきたコマンドは、ローカルでも十分実行可能ですが、今回はgithub actionで実施したのでその実装についてもう少し触れます。

github actionでキャッシュ削除コマンドを実装する

github actionでAWS CLIを使うためには、AWS CLIのインストールとアクセスキーを環境変数に設定することが必要です。

対象の部分だけ抽出すると、以下のようなスクリプトになります。

   - name: Setup AWS CLI
     uses: chrislennon/action-aws-cli@v1.1
   - name: Remove Cache
     run: |
       json=`aws cloudfront list-distributions --query DistributionList.Items[]`
       Id=`echo $json | jq -r '.[] | select(.Comment=="vueslsapp") | .Id'`
       if [ -n "$Id" ] ;then aws cloudfront create-invalidation --distribution-id $Id --paths "/*";else echo "Not found the Distribution."; fi
     env:
       AWS_DEFAULT_REGION: ap-northeast-1
       AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
       AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

まずAWS CLIをインストールするために、以下を呼び出します。

uses: chrislennon/action-aws-cli@v1.1

これでAWS CLIがインストールされます。便利…!

次に、実装したコマンドを実行しますが、AWS CLIを使うためにクレデンシャル情報を環境変数に入れる必要があります。それをenvで設定します。

このとき、クレデンシャル情報は大事な情報なので、github actionのSecretsの機能で安全に設定しましょう。

これでgithub actionでキャッシュ削除が自動化が完成です!

最後に

今回はCloudFrontを使うときには必要になるキャッシュ削除を自動化してみました。

CloudFrontのコマンドに"--filter"がなくてちょっと苦労しましたが、そこそこすっきりとしたコマンドに収まってホッとしています…。

それでは、よい自動削除ライフを!

この記事が気に入ったら、サポートをしてみませんか?
気軽にクリエイターの支援と、記事のオススメができます!
note.user.nickname || note.user.urlname

読んでいただいてありがとうございます!サポートもありがたいですが、Githubのスターを付けてもらえるともっとありがたいです!

ありがとうございます!フォローもお願いします!
2
自分で考えて実装したアプリと技術ネタ、健康ネタ、仕事術を記事にしていきます。#これからの仕事術コンテストグランプリ受賞 本業エンジニア。本格筋トレ歴1年。サラリーマンエンジニア歴8年。ハッカソン受賞歴多数。AWSとPythonとサーバーレスは至高の組み合わせ。

こちらでもピックアップされています

VueSlsAppの使い方
VueSlsAppの使い方
  • 7本

Vue.jsとServerless Frameworkで作ったサーバーレスなアプリの解説記事をまとめてます。

コメントを投稿するには、 ログイン または 会員登録 をする必要があります。