はじめに
Angular な SPA を Amazon S3 + CloudFront にホストするのはいいですが、じゃあデプロイはどうするのとなって行き詰まっていました。
いろいろ調べてみたところ、Angular の SPA を自動でデプロイして CloudFront のキャッシュもクリアする方法が分かったので、メモ的な位置づけでその方法を残しておきます。
なお、環境は以下の通りで、AWS SDK はすでにインストール済みであることを前提にしています。
- Windows 10
- Angular 7.0.0
- Node 10.12.0
- Visual Studio Code
Angular プロジェクトの作成
適当な場所で、以下のコマンドを実行して Angular プロジェクトを作成します。
$ ng new SampleProject
$ cd SampleProject
必要なパッケージのインストール
S3 へのアップロード、および CloudFront のキャッシュをクリアするのに必要なパッケージをインストールします。
$ npm install aws-sdk --save-dev $ npm install mime-types --save-dev $ npm install node-uuid --save-dev
デプロイスクリプトの作成
プロジェクトのルートパスにscript
フォルダを作成し、そのフォルダの下にdeploy.js
を新規作成します。
そして、以下のコードを貼り付け、config の設定を書き換えます。
基本的に config を書き換えればうまくいくと思うのでコピペでもいいと思いますが、詳細を確認したい場合は参考サイトを参照してください。
なお、アクセスキー ID とシークレットアクセスキーは、外部に漏洩しないよう厳重に管理してください。
const AWS = require("aws-sdk"); // imports AWS SDK const mime = require('mime-types') // mime type resolver const fs = require("fs"); // utility from node.js to interact with the file system const path = require("path"); // utility from node.js to manage file/folder paths var uuid = require('node-uuid'); // configuration necessary for this script to run const config = { s3BucketName: '{S3バケット名}', folderPath: '../dist/{プロジェクト名}', // path relative script's location accessKeyId: '{アクセスキーID}', // IAM user's AccessKeyID secretAccessKey: '{シークレットアクセスキー}', // IAM user's SecretAccessKey DistributionId: '{CloudFrontのID}' // DistributionId of CloudFront }; /* S3 のファイルアップロード設定 */ // initialise S3 client const s3 = new AWS.S3({ signatureVersion: 'v4' }); // resolve full folder path const distFolderPath = path.join(__dirname, config.folderPath); // Normalize \\ paths to / paths. function unixifyPath(filepath) { return process.platform === 'win32' ? filepath.replace(/\\/g, '/') : filepath; }; // Recurse into a directory, executing callback for each file. function walk(rootdir, callback, subdir) { // is sub-directory const isSubdir = subdir ? true : false; // absolute path const abspath = subdir ? path.join(rootdir, subdir) : rootdir; // read all files in the current directory fs.readdirSync(abspath).forEach((filename) => { // full file path const filepath = path.join(abspath, filename); // check if current path is a directory if (fs.statSync(filepath).isDirectory()) { walk(rootdir, callback, unixifyPath(path.join(subdir || '', filename || ''))) } else { fs.readFile(filepath, (error, fileContent) => { // if unable to read file contents, throw exception if (error) { throw error; } // map the current file with the respective MIME type const mimeType = mime.lookup(filepath) // build S3 PUT object request const s3Obj = { // set appropriate S3 Bucket path Bucket: isSubdir ? `${config.s3BucketName}/${subdir}` : config.s3BucketName, Key: filename, Body: fileContent, ContentType: mimeType } // upload file to S3 s3.putObject(s3Obj, (res) => { console.log(`Successfully uploaded '${filepath}' with MIME type '${mimeType}'`) }) }) } }) } /* CloudFrond のキャッシュクリア設定*/ var cloudfront = new AWS.CloudFront({ apiVersion: '2018-06-18', accessKeyId: config.accessKeyId, secretAccessKey: config.secretAccessKey, }); var timestamp = new Date(); var string_timestamp = String(timestamp.getTime()); var invalidate_items = ['/*']; // 全キャッシュクリア var item_count = invalidate_items.length var params = { DistributionId: config.DistributionId, InvalidationBatch: { CallerReference: string_timestamp, Paths: { Quantity: item_count, Items: invalidate_items } } }; // S3にファイルアップロード実行 walk(distFolderPath, (filepath, rootdir, subdir, filename) => { console.log('Filepath', filepath); }); // CloudFront キャッシュクリア実行 cloudfront.createInvalidation(params, function(err, data) { if (err) console.log(err, err.stack); else console.log(data); });
デプロイの設定
pakage.json
のscripts
配下に、以下の2行を追加します(追加時に、一行前の末尾にカンマを付けるのを忘れないこと)。
"predeploy": "ng build --prod --aot", "deploy": "node ./scripts/deploy.js"
デプロイの実行
以下のコマンドを実行することで、ビルド&デプロイ、キャッシュのクリアまで行うことができます。
$ npm run deploy
おわりに
ざっくりとした感じですが、このスクリプトで実際に動作させて問題ないことは確認済みです。
本番では CodeCommit などの Code シリーズと組み合わせた方がよいのでしょうが、開発段階ではとりあえずこれでも問題ないのではないかと思います。
なお、CloudFront の無効化(キャッシュクリア)は、1000回/月 を超えると課金されるのでスクリプト利用時は気をつけてください。