- 1. 解説動画
- 2. 概要
- CloudFront
- S3 (Simple Storage Service)
- 構成図
- CloudFrontの動作
- リソース一覧
- 公式URL
- 3. 解説手順
- 4. CFnテンプレート
- CloudFront, S3
- 5. 予習
- 6. FAQ(随時追加)
CloudFrontとS3について学んでいきましょう!
1. 解説動画
※動画の画質が悪い場合は、歯車から720p以上の画質を選択ください
2. 概要
CloudFront
【CloudFrontとは】
CloudFrontはCDN(Content Delivery Network; コンテンツ配信ネットワーク)と呼ばれるサービスです。
グローバルエッジネットワーク https://aws.amazon.com/jp/cloudfront/features/ より抜粋(2024年1月時点)
上の図のように世界中に置かれたエッジロケーションからHTTPの静的・動的コンテンツが配信されます。「配信」のイメージを具体的に図示しました(フリーハンドの線が微妙ですが 笑)
CloudFrontを使う前は直接ALB =https://viaelb.[ドメイン名]
に接続していました。世界中のクライアントはインターネットを経由して、東京リージョンのALBに接続するわけです。
一方、CloudFrontを使って viacdn.[ドメイン名]
に接続する場合、世界中のクライアントは一番地理的に近いエッジロケーションに接続できます。エッジロケーションからALBまではAWSの中のネットワークを使いますので、長距離の通信(黒矢印)がインターネットより高速です。
【CloudFront導入の目的】
ただクライアントも、エッジロケーションも、ALBやS3も全部日本国内の場合は高速になりません。元々速いので...。なので実は他の目的があります。
- IPv6にチェック1つで簡単に対応できる(VPCがIPv6非対応でもOK)
- WAF(Web Application Firewall)を使えばIPアドレスやパスやブラウザなどでフィルタができる
- S3に置いた静的ファイルやリダイレクトをALBやEC2とは別に(つまり負荷をかけずに)公開できる
- キャッシュを使えばさらに高速になる
ALB以外にS3のコンテンツもCloudFrontに設定したドメイン viacdn.[ドメイン名]
で公開できます。つまりパスによって静的コンテンツはS3、動的コンテンツはALBという使い分けができるようになります。CloudFrontのネットワークがオリジン(=S3やALB)にあるコンテンツを配信してくれるので、CDN(コンテンツ配信ネットワーク)と呼ばれるわけです。
今回のCFnテンプレートでは実装していませんが、もし「キャッシュ」も有効にすると、特定のファイルを特定の期間、エッジロケーションに保存しておくことができます。クライアントからのリクエストは以後「キャッシュ」を返すだけで完結しますので、長距離の通信もなくなり、さらに高速になります。
S3 (Simple Storage Service)
AWSで一番最初にリリースされたサービスです。多数のファイル(S3ではObjectと呼ぶ)をバケット(Bucket)で保管できます。プロパティ設定(Properties)、権限設定(Permissions)がかなり多くなっています。一例を挙げると...
【プロパティ設定の一例】
- Versioning:オブジェクトのバージョンを保持できる。誤って上書きしてしまった場合に元に戻せる
- Encryption:オブジェクトを暗号化できる
- Server access logging:オブジェクト操作のアクセスログを取得できる
- Event notifications:オブジェクトの作成・変更・削除などのイベントをトリガーに、Lambda関数(=Node.jsやPythonで書いたコード)を実行できる
- Static website hosting:バケットをWebサイトとして直接公開できる
【権限設定の一例】
- Block public access:オブジェクトを誤って公開状態にするのをブロックできる。AWSアカウント全体のブロックも可能
- Bucket policy:オブジェクトへのアクセス権限を設定できる。特にバケットが存在するAWSアカウント以外からのリクエストを受け付けるかどうか設定できる
- Cross-origin resource sharing(CORS):CORSはWebサイトを安全に運用するための仕組み。その設定ができる
構成図
CloudFrontとS3(3つ)を追加しています。CloudFrontからはALB、S3②リダイレクト用、S3③静的コンテンツ用に振り分けます。
今回作成するCloudFrontとS3の構成図はこちらです。 ※小さくて見づらいので、右クリックして新しいタブで開いてください。
CloudFrontの動作
CloudFront Distributionを作る際、Cache Behaviors(キャッシュ動作)とOrigins(オリジン)を設定する。今回は下記表の通り、パスごとにオリジン(=振り分け先)を指定している。
Precedence (優先度) | Path Pattern | Origin |
---|---|---|
0 |
| S3-Website (Redirections) |
1 |
| S3-Website (Redirections) |
2 |
| S3 (StaticContents) |
3 |
| S3 (StaticContents) |
4 |
| S3 (StaticContents) |
5 | Default ( | ELB (ALB) |
まとめると次のような動きとなる。
/favicon.ico
と/
:リダイレクト用のS3バケットで、応答を返す(=リダイレクト)/css/*
と/images/*
と/js/*
:静的コンテンツ用のS3バケットで、応答を返す- それ以外のパス
*
:ALBで、応答を返す
リソース一覧
Name | 目的 | 備考 |
---|---|---|
SecretsManager: Secret | CloudFrontで使う「シークレット」の安全な保管 | CloudFrontがクライアントからのリクエストをALBに振り分ける際、HTTPヘッダーに |
S3: Bucket | ファイル(S3ではオブジェクトと言う)を保存できる「バケット」 | バケット名は世界中で一意(=唯一)でないといけない。そのためアカウントIDをバケット名に入れている。今回は①CloudFrontのアクセスログ出力用 ②CloudFrontのリダイレクト用 ③CloudFrontで配信する静的コンテンツ用 の3つのバケットを作成する |
CloudFront: OriginAccessControl | CloudFront経由だと分かるようにするための「OAC オリジンアクセスコントロール」 | ALBの時はHTTPヘッダーを使ったが、③静的コンテンツ用S3バケットの場合はOACがCloudFront経由のリクエストと分かる目印になる。残念ながら②リダイレクト用S3バケット(静的ウェブサイト)の場合はOACが使えないので注意 |
CloudFront: Function | CloudFrontのViewer側で実行する「関数」 | TypeScriptでコードを記述し、Behaviorに紐付けて使う。Viewer側でリクエストやレスポンスの加工などを簡単に行うことができる |
CloudFront: Distribution | CloudFrontでコンテンツを配信するための「ディストリビューション(=配信)」 | ディストリビューションを作成して、コンテンツ配信の元(=オリジン)、振り分け方などを設定する。CloudFrontの本体 |
ElasticLoadBalancingV2: ListenerRule | リスナーの挙動(条件とアクションのセット)を定義できる「リスナールール」 | 今回は「404 Not Found」のリスナールールを追加する |
S3: BucketPolicy | バケットのアクセス権限を制御するための「バケットポリシー」 | S3のポリシーは様々あるが、今回はクライアントがCloudFront経由で ③静的コンテンツ用S3バケットを参照するために必要。このようにバケットポリシーではAWSアカウント外からのファイル(オブジェクト)操作について許可設定ができる |
Route53: RecordSet | Route 53のHostedZone(簡単に言うとドメイン)に追加する「レコードセット」 | ①ドメイン→IPv4アドレス に変換するためのAレコード ②ドメイン→IPv6アドレス に変換するためのAAAAレコード を追加する。追加するとブラウザ等から、受講生が所有するドメインでアクセスできるようになる。今回はCloudFrontのエイリアス(別名)のレコードを追加する |
公式URL
CloudFront
- 解説: Amazon CloudFront とは何ですか? - Amazon CloudFront
- CFn: CloudFront リソースタイプのリファレンス - AWS CloudFormation
- CLI: cloudfront — AWS CLI v2 Command Reference
S3
- 解説: Amazon S3 とは - Amazon Simple Storage Service
- CFn: Amazon Simple Storage Service リソースタイプのリファレンス - AWS CloudFormation
- CLI: s3 — AWS CLI v2 Command Reference
3. 解説手順
- CFnでCloudFrontとS3を作成する ※今回はRDS,EC2を使いませんので停止したままでOKです
- 動作確認を実施する
※Windowsの方はCloudShellか、EC2にSessionManagerで繋いで実行してください
curl -v http://cdn.[ご自身のドメイン名]/
※httpでリクエストcurl -v https://cdn.[ご自身のドメイン名]/
※httpsでリクエストcurl -v https://cdn.[ご自身のドメイン名]/favicon.ico
※特定パスにリクエストcurl -v https://cdn.[ご自身のドメイン名]/images/favicon.ico
※特定パスにリクエストcurl -v https://cdn.[ご自身のドメイン名]/users
※特定パスにリクエストcurl -k -v https://[上記コマンド実行結果 1行目のIPv4アドレス]/
※IPアドレスでリクエスト ※1行目がIPv6アドレスの場合はnslookup cdn.[ご自身のドメイン名]
でIPv4アドレスを調べる ※応答内容はどこかにコピペして保存しておいてください 👉 宿題に出ます
4. CFnテンプレート
CloudFront, S3
- [SystemName]=
awsmaster
をお好きなシステム名に置換してください。変えなくてもOKです。 - [Acm Certificate Arn] を以前③でバージニア北部リージョンに作ったACMのARNに置換してください。
Stack Name: [SystemName]-prod-cloudfront File Name: [SystemName]/cloudfront.yml
---
### [Change Acm Certificate Arn] arn:aws:acm:us-east-1:[xxxxxxxxxxxx]:certificate/[xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx]
### [Change System Name] awsmaster
## The following CFn stack must be created first in order to be referenced by the ImportValue function.
## 1. ${SystemName}-${Environment}-route53
## 2. ${SystemName}-${Environment}-elb
AWSTemplateFormatVersion: "2010-09-09"
Description: Create CloudFront, S3 etc.
Mappings:
EnvironmentMap:
prod:
### [Change Acm Certificate Arn] CloudFront certificate needs to be created in the us-east-1 region.
AcmCertificateArn: arn:aws:acm:us-east-1:[xxxxxxxxxxxx]:certificate/[xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx]
## CloudFront WebACL needs to be created in the us-east-1 region.
# WebACLArn: arn:aws:wafv2:us-east-1:[xxxxxxxxxxxx]:global/webacl/awsmaster-prod-webacl/[xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx]
stg:
AcmCertificateArn: arn:aws:acm:us-east-1:[xxxxxxxxxxxx]:certificate/[xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx]
# WebACLArn: arn:aws:wafv2:us-east-1:[xxxxxxxxxxxx]:global/webacl/awsmaster-stg-webacl/[xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx]
dev:
AcmCertificateArn: arn:aws:acm:us-east-1:[xxxxxxxxxxxx]:certificate/[xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx]
# WebACLArn: arn:aws:wafv2:us-east-1:[xxxxxxxxxxxx]:global/webacl/awsmaster-dev-webacl/[xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx]
Parameters:
SystemName:
Description: System Name
Type: String
Default: awsmaster ### [Change System Name]
Environment:
Description: Environment
Type: String
Default: prod
AllowedValues:
- prod
- stg
- dev
SubDomain:
Description: Sub Domain
Type: String
Default: cdn
AllowedPattern: ^[^.]*$
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Environment Configuration"
Parameters:
- SystemName
- Environment
- SubDomain
Resources:
## Secrets Manager: Secret (x-via-cloudfront)
## If the request has an x-via-cloudfront header, ELB can be authenticated as a request via CloudFront.
## The Secret is not named, because it takes at least 7 days to delete.
SecretForCloudFront:
Type: AWS::SecretsManager::Secret
Properties:
Description: !Sub Secret for CloudFront (${AWS::StackName})
GenerateSecretString:
SecretStringTemplate: "{}"
GenerateStringKey: x-via-cloudfront
ExcludePunctuation: true
PasswordLength: 128
## S3: Bucket (Access Logs)
S3BucketAccessLogs:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${SystemName}-${Environment}-cloudfront-accesslogs-${AWS::AccountId}
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
OwnershipControls:
Rules:
- ObjectOwnership: ObjectWriter
PublicAccessBlockConfiguration:
BlockPublicAcls: true
IgnorePublicAcls: true
BlockPublicPolicy: true
RestrictPublicBuckets: true
## S3: Bucket (Redirections)
S3BucketRedirections:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${SystemName}-${Environment}-cloudfront-redirections-${AWS::AccountId}
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
OwnershipControls:
Rules:
- ObjectOwnership: BucketOwnerEnforced
PublicAccessBlockConfiguration:
BlockPublicAcls: true
IgnorePublicAcls: true
BlockPublicPolicy: true
RestrictPublicBuckets: true
## Static website hosting
WebsiteConfiguration:
IndexDocument: index.html
RoutingRules:
- RoutingRuleCondition:
KeyPrefixEquals: docs/
RedirectRule:
HostName: !Sub
- "${SubDomain}.${DomainName}"
- DomainName:
Fn::ImportValue: !Sub ${SystemName}-${Environment}-route53-HostedZoneDomainName
HttpRedirectCode: "301"
Protocol: https
ReplaceKeyPrefixWith: documents/
- RoutingRuleCondition:
KeyPrefixEquals: favicon.ico
RedirectRule:
HostName: !Sub
- "${SubDomain}.${DomainName}"
- DomainName:
Fn::ImportValue: !Sub ${SystemName}-${Environment}-route53-HostedZoneDomainName
HttpRedirectCode: "302"
Protocol: https
ReplaceKeyWith: images/favicon.ico
## S3: Bucket (Static Contents)
S3BucketStaticContents:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${SystemName}-${Environment}-cloudfront-staticcontents-${AWS::AccountId}
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
OwnershipControls:
Rules:
- ObjectOwnership: BucketOwnerEnforced
PublicAccessBlockConfiguration:
BlockPublicAcls: true
IgnorePublicAcls: true
BlockPublicPolicy: true
RestrictPublicBuckets: true
## CloudFront: Origin Access Control
OriginAccessControl:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: !Sub ${SystemName}-${Environment}-cloudfront-oac
Name: !Sub ${SystemName}-${Environment}-cloudfront-oac
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
## CloudFront: Function
Function:
Type: AWS::CloudFront::Function
Properties:
Name: !Sub ${SystemName}-${Environment}-cloudfront-function
FunctionConfig:
Comment: !Sub ${SystemName}-${Environment}-cloudfront-function
Runtime: cloudfront-js-2.0
FunctionCode: |
async function handler(event) {
const request = event.request;
const host = request.headers.host.value;
const newurl = `https://${host}/users/`;
const response = {
statusCode: 302,
statusDescription: 'Found',
headers: {
'location': { value: newurl }
}
};
return response;
}
AutoPublish: true
## CloudFront: Distribution
Distribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
## Use North America, Europe, Asia, Middle East, and Africa
PriceClass: PriceClass_200
# WebACLId: !FindInMap [ EnvironmentMap, !Ref Environment, WebACLArn ]
Aliases:
- !Sub
- "${SubDomain}.${DomainName}"
- DomainName:
Fn::ImportValue: !Sub ${SystemName}-${Environment}-route53-HostedZoneDomainName
ViewerCertificate:
AcmCertificateArn: !FindInMap [ EnvironmentMap, !Ref Environment, AcmCertificateArn ]
SslSupportMethod: sni-only
MinimumProtocolVersion: TLSv1.2_2021
HttpVersion: http2and3
Logging:
Bucket: !GetAtt S3BucketAccessLogs.DomainName
Prefix: logs/
IncludeCookies: true
IPV6Enabled: true
Comment: !Sub ${SystemName}-${Environment}-cloudfront
Enabled: true
Origins:
## ELB
- DomainName:
Fn::ImportValue: !Sub ${SystemName}-${Environment}-elb-LoadBalancerDNSName
Id: !Sub ELB-${SystemName}-${Environment}-alb
CustomOriginConfig:
OriginSSLProtocols:
- TLSv1.2
OriginProtocolPolicy: https-only
OriginReadTimeout: 60
OriginKeepaliveTimeout: 5
HTTPSPort: 443
OriginCustomHeaders:
- HeaderName: x-via-cloudfront
HeaderValue: !Sub "{{resolve:secretsmanager:${SecretForCloudFront}:SecretString:x-via-cloudfront}}"
## S3: Static Contents
- DomainName: !GetAtt S3BucketStaticContents.RegionalDomainName
Id: !Sub S3-${S3BucketStaticContents}
S3OriginConfig:
OriginAccessIdentity: ''
OriginAccessControlId: !GetAtt OriginAccessControl.Id
## S3 Website: Redirections
- DomainName: !Select [ 1, !Split [ "://", !GetAtt S3BucketRedirections.WebsiteURL ] ]
Id: !Sub S3-Website-${S3BucketRedirections}
CustomOriginConfig:
OriginProtocolPolicy: http-only
OriginReadTimeout: 30
OriginKeepaliveTimeout: 5
HTTPPort: 80
DefaultCacheBehavior:
## ELB
TargetOriginId: !Sub ELB-${SystemName}-${Environment}-alb
Compress: true
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
- OPTIONS
- PUT
- POST
- PATCH
- DELETE
CachedMethods:
- GET
- HEAD
## Ref. https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad ## Managed-CachingDisabled
## Ref. https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/using-managed-origin-request-policies.html
OriginRequestPolicyId: 216adef6-5c7f-47e4-b989-5492eafa07d3 ## Managed-AllViewer
## Ref. https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/using-managed-response-headers-policies.html
ResponseHeadersPolicyId: 67f7725c-6f97-4210-82d7-5512b31e9d03 ## Managed-SecurityHeadersPolicy
CacheBehaviors:
## S3 Website: Redirections
- PathPattern: /
TargetOriginId: !Sub S3-Website-${S3BucketRedirections}
Compress: true
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad ## Managed-CachingDisabled
OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf ## Managed-CORS-S3Origin
ResponseHeadersPolicyId: 67f7725c-6f97-4210-82d7-5512b31e9d03 ## Managed-SecurityHeadersPolicy
FunctionAssociations:
- EventType: viewer-request
FunctionARN: !GetAtt Function.FunctionARN
- PathPattern: /docs/*
TargetOriginId: !Sub S3-Website-${S3BucketRedirections}
Compress: true
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad ## Managed-CachingDisabled
OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf ## Managed-CORS-S3Origin
ResponseHeadersPolicyId: 67f7725c-6f97-4210-82d7-5512b31e9d03 ## Managed-SecurityHeadersPolicy
- PathPattern: /favicon.ico
TargetOriginId: !Sub S3-Website-${S3BucketRedirections}
Compress: true
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 ## Managed-CachingOptimized
OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf ## Managed-CORS-S3Origin
ResponseHeadersPolicyId: 67f7725c-6f97-4210-82d7-5512b31e9d03 ## Managed-SecurityHeadersPolicy
## S3: Static Contents
- PathPattern: /documents/*
TargetOriginId: !Sub S3-${S3BucketStaticContents}
Compress: true
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad ## Managed-CachingDisabled
OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf ## Managed-CORS-S3Origin
ResponseHeadersPolicyId: 67f7725c-6f97-4210-82d7-5512b31e9d03 ## Managed-SecurityHeadersPolicy
- PathPattern: /images/*
TargetOriginId: !Sub S3-${S3BucketStaticContents}
Compress: true
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 ## Managed-CachingOptimized
OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf ## Managed-CORS-S3Origin
ResponseHeadersPolicyId: 67f7725c-6f97-4210-82d7-5512b31e9d03 ## Managed-SecurityHeadersPolicy
## ELB: Listener Rule (404 Not Found)
ELBListenerRuleNotFound:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
ListenerArn:
Fn::ImportValue: !Sub ${SystemName}-${Environment}-elb-ListenerHttps
Priority: 200
Conditions:
- Field: http-header
HttpHeaderConfig:
HttpHeaderName: x-via-cloudfront
Values:
- !Sub "{{resolve:secretsmanager:${SecretForCloudFront}:SecretString:x-via-cloudfront}}"
Actions:
- Type: fixed-response
FixedResponseConfig:
StatusCode: "404"
ContentType: text/html
MessageBody: |
<html>
<head>
<title>404 Not Found</title>
<link rel="icon" href="/images/favicon.ico">
</head>
<body>
<center><h1>404 Not Found</h1></center>
</body>
</html>
## S3: Bucket Policy (Static Contents)
S3BucketPolicyStaticContents:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref S3BucketStaticContents
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: s3:GetObject
Principal:
Service: cloudfront.amazonaws.com
Resource: !Sub ${S3BucketStaticContents.Arn}/*
Condition:
StringEquals:
AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${Distribution}
## Route 53: Record Set (IPv4)
Route53RecordSetIPv4:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId:
Fn::ImportValue: !Sub ${SystemName}-${Environment}-route53-HostedZone
Name: !Sub
- "${SubDomain}.${DomainName}."
- DomainName:
Fn::ImportValue: !Sub ${SystemName}-${Environment}-route53-HostedZoneDomainName
Type: A
AliasTarget:
## HostedZoneId of cloudfront.net; Ref. https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/quickref-route53.html#w2aac27c21c80c11
HostedZoneId: Z2FDTNDATAQYW2
DNSName: !GetAtt Distribution.DomainName
## Route 53: Record Set (IPv6)
Route53RecordSetIPv6:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId:
Fn::ImportValue: !Sub ${SystemName}-${Environment}-route53-HostedZone
Name: !Sub
- "${SubDomain}.${DomainName}."
- DomainName:
Fn::ImportValue: !Sub ${SystemName}-${Environment}-route53-HostedZoneDomainName
Type: AAAA
AliasTarget:
## HostedZoneId of cloudfront.net; Ref. https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/quickref-route53.html#w2aac27c21c80c11
HostedZoneId: Z2FDTNDATAQYW2
DNSName: !GetAtt Distribution.DomainName
Outputs:
## Secrets Manager: Secret (x-via-cloudfront)
SecretForCloudFront:
Value: !Ref SecretForCloudFront
Export:
Name: !Sub ${AWS::StackName}-SecretForCloudFront
## S3: Bucket (Access Logs)
S3BucketAccessLogs:
Value: !Ref S3BucketAccessLogs
Export:
Name: !Sub ${AWS::StackName}-S3BucketAccessLogs
S3BucketAccessLogsArn:
Value: !GetAtt S3BucketAccessLogs.Arn
Export:
Name: !Sub ${AWS::StackName}-S3BucketAccessLogsArn
S3BucketAccessLogsDomainName:
Value: !GetAtt S3BucketAccessLogs.DomainName
Export:
Name: !Sub ${AWS::StackName}-S3BucketAccessLogsDomainName
S3BucketAccessLogsDualStackDomainName:
Value: !GetAtt S3BucketAccessLogs.DualStackDomainName
Export:
Name: !Sub ${AWS::StackName}-S3BucketAccessLogsDualStackDomainName
S3BucketAccessLogsRegionalDomainName:
Value: !GetAtt S3BucketAccessLogs.RegionalDomainName
Export:
Name: !Sub ${AWS::StackName}-S3BucketAccessLogsRegionalDomainName
## S3: Bucket (Redirections)
S3BucketRedirections:
Value: !Ref S3BucketRedirections
Export:
Name: !Sub ${AWS::StackName}-S3BucketRedirections
S3BucketRedirectionsArn:
Value: !GetAtt S3BucketRedirections.Arn
Export:
Name: !Sub ${AWS::StackName}-S3BucketRedirectionsArn
S3BucketRedirectionsDomainName:
Value: !GetAtt S3BucketRedirections.DomainName
Export:
Name: !Sub ${AWS::StackName}-S3BucketRedirectionsDomainName
S3BucketRedirectionsDualStackDomainName:
Value: !GetAtt S3BucketRedirections.DualStackDomainName
Export:
Name: !Sub ${AWS::StackName}-S3BucketRedirectionsDualStackDomainName
S3BucketRedirectionsRegionalDomainName:
Value: !GetAtt S3BucketRedirections.RegionalDomainName
Export:
Name: !Sub ${AWS::StackName}-S3BucketRedirectionsRegionalDomainName
S3BucketRedirectionsWebsiteURL:
Value: !GetAtt S3BucketRedirections.WebsiteURL
Export:
Name: !Sub ${AWS::StackName}-S3BucketRedirectionsWebsiteURL
## S3: Bucket (Static Contents)
S3BucketStaticContents:
Value: !Ref S3BucketStaticContents
Export:
Name: !Sub ${AWS::StackName}-S3BucketStaticContents
S3BucketStaticContentsArn:
Value: !GetAtt S3BucketStaticContents.Arn
Export:
Name: !Sub ${AWS::StackName}-S3BucketStaticContentsArn
S3BucketStaticContentsDomainName:
Value: !GetAtt S3BucketStaticContents.DomainName
Export:
Name: !Sub ${AWS::StackName}-S3BucketStaticContentsDomainName
S3BucketStaticContentsDualStackDomainName:
Value: !GetAtt S3BucketStaticContents.DualStackDomainName
Export:
Name: !Sub ${AWS::StackName}-S3BucketStaticContentsDualStackDomainName
S3BucketStaticContentsRegionalDomainName:
Value: !GetAtt S3BucketStaticContents.RegionalDomainName
Export:
Name: !Sub ${AWS::StackName}-S3BucketStaticContentsRegionalDomainName
## CloudFront: Origin Access Control
OriginAccessControl:
Value: !Ref OriginAccessControl
Export:
Name: !Sub ${AWS::StackName}-OriginAccessControl
## CloudFront: Function
Function:
Value: !GetAtt Function.FunctionARN
Export:
Name: !Sub ${AWS::StackName}-FunctionARN
## CloudFront: Distribution
Distribution:
Value: !Ref Distribution
Export:
Name: !Sub ${AWS::StackName}-Distribution
DistributionDomainName:
Value: !GetAtt Distribution.DomainName
Export:
Name: !Sub ${AWS::StackName}-DistributionDomainName
DistributionAlternateDomainName:
Value:
Fn::ImportValue:
!Sub ${SystemName}-${Environment}-route53-HostedZoneDomainName
Export:
Name: !Sub ${AWS::StackName}-DistributionAlternateDomainName
## ELB: Listener Rule (404 Not Found)
ELBListenerRuleNotFound:
Value: !Ref ELBListenerRuleNotFound
Export:
Name: !Sub ${AWS::StackName}-ELBListenerRuleNotFound
## Route 53: Record Set (IPv4)
Route53RecordSetIPv4:
Value: !Ref Route53RecordSetIPv4
Export:
Name: !Sub ${AWS::StackName}-Route53RecordSetIPv4
## Route 53: Record Set (IPv6)
Route53RecordSetIPv6:
Value: !Ref Route53RecordSetIPv6
Export:
Name: !Sub ${AWS::StackName}-Route53RecordSetIPv6
【変更履歴】
- 2021-07-20:MinimumProtocolVersionを
TLSv1.2_2019
からTLSv1.2_2021
に変更 - 2021-10-05:HTTPヘッダーで使う
x-pre-shared-key
の名前が分かりづらいため、x-via-cloudfront
に変更 - 2022-01-10:CloudFrontに関連づけるWAFをWAFv1からWAFv2に変更(ただしコメントアウト状態なので実際は使っていない)
- 2022-04-27:S3バケットで
OwnershipControls
の設定を追加(アクセスログ用はACL有効のままで、他はACL無効に変更) - 2022-09-02:HTTP/3対応を追加
- 2022-11-30:
HostedZoneId
にコメント追加、不要なWebsiteURL
Outputsの削除、OAI(OriginAccessIdentity
)からOAC(OriginAccessControl
)に移行 - 2023-03-26:
ResponseHeadersPolicyId
の設定を追加 - 2023-04-06:冒頭でImportValueについてのコメント補足追加
- 2023-04-14:コメント文を全般的に修正
- 2023-05-26:勉強のため
/favicon.ico
のみキャッシュを有効化 - 2023-10-07:
PublicAccessBlockConfiguration
の順番を変更(画面に合わせた) - 2025-01-11:
- 説明の都合上、Cache Behaviorの設定を変更
- S3 Bucket Policyで
s3:ListBucket
の許可を取り消す - 2025-01-12:CloudFront Functionによるリダイレクトを追加。それに伴い、S3によるリダイレクトの設定を変更
5. 予習
- 解説動画の通り、設定・作成してください ※CFnでエラーになった場合は、EventsタブのスクリーンショットをSlackでください (AWSの画面が変わっている場合は同じ設定項目を探してください)
- CFnで作成したCloudFrontとS3を手でも作ってください。作成中に分からなかった設定項目については調べたり、質問のためにメモしておいてください
(同じドメインでは重複して作れないので別のサブドメインでお願いします。例えばCFnの
cdn.[ご自身のドメイン名]
に対してcdn2.[ご自身のドメイン名]
のように) (手順が分かりづらいので、分からない場合はSlackかZoomでお答えします) - 【宿題】下記の項目について調べて、次回のZoomで説明してください (公式の情報が優先で、分からない場合はブログ等を参照ください)
- CloudFrontとALBの両方で
x-via-cloudfront
が設定されている箇所を探してください。x-via-cloudfront
がどう使われているか分かりますか? - CloudFrontと静的コンテンツ用S3の両方で
OAC(Origin Access Control)
が設定されている箇所を探してください。OAC
がどう使われているか分かりますか? - CloudFront・S3の設定と挙動を理解するために、解説手順の動作確認でアクセスした6回のリクエストについて、各応答結果を上記の表にまとめてください。①HTTPリクエストはAWSの中でどう通って、②最終的に誰(=Server)が、③どんなHTTP応答(=HTTPステータスコード)を返したか? ④それらの設定箇所はAWSコンソール画面でどこにあるか? を調べてください。分かりづらいかもしれないので、一部だけ答えを書いておきました。ちなみにキャッシュは存在しない前提でご回答ください
- S3バケット
[SystemName]-prod-cloudfront-staticcontents-[AWSアカウントID]
の中にimages
フォルダーを作成し、favicon.ico
ファイルをアップロードしてください ※faviconは https://favicon.io/ でImageやTextから生成するか、Emojiをダウンロードして頂くとfavicon.ico
が入手できます - 上記4.が完了しましたら、上記の表の一番下にある行を埋めてください。また
https://cdn.[ご自身のドメイン名]/
にブラウザでアクセスしてみて favicon が使われていること(ブラウザのタブに表示されること)を確認ください - SSL Labs の SSL Server Test (https://www.ssllabs.com/ssltest/) で公開したCloudFrontのドメイン(cdn.[ご自身のドメイン名])を入力してSSLの安全性をチェックする。Do not show the results on the boards に必ずチェック☑ を入れること。入れないとドメインが公開されてしまう。
- Submitボタンを押した後に表示されるServerはCloudFrontのIPアドレスである。ドメイン名からDNS名前解決した結果だが、IPv4とIPv6の両方に対応するためにはどういう設定が必要か?(確認後、どれか適当に1つをクリックする)
- TLSのバージョンは何が有効か?(ELBで有効だったバージョンは何か?)
- CloudFrontの料金体系
- S3の料金体系 ※S3ストレージクラスのパフォーマンス↓にも目を通してください https://aws.amazon.com/jp/s3/storage-classes/
- S3静的ウェブサイトホスティングを有効にしたバケットは3つのうちどれか分かりますか?
またそのウェブサイトのURLも分かればブラウザや
curl
コマンドでアクセスしてみてください - CORS(Cross-Origin Resource Sharing) について調べて、概要を説明してください
- キャッシュの挙動を確認
curl -v https://cdn.[ご自身のドメイン名]/favicon.ico
コマンドを実行し、キャッシュが有効になっていることを確認してください- マネジメントコンソールで、キャッシュポリシーを変更せずに
/favicon.ico
のみキャッシュを削除してください - 削除完了後に
curl
コマンドを再度実行し、キャッシュが無効になっていることを確認してください - マネジメントコンソールで、キャッシュヒット率(リクエスト合計に占めるキャッシュがヒットした割合)をHourly(1時間ごと)で確認してください ※リクエスト結果が画面に表示されるまで、10分程度かかります
- 【応用課題】余裕のある方は実際に挑戦して頂くと勉強になると思います! ※時間の都合でZoomで解説はしませんので、答え合わせをされたい方はSlackでDMください
- 次の公式ブログに書かれているCloudFront Functionsを実際に試してみてください CloudFront Functions を使用して Amazon S3 の Amazon CloudFront オリジンでデフォルトディレクトリインデックスを実装 | Amazon Web Services ブログ
- Behaviorで使うCache PolicyとOrigin Request Policyについて調べてください(AWSで定義されたポリシーの種類や違い、自分で作る場合の方法など)
- Originにはドメイン名を設定しますので、今まで構築したEC2, ELB, RDS, S3のドメイン名(エンドポイントと呼びます)を調べてみてください。例えばEC2の場合は
ec2-52-194-141-28.ap-northeast-1.compute.amazonaws.com
のようなドメインですが、他のリソースも並べてみて共通点を探るとAWSで使われるドメイン名の理解が進むと思います - https://cdn.(ドメイン名)/images/favicon.ico にアクセスし、HTTP/3に対応していることをChromeのデベロッパーツールで確認してください(Chrome以外のブラウザにも同様の機能があります)
- Response Headers Policyの有無によってHTTPヘッダーがどう変わるのか、確認してください。具体的には
/favicon.ico
の場合と(⑪ECSや⑫Fargateまで構築後に)/users/
の場合を確認してください - Response Headers Policyによって追加された各セキュリティヘッダーの意味を調べてください
- Chromeなどブラウザの「デベロッパーツール(検証)」でHTTPレスポンスヘッダーを見て、HTTP圧縮されているか否かの見分け方を調べてください。HTTP圧縮を有効にするパターンが CloudFront のファイル圧縮に関する問題をトラブルシューティングする | AWS re:Post に記載されているので、それに合わせてDistributionの設定変更やファイルを準備し、実際にHTTP圧縮が有効になっていることを確認してください
$ curl -v http://cdn.[ご自身のドメイン名]/
(中略)
< HTTP/1.1 301 Moved Permanently
< Server: CloudFront
< Date: Thu, 12 Dec 2024 16:13:49 GMT
< Content-Type: text/html
< Content-Length: 167
< Connection: keep-alive
< Location: https://cdn.[ご自身のドメイン名]/
< X-Cache: Redirect from cloudfront
< Via: 1.1 13c972ba4a9ffc82bb0bdabc148e3d20.cloudfront.net (CloudFront)
< X-Amz-Cf-Pop: KIX56-P1
< Alt-Svc: h3=":443"; ma=86400
< X-Amz-Cf-Id: t18BHRXuneH-vcidybhDtJo7KEKjkUiDVL-DoKOQNXrYFU0Y35rEtQ==
< X-XSS-Protection: 1; mode=block
< X-Frame-Options: SAMEORIGIN
< Referrer-Policy: strict-origin-when-cross-origin
< X-Content-Type-Options: nosniff=
段階 | URL | HTTP応答 | Server | 経路 | 設定箇所 |
---|---|---|---|---|---|
CloudFront
作成後 |
| 301 ※上記赤字 | CloudFront ※上記赤字 | (リクエストがServerにたどり着くまでの流れ) | (左記の経路にするための設定はどこに?) |
(同上) |
| ||||
(同上) |
| ||||
(同上) |
| ||||
(同上) |
| ||||
(同上) |
| なし(TLSエラーで中断) | なし(TLSエラーで中断) | ||
下記4.を実施後 |
|
6. FAQ(随時追加)
/images/favicon.ico
をS3バケットに置いてもブラウザのタブでアイコンが表示されませんどうやら /images/favicon.ico
をS3バケットに置いても存在しなかった状態がブラウザにキャッシュされて、変更が反映されないようです。お手数ですが、表示されない場合はChrome「ゲストモード」や他のブラウザ(Firefox, Opera, Safari, Edge etc.) でアクセスしてみてください。アイコンが表示されると思います。
https://[ご自身のドメイン名]/images/favicon.ico
にアクセスしたところ、次のような表示になりました。このエラーはどういう意味ですか?<Error>
<Code>NoSuchKey</Code>
<Message>The specified key does not exist.</Message>
<Key>images/favicon.ico</Key>
(以下略)
これはS3が表示したエラー文言です。S3はバケットの中にオブジェクト(要はファイルのこと。ファイル形式でなくても良いのでオブジェクトと呼ぶ)を保管できますが、そのオブジェクト名(要はパスのこと)をキー「Key」と読んでいます。
今回の場合は images/favicon.ico
がキーです。そのキー(=パス)にオブジェクト(=ファイル)が存在しない場合は、S3の仕様として NoSuchKey
= The specified key does not exist.
=「その Key は存在しない」という言い方になります。
余談になりますが、S3バケットに「フォルダ」や「ディレクトリ」という概念は存在しません。ただ画面上ではそれらがあるように見えているので紛らわしいのですが...。存在するのは images/favicon.ico
のように、オブジェクト名にあたる「Key」だけです。
S3は各リソースのタグと同じように「Key」と「Value」を保管するための場所と捉えれば、なぜファイルのパスのことを「Key」と呼ぶのか、理解できると思います。
キーの仕様については公式ドキュメント オブジェクトキー名の作成 - Amazon Simple Storage Service を参照ください。
S3バケット内のファイルを公開する場合には必要です。
公式ドキュメント (チュートリアル: Amazon S3 での静的ウェブサイトの設定 - Amazon Simple Storage Service) に静的ウェブサイトホスティングを使って公開する場合の手順が記載されており、ステップ4でバケットポリシーの設定をしています。
今回はあくまで「リダイレクトルール」しか使わずファイルの公開はないため、バケットポリシーの設定が不要です。
The S3 bucket that you specified for CloudFront logs dones not enable ACL access
) が表示されます。どうすれば良いですか?2021年12月からS3バケット作成時に「ACLs disabled (recommended)」「ACL無効 (推奨)」という設定がデフォルトになりました。詳しくは 【アップデート】S3でACLを無効化できるようになりました #reinvent | DevelopersIO をご覧ください。
通常はそれで問題ないのですが、CloudFrontのアクセスログ用S3バケットについては、ACLを有効にする必要があります。ACLでログ出力の権限を設定しているためです。
バケット作成後に変更もできますので、エラーになった場合は「ACLs enabled」「ACL 有効」に変更してください。詳しくは CloudFrontのアクセスログ保存用S3バケットにはACL有効化が必要なので注意しよう | DevelopersIO をご覧ください。
↓この箇所ですね。
PublicAccessBlockConfiguration:
BlockPublicAcls: true
IgnorePublicAcls: true
BlockPublicPolicy: true
RestrictPublicBuckets: true
私も正直良く分からず、とりあえず全部ブロックすれば安全ということで避けてました(笑)。調べたところ、大変分かりやすい記事がありましたのでご覧ください → S3のブロックパブリックアクセスが怖くなくなった【AWS S3】
①場所と数が異なります。例えば2021年7月現在の南米大陸では次の図の通りです。水色がエッジロケーション、オレンジ色がリージョンを指しています。エッジロケーションの方が多く、より身近な存在だと分かります。リージョンの2倍以上あり、最新の数については AWS グローバルインフラストラクチャ | AWS をご覧ください。
②配置されるAWSのサービスが異なります。エッジロケーションには次の図の通りRoute 53, CloudFrontなどが配置され、EC2, VPC, RDS, S3などはリージョンに配置されます。エッジロケーションほどユーザーの身近にありますので、高速な応答が求められる軽めのサービスが配置されています。
データセンターの場所は秘密にされることが多いです。それが2018年にWikiLeaksによってAWSのデータセンター(エッジロケーションとアベイラビリティゾーン)の所在地が暴露されましたので、ご参考まで → WikiLeaksがAWSのデータセンター所在地を暴露したので詳細を見る - Qiita データセンター銀座と呼ばれる千葉県印西市に集中していますね。
以上、お疲れさまでした!