AWS CDK S3+CloudFrontウェブサイト構築 Typescript #2
はじめに
以前、AWSCDKでS3静的ウェブサイトホスティング構築をしました。
今回はAWSが提供するCDNサービスであるCloudFrontをS3の前に配置してCloudFront経由でS3静的サイトを配信できるようにしていきます。
CloudFrontを使用する理由
グローバルへの高速なコンテンツ配信
AWSのCDN(コンテンツ配信ネットワーク)であるCloudFrontを使用すると、S3に格納されたコンテンツはアクセス元から最も近い場所(エッジロケーション)から配信されます。
これによりコンテンツを高速に配信することが可能になります。
コスト効率性
S3バケットはインターネットへのコンテンツ配信に対して料金が発生します。
コンテンツへのアクセスが増加すればするほど料金が高くなります。
CloudFrontには配信したコンテンツをキャッシュに保持しておく機能がありキャッシュされた内容へのアクセスにはS3への問い合わせを行うことなくレスポンスすることができます。
これによりS3(オリジンサーバー)へのアクセスを削減することができコスト軽減が可能になります。
S3への直接的なアクセスの防止
クライアントから見た場合にCloudFrontがS3の前に配置されることでクライアントがコンテンツにアクセスする場合にはCloudFrontに対してのアクセスを実施することになります。
これにより直接S3バケットのURLを叩いてアクセスされるリスクを回避することができます。
前述の特徴と重複しますが、S3への直接的なアクセス防止は集中アクセスによるS3料金の増幅を回避できます。
(S3単体にはレート制限機能等のDos攻撃への対抗策が無い)
S3単体ではHTTPS通信に対応していない。
CloudFrontを使用することでHTTPSを使用したセキュアな通信が可能になります。
一元的なログ取得の実現
コンテンツへのアクセスをCloudFront経由に限定することでログをCloudFrontに集約することができます。
S3への直接的なアクセスを許可してしまうとCloudFront側のログに記録されないものが出てしまいます。
構築
今回はS3の前にCloudFrontを配置しCloudFrontからのコンテンツ配信を可能とします。
まずはS3バケットを非公開にします。
今回使用するCloudFrontのモジュールをimportに追加しておきます。
前回記述していたwebsiteIndexDocumentがCloudFront側に設定するためS3バケット側では不要となります。
その他修正するポイントは以下コードを参照してください。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import {
aws_s3 as s3,
aws_s3_deployment as s3_deployment,
aws_cloudfront as cloudfront,
aws_cloudfront_origins as cloudfront_origins,
} from 'aws-cdk-lib';
export class S3CloudfrontStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const s3Bucket = new s3.Bucket(this, 'WebSiteBucket', {
bucketName: `s3-bucket-${cdk.Aws.ACCOUNT_ID}-${cdk.Aws.REGION}`,
autoDeleteObjects: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
// S3バケットのパブリックアクセスを拒否(デフォルト)
publicReadAccess: false,
websiteIndexDocument: 'index.html',
// S3へのパブリックアクセスを全て拒否(デフォルト)
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
});
これでS3バケットがパブリックに公開されなりました。
なのでこれ以降はS3の静的ウェブサイトホスティング機能ではなくCloudFrontの機能としてコンテンツを配信していくことになります。
OAC(オリジンアクセスコントロール)という機能を使うことでS3バケットへのアクセスをCloudFront経由に限定することができます。
OACの名前にあるとおりCloudFrontを経由して配信されるコンテンツをオリジンと呼びます。
CloudFrontではオリジンや配信におけるキャッシュポリシーなどの設定をひとまとめにディストリビューションとして扱います。
CDKではOAC、ディストリビューション、オリジンを定義してCloudFrontを構築していきます。
const oac = new cloudfront.S3OriginAccessControl(this, 'OAC', {
originAccessControlName: `${id}-oac`,
// 認証の設定
// SIGV4:認証プロトコル(現状唯一)
// ALWAYS/NO_OVERRIDE:CloudFrontが認証するリクエストの選定
signing: cloudfront.Signing.SIGV4_ALWAYS,
})
const distribution = new cloudfront.Distribution(this, 'Distribution', {
// CloudFrontディストリビューションのデフォルト動作を各種設定
defaultBehavior: {
// オリジンの指定
origin: cloudfront_origins.S3BucketOrigin.withOriginAccessControl(
s3Bucket,{
originAccessLevels: [cloudfront.AccessLevel.READ],
},
),
},
// S3からではなくCloudFrontから配信するのでこちらでrootオブジェクトを指定
defaultRootObject: 'index.html',
});
デプロイ後、CloudFrontのディストリビューションドメイン名を使用してサイトにアクセスしてみます。


本来、CloudFront経由でS3のコンテンツを配信する場合はS3バケットポリシーでCloudFrontからのS3バケット接続を許可する必要があります。
本コードではCDK側がよしなにバケットポリシーを記述してくれているので配信ができています。
おわり
今回はS3静的ウェブサイトホスティングで配信していたコンテンツをCloudFront経由でHTTPS配信できるようにしました。
CloudFront経由の配信はAWSがドメインを証明してくれるので独自ドメインを取得せずともHTTPS配信ができます。
次回以降では独自ドメインを取得しRoute53やACMと連携させていきたいと思います。
それでは、ここまでお読みいただきありがとうございました。
ソースコード
参考
Discussion