はじめに
まず、大前提として、ここから先は現在キャッチアップしているCloudFormationを使いながら、さまざまなコードを自分用に書いていきます。
※補足事項
そのため、内容は完全に自分向けとなっています。
また、このブログ投稿サイトのプラットフォームについても、自分自身の備忘録としてメモを残す場として活用していきたいと考えています。このプラットフォーム上に記録を積み重ねていく予定です。
あらかじめ、その点をご理解いただければ幸いです。
今回作成する予定のAWS構成図について
詳細は省略しますが、今回は受講しているITスクールで、以下のようなAWSインフラ構成をコード化しています。
Terraformによる実装も完了しており、以下の記事でコードの全体像をご紹介しています。
結論:先に成果物のご紹介
お名前.comで取得したドメイン名を使用してアクセスしたところ、以下のようにブラウザで正常に表示されることを確認しています。
また、今回作成したCloudFrontのディストリビューションドメイン名でも、以下のように正常に表示されることを確認しています。
今回追加で実装したRDSインスタンスについても、マネジメントコンソール上で正しく作成されていることを確認しました。
実際にコードで行っていること
-
VPC(仮想プライベートクラウド)の作成
- IPアドレス範囲(CIDRブロック)を「10.0.0.0/16」に設定。
- DNSサポートとDNSホスト名を有効化。
-
サブネットの作成
-
パブリックサブネット
- 2つのサブネットを作成(
10.0.0.0/20
、10.0.16.0/20
)。 - 各サブネットを異なるアベイラビリティゾーン(
ap-northeast-1a
、ap-northeast-1c
)に配置。
- 2つのサブネットを作成(
-
プライベートサブネット
- 4つのプライベートサブネットを作成(例:
10.0.128.0/20
など)。 - アベイラビリティゾーンごとに分けて配置。
- 4つのプライベートサブネットを作成(例:
-
パブリックサブネット
-
インターネットゲートウェイとNATゲートウェイの作成
- 外部インターネット通信を可能にするインターネットゲートウェイを作成。
- プライベートサブネットからインターネットアクセスを可能にするNATゲートウェイを設定。
-
ルートテーブルとルート設定
- 各サブネットに対応するルートテーブルを作成。
- パブリックサブネット: インターネットゲートウェイへのルートを設定。
- プライベートサブネット: NATゲートウェイを経由するルートを設定。
-
セキュリティグループの設定
- ECS用: アプリケーション用セキュリティグループを作成。
- ALB用: HTTPS通信(443ポート)を許可。
- RDS用: データベースアクセス(3306ポート)を許可。
-
ECSクラスターとサービスの作成
- Fargateを使用したECSクラスターを作成。
- タスク定義、コンテナ設定、ネットワーク構成を含むサービスを設定。
-
アプリケーションロードバランサー(ALB)の作成
- HTTPSリスナー(443ポート)を設定。
- ターゲットグループと接続。
-
RDS(データベース)インスタンスの作成
- MySQLエンジンを使用したRDSインスタンスをプライベートサブネットにデプロイ。
- 無料利用枠に対応した構成(
db.t4g.micro
インスタンス、20GBストレージ)。
-
S3バケットとCloudFrontの設定
- S3バケット: Reactアプリを格納予定の非公開バケットを作成。
- CloudFront: S3バケットをオリジンとするディストリビューションを設定。
- ウェブホスティング用に
index.html
をアップロードし、HTTPS経由でアクセス可能に。
注意点:HTMLファイルのアップロードは手動で対応
作成したテンプレートをCloudFormationで使用しようとしたところ、以下のエラーが発生しました。
Template format error: Unrecognized resource types: [AWS::S3::Object]
調べてみたところ、どうやらCloudFormationではAWS::S3::Objectリソースタイプがサポートされていないみたいです…
正直、これが本当なのか少し自信がないのですが、少なくとも自分の調査では直接アップロードする方法は見つかりませんでした。
そのため、以下のような方法で対応するしかないのかなと思っています。もし他に良い方法があれば教えてほしいところです…。
今回は、index.html
をS3バケットに手動でアップロードし、それをCloudFront経由で配信する形で対応しました。
アップロード後の画面
事前準備と補足事項
1. お名前.comで購入したドメインを使用してSSL証明書を取得する方法
AWS ACMとRoute53を利用してSSL証明書を取得する手順については、以下の記事をご参照ください:
2. ALBのAレコードをRoute53のホストゾーンに追加する方法
CloudFormationで自動デプロイ後、ALBのAレコードをRoute53に追加する手順については、以下の記事をご参照ください:
作ったコードについて
AWSTemplateFormatVersion: "2010-09-09"
Description: CloudFormation template for creating a VPC, subnets, NAT gateway, and related resources.
# 変数の定義
Parameters:
ACMCertificate:
Type: String
Default: "arn:aws:acm:ap-northeast-1:881490128743:certificate/83d4b57c-6b4f-4658-8fec-b826de14c5a9"
Description: "ARN of the ACM Certificate"
Resources:
# VPCの作成
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "10.0.0.0/16"
InstanceTenancy: "default"
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: "CloudFormation-vpc-vpc"
# パブリックサブネット1の作成
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: "10.0.0.0/20"
AvailabilityZone: "ap-northeast-1a"
Tags:
- Key: Name
Value: "CloudFormation-vpc-subnet-public1-ap-northeast-1a"
# パブリックサブネット2の作成
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: "10.0.16.0/20"
AvailabilityZone: "ap-northeast-1c"
Tags:
- Key: Name
Value: "CloudFormation-vpc-subnet-public2-ap-northeast-1c"
# プライベートサブネット1の作成
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: "10.0.128.0/20"
AvailabilityZone: "ap-northeast-1a"
Tags:
- Key: Name
Value: "CloudFormation-vpc-subnet-private1-ap-northeast-1a"
# プライベートサブネット2の作成
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: "10.0.144.0/20"
AvailabilityZone: "ap-northeast-1c"
Tags:
- Key: Name
Value: "CloudFormation-vpc-subnet-private2-ap-northeast-1c"
# プライベートサブネット3の作成
PrivateSubnet3:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: "10.0.160.0/20"
AvailabilityZone: "ap-northeast-1a"
Tags:
- Key: Name
Value: "CloudFormation-vpc-subnet-private3-ap-northeast-1a"
# プライベートサブネット4の作成
PrivateSubnet4:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: "10.0.176.0/20"
AvailabilityZone: "ap-northeast-1c"
Tags:
- Key: Name
Value: "CloudFormation-vpc-subnet-private4-ap-northeast-1c"
# インターネットゲートウェイの作成
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: "CloudFormation-vpc-igw"
# VPCへのインターネットゲートウェイのアタッチ
AttachInternetGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
# パブリックルートテーブルの作成
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: "CloudFormation-vpc-rtb-public"
# パブリックルートの設定
PublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref InternetGateway
# パブリックサブネット1をルートテーブルに関連付け
PublicRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable
# パブリックサブネット2をルートテーブルに関連付け
PublicRouteTableAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicRouteTable
# NATゲートウェイ用のElastic IPの作成
NatEIP:
Type: AWS::EC2::EIP
Properties:
Domain: "vpc"
Tags:
- Key: Name
Value: "CloudFormation-vpc-eip-ap-northeast-1a"
# NATゲートウェイの作成
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
SubnetId: !Ref PublicSubnet1
AllocationId: !GetAtt NatEIP.AllocationId
Tags:
- Key: Name
Value: "CloudFormation-vpc-nat-public1-ap-northeast-1a"
# プライベートルートテーブル1の作成
PrivateRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: "CloudFormation-vpc-rtb-private1-ap-northeast-1a"
# プライベートルート1の設定
PrivateRoute1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable1
DestinationCidrBlock: "0.0.0.0/0"
NatGatewayId: !Ref NatGateway
# プライベートサブネット1をルートテーブルに関連付け
PrivateRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref PrivateRouteTable1
# プライベートルートテーブル2の作成
PrivateRouteTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: "CloudFormation-vpc-rtb-private2-ap-northeast-1c"
# プライベートルート2の設定
PrivateRoute2:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable2
DestinationCidrBlock: "0.0.0.0/0"
NatGatewayId: !Ref NatGateway
# プライベートサブネット2をルートテーブルに関連付け
PrivateRouteTableAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet2
RouteTableId: !Ref PrivateRouteTable2
# プライベートルートテーブル3の作成
PrivateRouteTable3:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: "CloudFormation-vpc-rtb-private3-ap-northeast-1a"
# プライベートルート3の設定
PrivateRoute3:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable3
DestinationCidrBlock: "0.0.0.0/0"
NatGatewayId: !Ref NatGateway
# プライベートサブネット3をルートテーブルに関連付け
PrivateRouteTableAssociation3:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet3
RouteTableId: !Ref PrivateRouteTable3
# プライベートルートテーブル4の作成
PrivateRouteTable4:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: "CloudFormation-vpc-rtb-private4-ap-northeast-1c"
# プライベートルート4の設定
PrivateRoute4:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable4
DestinationCidrBlock: "0.0.0.0/0"
NatGatewayId: !Ref NatGateway
# プライベートサブネット4をルートテーブルに関連付け
PrivateRouteTableAssociation4:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet4
RouteTableId: !Ref PrivateRouteTable4
# ECS用セキュリティグループ
ECSGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Security group for ECS instances"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: "tcp"
FromPort: 1323
ToPort: 1323
SourceSecurityGroupId: !GetAtt PublicSG.GroupId
SecurityGroupEgress:
- IpProtocol: "-1"
FromPort: 0
ToPort: 0
CidrIp: "0.0.0.0/0"
Tags:
- Key: Name
Value: "CloudFormation-private-instance-sg"
# ECSクラスターの作成
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: "my-ecs-cluster"
# ECSタスク実行ロールの作成
ECSExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: "ecs-task-execution-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: "ecs-tasks.amazonaws.com"
Action: "sts:AssumeRole"
Policies:
- PolicyName: "ecs-task-execution-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "ecr:GetAuthorizationToken"
- "ecr:GetDownloadUrlForLayer"
- "ecr:BatchGetImage"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
- "s3:GetObject"
Resource: "*"
# ECSタスク定義の作成
ECSTaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: "my-app-task"
NetworkMode: "awsvpc"
RequiresCompatibilities:
- "FARGATE"
Cpu: "256"
Memory: "512"
ExecutionRoleArn: !GetAtt ECSExecutionRole.Arn
ContainerDefinitions:
- Name: "my-app-repo"
Image: "881490128743.dkr.ecr.ap-northeast-1.amazonaws.com/my-app-repo"
PortMappings:
- ContainerPort: 1323
HostPort: 1323
Protocol: "tcp"
# ECSサービスの作成
ECSService:
Type: AWS::ECS::Service
Properties:
ServiceName: "my-ecs-service"
Cluster: !Ref ECSCluster
TaskDefinition: !Ref ECSTaskDefinition
DesiredCount: 1
LaunchType: "FARGATE"
NetworkConfiguration:
AwsvpcConfiguration:
Subnets:
- !Ref PublicSubnet1
SecurityGroups:
- !GetAtt ECSGroup.GroupId
AssignPublicIp: "ENABLED"
LoadBalancers:
- TargetGroupArn: !Ref MainTargetGroup
ContainerName: "my-app-repo"
ContainerPort: 1323
DependsOn:
- HttpsListener # HttpListenerではなく、HttpsListenerを指定
# パブリック用セキュリティグループ作成
PublicSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Public security group for ALB"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: "tcp"
FromPort: 443
ToPort: 443
CidrIp: "0.0.0.0/0"
SecurityGroupEgress:
- IpProtocol: "-1"
FromPort: 0
ToPort: 0
CidrIp: "0.0.0.0/0"
Tags:
- Key: Name
Value: "CloudFormation-public-sg"
# アプリケーションロードバランサー(ALB)作成
MainALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: "cloudformation-main-alb"
Scheme: "internet-facing"
Type: "application"
SecurityGroups:
- !GetAtt PublicSG.GroupId
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
Tags:
- Key: Name
Value: "cloudformation-main-alb"
# ターゲットグループ作成
MainTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: "cloudformation-main-target-group"
Port: 1323
Protocol: "HTTP"
VpcId: !Ref VPC
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
HealthCheckProtocol: "HTTP"
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 3
UnhealthyThresholdCount: 3
TargetType: "ip" # ターゲットタイプをIPに設定
Tags:
- Key: Name
Value: "cloudformation-main-target-group"
# ALBのリスナー作成 (HTTPS)
HttpsListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref MainALB
Port: 443
Protocol: "HTTPS"
Certificates:
- CertificateArn: !Ref ACMCertificate
DefaultActions:
- Type: "forward"
TargetGroupArn: !Ref MainTargetGroup
# RDS用セキュリティグループ
RDSSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Security group for RDS"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: "tcp"
FromPort: 3306
ToPort: 3306
CidrIp: "10.0.0.0/16"
SecurityGroupEgress:
- IpProtocol: "-1"
FromPort: 0
ToPort: 0
CidrIp: "0.0.0.0/0"
Tags:
- Key: Name
Value: "CloudFormation-rds-sg"
# RDS用サブネットグループ
RDSSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: "Subnet group for RDS"
SubnetIds:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
Tags:
- Key: Name
Value: "CloudFormation-rds-subnet-group"
# RDSインスタンス
RDSInstance:
Type: AWS::RDS::DBInstance
DeletionPolicy: Delete
Properties:
AllocatedStorage: "20" # 無料利用枠に合わせたストレージ
StorageType: "gp2"
Engine: "mysql"
EngineVersion: "8.0.39"
DBInstanceClass: "db.t4g.micro" # Graviton2プロセッサを使用したインスタンス
MasterUsername: "admin"
MasterUserPassword: "mypassword123"
DBParameterGroupName: "default.mysql8.0"
DBSubnetGroupName: !Ref RDSSubnetGroup
VPCSecurityGroups:
- !GetAtt RDSSecurityGroup.GroupId
PubliclyAccessible: false
MultiAZ: false
Tags:
- Key: Name
Value: "CloudFormation-rds-instance"
DeletionProtection: false
# S3バケット作成
S3Bucket:
Type: "AWS::S3::Bucket"
Properties:
BucketName: "my-cloudfront-bucket-tokyo" # S3バケット名を指定
AccessControl: "Private" # バケットのアクセス制御をプライベートに設定
Tags:
- Key: "Name"
Value: "MyS3Bucket" # バケットの名前タグ
- Key: "Environment"
Value: "Production" # 環境情報のタグ(本番用)
WebsiteConfiguration:
IndexDocument: "index.html" # デフォルトのインデックスドキュメント
ErrorDocument: "error.html" # エラーページの設定
S3BucketPolicy:
Type: "AWS::S3::BucketPolicy"
Properties:
Bucket: !Ref S3Bucket # バケットポリシーを適用するS3バケット
PolicyDocument:
Version: "2012-10-17" # ポリシーのバージョン
Statement:
- Effect: "Allow"
Principal:
CanonicalUser: !GetAtt CloudFrontOAI.S3CanonicalUserId # CloudFront OAIを許可
Action: "s3:GetObject" # オブジェクトの読み取り権限
Resource: !Sub "${S3Bucket.Arn}/*" # バケット内のすべてのオブジェクトが対象
CloudFrontOAI:
Type: "AWS::CloudFront::CloudFrontOriginAccessIdentity"
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: "OAI for S3 bucket" # S3バケット用のCloudFront OAI設定
CloudFrontDistribution:
Type: "AWS::CloudFront::Distribution"
Properties:
DistributionConfig:
Enabled: true # CloudFrontディストリビューションを有効化
Comment: "CloudFront Distribution for S3 bucket in Tokyo region" # ディストリビューションの説明コメント
DefaultRootObject: "index.html" # デフォルトのルートオブジェクト
Origins:
- Id: "S3-my-cloudfront-bucket" # オリジンのID
DomainName: !GetAtt S3Bucket.RegionalDomainName # S3バケットのドメイン名
S3OriginConfig:
OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CloudFrontOAI}" # OAIを使用してセキュアにアクセス
DefaultCacheBehavior:
TargetOriginId: "S3-my-cloudfront-bucket" # ターゲットオリジンのID
ViewerProtocolPolicy: "redirect-to-https" # HTTPリクエストをHTTPSにリダイレクト
AllowedMethods:
- "GET"
- "HEAD" # 許可されるHTTPメソッド
CachedMethods:
- "GET"
- "HEAD" # キャッシュ可能なHTTPメソッド
ForwardedValues:
QueryString: false # クエリ文字列を転送しない
Cookies:
Forward: "none" # クッキーを転送しない
PriceClass: "PriceClass_100" # 利用可能なリージョン(最も安価なプラン)
Restrictions:
GeoRestriction:
RestrictionType: "none" # 地域制限なし
ViewerCertificate:
CloudFrontDefaultCertificate: true # デフォルトのCloudFront証明書を使用
Tags:
- Key: "Name"
Value: "MyCloudFrontDistribution" # ディストリビューションの名前タグ
- Key: "Environment"
Value: "Production" # 環境情報のタグ(本番用)
まとめ
この記事は、自分用の備忘録としてまとめたものです。その点をご理解いただけると幸いです。
もし、この記事の内容が少しでも参考になれば嬉しく思います。
今後も同様の内容を継続して投稿していきますので、温かく見守っていただけるとありがたいです。