#■はじめに
EC2(wordpress)・RDSのシングル構成(※)を今度はCloudFormationで構築してみました。
※AWSで基本的なブログサービスを構築する(シングル構成)
#■テンプレート概要
上記 85ページを参考に、
ネットワーク、セキュリティ、アプリケーションの3つに分ける。
加え、一部設定値を記述した外部ファイルの計4つを作成する。
- ネットワーク ⇒ cf_network.yml
- セキュリティ ⇒ cf_security.yml
- アプリケーション ⇒ cf_application.yml
- 設定ファイル ⇒ dev.cfg
#■テンプレート詳細
【ネットワークテンプレート】
VPC, サブネット, ルートテーブルについて記述する。
VPC
CidrBlockの指定。
サブネット
パブリックサブネット1,2、プライベートサブネット1,2のAZ,VPC,CidrBlockの指定。
DBサブネットグループ
SubnetIdsでプライベートサブネット1,2 であることを指定。
インターネットゲートウェイ
VPC1にアタッチすることを指定。
ルートテーブル
パブリックサブネット用のルートテーブルには localとインターネットゲートウェイ向けの設定。
プライベートサブネット用のルートテーブルには 宛先指定なし(localのみになる)の設定。
output
VPCID,サブネットID,サブネットグループ名、インターネットゲートウェイ名、ルートテーブルIDをアウトプット指定。
AWSTemplateFormatVersion: 2010-09-09
Resources:
#-----------------------
#VPC Create
#-----------------------
MyVPC1Cf:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.8.0/21
EnableDnsSupport: true
Tags:
- Key: Name
Value: MyVPC1-Cf
#-----------------------
#Subnet Create
#-----------------------
#PublicSubnet1 create
PublicSubnet1Cf:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: "ap-northeast-1a"
VpcId: !Ref MyVPC1Cf
CidrBlock: 10.0.10.0/24
Tags:
- Key: Name
Value: PublicSubnet1-Cf
#PublicSubnet2 create
PublicSubnet2Cf:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: "ap-northeast-1c"
VpcId: !Ref MyVPC1Cf
CidrBlock: 10.0.11.0/24
Tags:
- Key: Name
Value: PublicSubnet2-Cf
#PrivateSubnet1 create
PrivateSubnet1Cf:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: "ap-northeast-1a"
VpcId: !Ref MyVPC1Cf
CidrBlock: 10.0.12.0/24
Tags:
- Key: Name
Value: PrivateSubnet1-Cf
#PrivateSubnet2 create
PrivateSubnet2Cf:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: "ap-northeast-1c"
VpcId: !Ref MyVPC1Cf
CidrBlock: 10.0.13.0/24
Tags:
- Key: Name
Value: PrivateSubnet2-Cf
#-----------------------
#RDS SubnetGroup Create
#-----------------------
RdsSubnetGroupCf:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupName: RdsSubnetGroupCf
DBSubnetGroupDescription: RdsSubnetGroupCf
SubnetIds:
- !Ref PrivateSubnet1Cf
- !Ref PrivateSubnet2Cf
Tags:
- Key: Name
Value: RdsSubnetGroup-Cf
#-----------------------
#InternetGateway Create
#-----------------------
InternetGW1Cf:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: InternetGW1-Cf
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref MyVPC1Cf
InternetGatewayId: !Ref InternetGW1Cf
#-----------------------
#Routetable Create
#-----------------------
#PublicRoutetable create
PublicRoutetableCf:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref MyVPC1Cf
Tags:
- Key: Name
Value: PublicRoutetable-Cf
PublicRoutetableAssociationCf:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1Cf
RouteTableId: !Ref PublicRoutetableCf
PublicRouteCf:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRoutetableCf
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGW1Cf
#PrivateRoutetable create
PrivateRoutetableCf:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref MyVPC1Cf
Tags:
- Key: Name
Value: PrivateRoutetable-Cf
PrivateRoutetableAssociationCf:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1Cf
RouteTableId: !Ref PrivateRoutetableCf
Outputs:
#-----------------------
#VPC output
#-----------------------
MyVPC1CfOutput:
Value: !Ref MyVPC1Cf
Export:
Name: MyVPC1Cf-Id
#-----------------------
#Subnet output
#-----------------------
#PublicSubnet1Cf
PublicSubnet1CfOutput:
Value: !Ref PublicSubnet1Cf
Export:
Name: PublicSubnet1Cf-Id
#PublicSubnet2Cf
PublicSubnet2CfOutput:
Value: !Ref PublicSubnet2Cf
Export:
Name: PublicSubnet2Cf-Id
#PrivateSubnet1Cf
PrivateSubnet1CfOutput:
Value: !Ref PrivateSubnet1Cf
Export:
Name: PrivateSubnet1Cf-Id
#PrivateSubnet1Cf
PrivateSubnet2CfOutput:
Value: !Ref PrivateSubnet2Cf
Export:
Name: PrivateSubnet2Cf-Id
#-----------------------
#RDS SubnetGroup output
#-----------------------
RdsSubnetGroupCfOutput:
Value: !Ref RdsSubnetGroupCf
Export:
Name: RdsSubnetGroupCf-GroupName
#-----------------------
#InternetGateway output
#-----------------------
InternetGW1CfOutput:
Value: !Ref InternetGW1Cf
Export:
Name: InternetGW1Cf-Name
#-----------------------
#Routetable output
#-----------------------
#PublicRoutetableCf
PublicRoutetableCfOutput:
Value: !Ref PublicRoutetableCf
Export:
Name: PublicRoutetableCf-Id
#PrivateRoutetableCf
PrivateRoutetableCfOutput:
Value: !Ref PrivateRoutetableCf
Export:
Name: PrivateRoutetableCf-Id
【セキュリティテンプレート】
セキュリティグループについて記述する。
セキュリティグループ
EC2セキュリティグループとRDSセキュリティグループの作成。
EC2はsshとhttpのインバウンド、RDSはmysqlのインバウンド指定。
アウトプット
アプリケーションテンプレートから セキュリティグループのIDを参照するためGetAttで指定。
AWSTemplateFormatVersion: 2010-09-09
Resources:
#-----------------------
#SecurityGroup Create
#-----------------------
#EC2 SecurityGroup create
WebSg1Cf:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: WebSg1Cf
GroupDescription: WebSg1Cf
VpcId: !ImportValue MyVPC1Cf-Id
SecurityGroupIngress:
# ssh
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
# http
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: WebSg1-Cf
#RDS SecurityGroup create
RdsSg1Cf:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: RdsSg1Cf
GroupDescription: RdsSg1cf
VpcId: !ImportValue MyVPC1Cf-Id
SecurityGroupIngress:
- SourceSecurityGroupId: !Ref WebSg1Cf
IpProtocol: tcp
FromPort: 3306
ToPort: 3306
Tags:
- Key: Name
Value: RdsSg1-Cf
Outputs:
#-----------------------
#SecurityGroup output
#-----------------------
#WebSg1Cf
WebSg1CfOutput:
Value: !GetAtt WebSg1Cf.GroupId
Export:
Name: WebSg1Cf-Id
#RdsSg1Cf
RdsSg1CfOutput:
Value: !GetAtt RdsSg1Cf.GroupId
Export:
Name: RdsSg1Cf-Id
【アプリケーションテンプレート】
EC2, RDSについて記述する。
パラメータ
EC2インスタンスのイメージIDとインスタンスタイプの指定、キーペア名の指定、データベースパスワードの型定義。
イメージIDとは、マネジメントコンソールでいうと以下の部分に該当する。
CLIで以下を実行するとインスタンスイメージIDが取得できる。
$ aws ssm get-parameter --name /aws/service/ami-amazon-linux-latest/amzn2-ami-kernel-5.10-hvm-x86_64-gp2 --query "Parameter.Value" --region ap-northeast-1
ami-0404778e217f54308
CloudFormationで自動で取得するにはParameterで以下を指定する。
EC2ImageIdCf:
Type: AWS::SSM::Parameter::Value<String>
Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-kernel-5.10-hvm-x86_64-gp2
キーペアはあらかじめ作成しておく前提。
以下記述をしておくことで、マネジメントコンソールから選択できるようになる。
また、CLIからデプロイ実行時は"Default"のキーペア名が使用される
KeyNameCf:
Description: The EC2 Key Pair to allow SSH access to the instance
Type: "AWS::EC2::KeyPair::KeyName"
Default: "Mykeypair"
EC2
パブリックIPの自動割り当て有効化のため、"NetworkInterface:" を記述。
ここでインデックスを指定しないと自動割り当てされない。(もしくはVPC側の設定で自動割り当てされるようにする必要がある)
wordpressのインストールコマンドは「UserData」に記述。
NetworkInterfaceとは、マネジメントコンソールでいうと以下の部分に該当する。
テンプレートでは以下のように記述する。セキュリティグループID(GroupSet)も記述しなければならない。
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
SubnetId: !ImportValue PublicSubnet1Cf-Id
GroupSet:
- !ImportValue WebSg1Cf-Id
RDS
マスターユーザーパスワードは、コマンド実行時に外部ファイルをオーバーライド指定するので !Refで参照する
AWSTemplateFormatVersion: 2010-09-09
Parameters:
#-----------------------
#EC2 Instance Parameter
#-----------------------
EC2ImageIdCf:
Type: AWS::SSM::Parameter::Value<String>
Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-kernel-5.10-hvm-x86_64-gp2
EC2InstanceTypeCf:
Type: String
Default: t2.micro
#-----------------------
#EC2 Keypair Parameter
#-----------------------
KeyNameCf:
Description: The EC2 Key Pair to allow SSH access to the instance
Type: "AWS::EC2::KeyPair::KeyName"
Default: "Mykeypair"
#-----------------------
#RDS DatabasePassword
#-----------------------
DatabasePassword:
Type: String
Description: Database password
NoEcho: "true"
Resources:
#-----------------------
#EC2 Create
#-----------------------
WebServer1Cf:
Type: AWS::EC2::Instance
Properties:
KeyName: !Ref KeyNameCf
ImageId: !Ref EC2ImageIdCf
InstanceType: !Ref EC2InstanceTypeCf
Monitoring: false
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
SubnetId: !ImportValue PublicSubnet1Cf-Id
GroupSet:
- !ImportValue WebSg1Cf-Id
#wordpress install
UserData:
Fn::Base64: |
#!/bin/bash
yum -y update
amazon-linux-extras install php7.2 -y
yum -y install mysql httpd php-mbstring php-xml gd php-gd
systemctl enable httpd.service
systemctl start httpd.service
cd /var/tmp
wget http://ja.wordpress.org/latest-ja.tar.gz ./
tar zxvf ./latest-ja.tar.gz
cp -r ./wordpress/* /var/www/html/
chown apache:apache -R /var/www/html
Tags:
- Key: Name
Value: WebServer1-Cf
#-----------------------
#RDS Create
#-----------------------
Database1Cf:
Type: AWS::RDS::DBInstance
Properties:
Engine: mysql
EngineVersion: 5.7
DBInstanceClass: db.t2.micro
AllocatedStorage: 10
StorageType: gp2
MasterUsername: wordpress
MasterUserPassword: !Ref DatabasePassword
DBName: wordpress
VPCSecurityGroups:
- !ImportValue RdsSg1Cf-Id
DBSubnetGroupName: !ImportValue RdsSubnetGroupCf-GroupName
AvailabilityZone: "ap-northeast-1a"
DBInstanceIdentifier: database-1-cf
MultiAZ: false
BackupRetentionPeriod: 0
Tags:
- Key: Name
Value: Database1-Cf
【設定ファイル】
データベースパスワードのみ記述。
DatabasePassword=xxxxxx
#■作成時に詰まった点
【ec2 「NetworkInterfaces」のエラー】
NetworkInterfacesの中にGroupSetを記述する必要があるが、
以下のようにNetworkInterfacesとGroupSetを同列に記述してエラーが発生していた。
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
SubnetId: !ImportValue PublicSubnet1Cf-Id
GroupSet:
- !ImportValue WebSg1Cf-Id
また、GroupSetではなく、SecurityGroupIds にIDを書くこともできるようだが、
そちらは私のテンプレートではエラーが出てしまっていた。List of Stringでないとか。
対処できていないがGroupSetで構築できてしまったので一旦パスした。
【外部ファイルからのデータベースパスワード読み込み不可】
エラー文字列をメモるのを忘れてしまったが、ファイル読み込み時に「パスワードに使用不可の文字が設定されている」といったようなエラー。
こちらも原因不明だが、外部ファイルを作り直したら読み込みに成功した(変な改行、ブランクが入っていた? 文字コードがおかしかった?)
#■deploy、deleteコマンド
作成時は依存性の低い順、削除時は逆順で以下実行。
【作成】
$ aws cloudformation deploy --template-file cf_network.yml --stack-name CFNetwork
$ aws cloudformation deploy --template-file cf_security.yml --stack-name CFSecurity
$ aws cloudformation deploy --template-file cf_application.yml --stack-name CFApplication --parameter-overrides $(cat dev.cfg)
【削除】
$ aws cloudformation delete-stack --stack-name CFApplication
$ aws cloudformation delete-stack --stack-name CFSecurity
$ aws cloudformation delete-stack --stack-name CFNetwork