1. 概要
AWSサービスの一部にCloudFormationを用いて自作のRailsを公開するための環境を構築する。
長期間利用しない場合はCloudFormationスタックを削除することで、コストを抑える。
ECSのDokcerコンテナ上でRailsアプリを立ち上げ、RDS PostgreSQL上のデータベースと接続してアプリを稼働させる。
2. 使用するAWSサービス
RailsアプリをAWS上で稼働させるために、以下のAWSサービスを使用する。
なお、CloudFormationではなく手動作成したほうが運用性が高いもの、検証過程で作成済みであり、テンプレート化する必要がないものについてはテンプレート化の対象外としている。
- VPC(CloudFormationで定義)
- RDS(CloudFormationで定義)
- ALB(CloudFormationで定義)
- CloudFront(CloudFormationで定義)
- ECS
- Route53
- ACM
- IAM
- SSM Parameter Store
3. 構成図
4. 構築方法
- 大規模なシステムでは、AWSサービスの種別、ライフサイクル、依存度、開発部署、等様々な観点でCloudFormationテンプレートを分割したほうが運用保守する上で便利だが、今回構築するシステムの規模は小さいため、一つのテンプレートにまとめて定義する
- CloudFormationで構築するAWSリソースの設定情報を整理したプロンプトを入力し、GitHub Copilotに読み込ませることで、テンプレートを作成した。
詳細については以下の記事に整理している。
5. 構築方法
出来上がったテンプレートファイルは以下の通り。
ただしRailsアプリを稼働するためには、以下のテンプレートのみではアプリの実行環境は定義できないため、追加でECS、IAM等のサービスを手動構築する必要があるため注意。
app-stack.yml
AWSTemplateFormatVersion: '2010-09-09'
Mappings:
DefaultVPCID:
VPC:
Id: vpc-XXXXXXXXXXXXXXXX
DefaultIGWID:
IGW:
Id: igw-XXXXXXXXXXXXXXXX
Resources:
# VPC Resources
AlcoholPubSubnet01:
Type: AWS::EC2::Subnet
Properties:
VpcId: !FindInMap [DefaultVPCID, VPC, Id]
CidrBlock: 172.31.0.0/27
AvailabilityZone: ap-northeast-1a
Tags:
- Key: Name
Value: alcohol-pub-subnet-01
AlcoholPubSubnet02:
Type: AWS::EC2::Subnet
Properties:
VpcId: !FindInMap [DefaultVPCID, VPC, Id]
CidrBlock: 172.31.0.32/27
AvailabilityZone: ap-northeast-1c
Tags:
- Key: Name
Value: alcohol-pub-subnet-02
AlcoholPrvSubnet01:
Type: AWS::EC2::Subnet
Properties:
VpcId: !FindInMap [DefaultVPCID, VPC, Id]
CidrBlock: 172.31.0.64/27
AvailabilityZone: ap-northeast-1a
Tags:
- Key: Name
Value: alcohol-prv-subnet-01
AlcoholPrvSubnet02:
Type: AWS::EC2::Subnet
Properties:
VpcId: !FindInMap [DefaultVPCID, VPC, Id]
CidrBlock: 172.31.0.96/27
AvailabilityZone: ap-northeast-1c
Tags:
- Key: Name
Value: alcohol-prv-subnet-02
AlcoholRouteTablePub:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !FindInMap [DefaultVPCID, VPC, Id]
Tags:
- Key: Name
Value: alcohol-routetbl-pub
AlcoholRouteTablePrv:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !FindInMap [DefaultVPCID, VPC, Id]
Tags:
- Key: Name
Value: alcohol-routetbl-prv
AlcoholRoutePub:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref AlcoholRouteTablePub
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !FindInMap [DefaultIGWID, IGW, Id]
AlcoholSubnetRouteTableAssociationPub01:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref AlcoholPubSubnet01
RouteTableId: !Ref AlcoholRouteTablePub
AlcoholSubnetRouteTableAssociationPub02:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref AlcoholPubSubnet02
RouteTableId: !Ref AlcoholRouteTablePub
AlcoholSubnetRouteTableAssociationPrv01:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref AlcoholPrvSubnet01
RouteTableId: !Ref AlcoholRouteTablePrv
AlcoholSubnetRouteTableAssociationPrv02:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref AlcoholPrvSubnet02
RouteTableId: !Ref AlcoholRouteTablePrv
AlcoholSGALB:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ALB security group
VpcId: !FindInMap [DefaultVPCID, VPC, Id]
Tags:
- Key: Name
Value: alcohol-sg-alb
AlcoholSGECS:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ECS security group
VpcId: !FindInMap [DefaultVPCID, VPC, Id]
Tags:
- Key: Name
Value: alcohol-sg-ecs
AlcoholSGRDS:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: RDS security group
VpcId: !FindInMap [DefaultVPCID, VPC, Id]
Tags:
- Key: Name
Value: alcohol-sg-rds
AlcoholSGALBIngressHTTP:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref AlcoholSGALB
IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
AlcoholSGALBIngressHTTPS:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref AlcoholSGALB
IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
AlcoholSGALBEgress:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref AlcoholSGALB
IpProtocol: tcp
FromPort: 80
ToPort: 80
DestinationSecurityGroupId: !Ref AlcoholSGECS
AlcoholSGECSIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref AlcoholSGECS
IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref AlcoholSGALB
AlcoholSGECSEgress:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref AlcoholSGECS
IpProtocol: tcp
FromPort: 5432
ToPort: 5432
DestinationSecurityGroupId: !Ref AlcoholSGRDS
AlcoholSGECSEgressHTTPS:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref AlcoholSGECS
IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
AlcoholSGECSEgressSMTP:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref AlcoholSGECS
IpProtocol: tcp
FromPort: 587
ToPort: 587
CidrIp: 0.0.0.0/0
AlcoholSGRDSIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref AlcoholSGRDS
IpProtocol: tcp
FromPort: 5432
ToPort: 5432
SourceSecurityGroupId: !Ref AlcoholSGECS
# RDS Resources
AlcoholRDSSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: Subnet group for RDS
SubnetIds:
- !Ref AlcoholPrvSubnet01
- !Ref AlcoholPrvSubnet02
DBSubnetGroupName: alcohol-subnet-group
AlcoholRDSInstance:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceIdentifier: alcohol-rds
AllocatedStorage: 20
DBInstanceClass: db.t3.micro
Engine: postgres
MasterUsername: postgres # ユーザー名は適宜変更してください
MasterUserPassword: password # パスワードは適宜変更してください
DBSubnetGroupName: !Ref AlcoholRDSSubnetGroup
VPCSecurityGroups:
- !Ref AlcoholSGRDS
PubliclyAccessible: false
AvailabilityZone: ap-northeast-1a
DBName: Alcohol_production
# ALB Resources
AlcoholTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: alcohol-target-group
Port: 80
Protocol: HTTP
VpcId: !FindInMap [DefaultVPCID, VPC, Id]
TargetType: ip
AlcoholALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: alcohol-alb
Subnets:
- !Ref AlcoholPubSubnet01
- !Ref AlcoholPubSubnet02
SecurityGroups:
- !Ref AlcoholSGALB
AlcoholALBListenerHTTP: #リスナーはECSサービス作成時/作成後に手動設定するためダミーのリスナーを設定しておく
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref AlcoholALB
Port: 8080
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref AlcoholTargetGroup
# CloudFront Resources
AlcoholCloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- DomainName: alb.stop-deisui.com
Id: alcohol-alb-origin
CustomOriginConfig:
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: match-viewer
Enabled: true
DefaultCacheBehavior:
TargetOriginId: alcohol-alb-origin
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
- OPTIONS
- PUT
- POST
- PATCH
- DELETE
CachedMethods:
- GET
- HEAD
ForwardedValues:
QueryString: false
CachePolicyId: 83da9c7e-98b4-4e11-a168-04f0df8e2c65 #UseOriginCacheControlHeaders
OriginRequestPolicyId: 216adef6-5c7f-47e4-b989-5492eafa07d3 #AllViewers
ResponseHeadersPolicyId: 60669652-455b-4ae9-85a4-c4c02393f86c #SimpleCORS
ViewerCertificate:
AcmCertificateArn: !Sub arn:aws:acm:us-east-1:${AWS::AccountId}:certificate/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
SslSupportMethod: sni-only
Aliases:
- XXXX.com
6. 結果
アプリの実行環境を構築できた。
扱ったことがないサービスを構築する場合は、初めに手動でリソースを立てて検証したほうがいいと思うが、慣れているサービスを利用するときはテンプレートで定義したほうが早い。便利!