はじめに
以前ハンズオンで学習したCFnを使用してWordPress環境を構築した際の応用として、マルチAZで分散する冗長構成のテンプレートをCloudFormationで書いてみました。
動作検証、比較も兼ねてAmazon QとLinterを拡張機能を導入したVScodeとChatGPTを使用。
やること
- 冗長構成のVPC作成
- EC2インスタンス(Pub)とRDSインスタンス(Pri)をマルチAZ構成で作成
- RDSはパブリックサブネットのEC2からのみのアクセス許可
- ELBでEC2の冗長化
- LBのトラフィック制御
構成図
使用ツール
- VScode(Amazon Q, cfn-linter)
- CloudFormation
- ChatGPT
以下が作成したテンプレートです
VPC
AWSTemplateFormatVersion: 2010-09-09
Description: VPC with public and private subnets
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: xxxxxxx
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.0.0/24
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref AWS::Region
MapPublicIpOnLaunch: true
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.1.0/24
AvailabilityZone: !Select
- 1
- Fn::GetAZs: !Ref AWS::Region
MapPublicIpOnLaunch: true
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.10.0/24
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref AWS::Region
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.11.0/24
AvailabilityZone: !Select
- 1
- Fn::GetAZs: !Ref AWS::Region
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: test-igw
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: test-public-crt
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicRouteTable
PublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
Outputs:
VPCID:
Description: VPC ID
Value: !Ref VPC
Export:
Name: !Sub ${AWS::StackName}-VPCID
PublicSubnet1ID:
Description: Public Subnet1 ID
Value: !Ref PublicSubnet1
Export:
Name: !Sub ${AWS::StackName}-public-subnet1-id
PublicSubnet2ID:
Description: Public Subnet2 ID
Value: !Ref PublicSubnet2
Export:
Name: !Sub ${AWS::StackName}-public-subnet2-id
PrivateSubnet1ID:
Description: Private Subnet1 ID
Value: !Ref PrivateSubnet1
Export:
Name: !Sub ${AWS::StackName}-private-subnet1-id
PrivateSubnet2ID:
Description: Private Subnet2 ID
Value: !Ref PrivateSubnet2
Export:
Name: !Sub ${AWS::StackName}-private-subnet2-id
EC2
AWSTemplateFormatVersion: 2010-09-09
Description: template to launch an EC2 instance
Parameters:
VPCStack:
Type: String
Description: xxxxx
EC2AMI:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
Resources:
EC2Instance1A:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref EC2AMI
InstanceType: t2.micro
SubnetId: xxxxxxxxxxxxxxxxx
UserData:
Fn::Base64: |
#! /bin/bash
amazon-linux-extras install php7.2 -y
yum -y install mysql httpd php-mbstring php-xml
wget http://ja.wordpress.org/latest-ja.tar.gz -P /tmp/
tar zxvf /tmp/latest-ja.tar.gz -C /tmp
cp -r /tmp/wordpress/* /var/www/html/
touch /var/www/html/.check_alive
chown apache:apache -R /var/www/html
sudo systemctl start httpd.service
sudo systemctl enable httpd.service
SecurityGroupIds:
- !Ref EC2SG
EC2Instance1C:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref EC2AMI
InstanceType: t2.micro
SubnetId: xxxxxxxxxxxxxxxxx
UserData:
Fn::Base64: |
#! /bin/bash
amazon-linux-extras install php7.2 -y
yum -y install mysql httpd php-mbstring php-xml
wget http://ja.wordpress.org/latest-ja.tar.gz -P /tmp/
tar zxvf /tmp/latest-ja.tar.gz -C /tmp
cp -r /tmp/wordpress/* /var/www/html/
touch /var/www/html/.check_alive
chown apache:apache -R /var/www/html
sudo systemctl start httpd.service
sudo systemctl enable httpd.service
SecurityGroupIds:
- !Ref EC2SG
EC2SG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow HTTP access
VpcId: xxxxxxxxxxxxxxxxxxxxxxxxx
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 10.0.0.0/16
Outputs:
EC2Instance1A:
Value: !Ref EC2Instance1A
Export:
Name: !Sub ${AWS::StackName}-EC2Instance1A
EC2Instance1C:
Value: !Ref EC2Instance1C
Export:
Name: !Sub ${AWS::StackName}-EC2Instance1C
RDS
AWSTemplateFormatVersion: 2010-09-09
Description: CloudFormation template to create an RDS instance
Parameters:
VPCStackName:
Type: String
Default: xxxxxxx
DBUsername:
Type: String
Default: admin
Description: The name of the database.
DBPassword:
Type: String
Default: xxxxxxxx
NoEcho: true
Description: The password for the database user.
Resources:
MyDBInstance:
Type: AWS::RDS::DBInstance
DeletionPolicy: Delete
Properties:
DBInstanceClass: db.t3.micro
AllocatedStorage: 10
StorageType: gp2
Engine: MySQL
MasterUsername: !Ref DBUsername
MasterUserPassword: !Ref DBPassword
DBName: wordpress
BackupRetentionPeriod: 0
DBSubnetGroupName: !Ref MyDBSubnetGroup
VPCSecurityGroups:
- !Ref MyDBSecurityGroup
MyDBSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: Subnet group for RDS
SubnetIds:
- subnet-xxxxxxxxxxxxxxxxx
- subnet-xxxxxxxxxxxxxxxxx
MyDBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for RDS
VpcId: vpc-xxxxxxxxxxxxxxxxx
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
CidrIp: 10.0.0.0/16
Outputs:
DBEndpoint:
Value: !GetAtt MyDBInstance.Endpoint.Address
Export:
Name: !Sub ${AWS::StackName}-DBEndpoint
ELB
ターゲットグループで複数のEC2インスタンスの指定はできなかったので書き加えました。
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFormation template to create EC2 instances with Multi-AZ and ELB for load balancing (without Auto Scaling)
Parameters:
VPCStack:
Type: String
Default: xxxxxx
EC2Stack:
Type: String
Default: xxxxxx
Resources:
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: "MyLoadBalancer"
Subnets:
- subnet-xxxxxxxxxxxxxxxxx
- subnet-xxxxxxxxxxxxxxxxx
SecurityGroups:
- !Ref LoadBalancerSecurityGroup
Scheme: internet-facing
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Protocol: HTTP
Port: 80
DefaultActions:
- Type: forward
TargetGroupArn: !Ref LoadBalancerTargetGroup
LoadBalancerTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: "MyTargetGroup"
VpcId: vpc-xxxxxxxxxxxxxxxx
Port: 80
Protocol: HTTP
Targets:
- Id: i-xxxxxxxxxxxxxxxxx
- Id: i-xxxxxxxxxxxxxxxxx
HealthCheckPath: /.check_alive
LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for the Load Balancer
VpcId: vpc-xxxxxxxxxxxxxxxxx
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Outputs:
LoadBalancerEndpoint:
Value: !GetAtt LoadBalancer.DNSName
Export:
Name: !Sub ${AWS::StackName}-Endpoint
検証
資料
最後に
- Amazon Qは自分で構文の前後を読み取って自動で組込関数を指定して参照したり、マルチAZの指定をしてくれたりと便利だった
- どちらも出力をそのまま使おうとすると、Paramatersに余計な構文を入れていたり逆になかったりと、結局は見直しで時間がかかったため公式のテンプレートリファレンスを参照しながら進めた
- ただアシストツールとしては優れているので、ある程度の知識があればIaC実践のハードルを下げてくれると思う