AWS cdkを使ってS3+Cloudfrontでのstatic website hostingを構築してみる
やりたいこと
- 本番環境とステージング環境を作成
- ステージング環境にはlambda@edgeを利用してbasic認証を設定する
構成
- AWS S3
- 静的ホスティング機能を利用
- オリジンアクセスコントロール (OAC) 有効
- Cloudfront
- SSL有効
- 証明書はACMで取得済みとする
- CNAME設定
- Lambda@edge
- STG環境のみ
- Cloudfrontへのアクセスに対し、basic認証をかける
- viewer request
CDKの環境を構築する。
cdk toolkitのインストール
npm install -g aws-cdk
❯ cdk --version
2.118.0 (build a40f2ec)
プロジェクトディレクトリの作成
mkdir cdk && cd cdk
cdk init app --language typescript
以下のような構造のディレクトリが生成される。
❯ tree -I node_modules
.
├── README.md
├── bin
│ ├── cdk.d.ts
│ ├── cdk.js
│ └── cdk.ts
├── cdk.json
├── jest.config.js
├── lib
│ ├── cdk-stack.d.ts
│ ├── cdk-stack.js
│ └── cdk-stack.ts
├── package-lock.json
├── package.json
├── test
│ ├── cdk.test.d.ts
│ ├── cdk.test.js
│ └── cdk.test.ts
└── tsconfig.json
3 directories, 15 files
AWSのドキュメントが自動翻訳されてひどいことになっている。
lib/cdk-stack.ts
に書いていけばいいっぽい。
とりあえずS3 bucketとCloudfront distributionを作ってみる。
const bucket = new s3.Bucket(this, "deployBucket", {
publicReadAccess: true,
});
// cloudfront
const distribution = new cloudfront.Distribution(this, "Distribution", {
defaultBehavior: { origin: new origins.S3Origin(bucket) },
defaultRootObject: "index.html",
errorResponses: [
{
httpStatus: 404,
responseHttpStatus: 200,
responsePagePath: "/index.html",
},
],
});
書いたら、
npm synth
でスタックが生成される。
デプロイしてみる
まずはbootstrapという作業が必要らしい。
cdk bootstrap --profile [PROFILE]
cloudformation Stackが作られる。
そしたらデプロイする。
cdk deploy --profile [PROFILE]
bucketpolicyの作成に失敗。
修正が必要っぽい。
blockPublicAccess: BlockPublicAccess.BLOCK_ACLS,
を追加する必要があるらしい。
const bucket = new s3.Bucket(this, "deployBucket", {
publicReadAccess: true,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ACLS,
});
修正して cdk deploy
デプロイ中の表示がかっこいい。
デプロイ完了。 ✨ Total time: 253.58s
ちゃんとS3 bucketもできている。
Cloudfront distributionもできている。
S3に手動で index.html
をアップロードし、見られることを確認した。
スタック名を変えたいので、一回スタックを削除する。
cdk destroy --profile [PROFILE]
took 3m40
ちゃんといろいろ消えた。
S3だけ消えない(DeletionPolicy: Retainになっている)ので、手動で消す。
スタック名は bin/cdk.ts
の方にある。
bin/cdk.ts
から lib/cdk-stack.ts
を呼び出してるだけ。
複数環境にデプロイしたい。
- DevStack, ProdStackなど、別のスタックインスタンスを定義する方法
- 環境変数でenvを定義する
- Contextでenvを定義する
環境変数で指定するのが楽じゃね?
const stage = process.env.STAGE || "dev";
cdk deploy
と
STAGE=prod cdk deploy
で分ける感じ。
basic認証を仕掛ける
lambda@edgeを使う。
lambdaをデプロイする
cdkにはLambdaをデプロイする仕組みがあるっぽい。
LambdaをTypeScirptで書いて、CDKでサクッとデプロイする方法|F Lab|Fixel株式会社
Lambda at edgeへの応用はこれ
Basic Authの実装はこれを見る
[AWS] CloudFrontでBASIC認証を行う - Lambda@Edge + Node.js - ねこの足跡R
aws_lambda_nodejs
を使う方法だと、 cloudfront.experimental.EdgeFunction
が使えない?
素直に、us-east-1用にstackを作る。
cf. AWS CDKで別リージョンに基本認証用Lambda@Edgeを作成するスタックをデプロイしてAmazon CloudFrontに設定する - NRIネットコムBlog
crossRegionReferences
を利用して、値を受け渡す
cf. AWS CDK(Typescript) で LambdaEdge を使う #TypeScript - Qiita
できた!
Lambda@edgeの更新ができない問題
更新すると更新でエラーが出る。
23:20:19 | UPDATE_FAILED | Custom::CrossRegionExportWriter | ExportsWriterapnor...t12334E1B81D43DF3F Received response status [FAILED] from custom resource. Message returned: Error: Exports cannot be updated:
ロールバックもできなくなってしまった。
Cross origin referenceがよくわからない。
Lambda用のarnが変わってしまうのが悪いのかな。
lambda関数に
currentVersionOptions: { removalPolicy: cdk.RemovalPolicy.RETAIN, },
を指定したが、解決はせず。
cloudfront.experimental.EdgeFunction
を使う。
これを使うのが、いろいろやってくれて楽だろう。
残念ながら aws-lambda-nodejs
はサポートされていない。
CI/CDをする
Github ActionsからCDできるようにする
GithubのOIDCを使い、特定のリポジトリからのS3/Cloudfrontアクセスを許可する。
一旦全てを破壊する。
cdk destroy
ちなみに以下のエラーで一回必ず失敗する。
Lambda was unable to delete arn:aws:lambda:...:1 because it is a replicated function.
Lambda@edgeに割り当てた関数は、Cloudfront distributionを削除してから時間が経つまで削除できないため。
レプリカは通常、数時間以内に削除されます。Lambda@Edge 関数のレプリカを手動で削除することはできません。
Lambda@Edge 関数とレプリカの削除 - Amazon CloudFront