超細分化 AWS CloudFormation RootStackTemplate
AWSTemplateFormatVersion: 2010-09-09
_____ _ _____ _ _
| __ \ | | / ____|| | | |
| |__) | ___ ___ | |_ | (___ | |_ __ _ ___ | | __
| _ / / _ \ / _ \ | __| \___ \ | __| / _` | / __|| |/ /
| | \ \ | (_) || (_) || |_ ____) || |_ | (_| || (__ | <
|_| \_\ \___/ \___/ \__||_____/ \__| \__,_| \___||_|\_\
あらまし
こんにちは、先月SAAに合格したのですが、どうも机上の空論で終わっている気がしたので、復習も兼ねて実際にCloudFormationをもっと実践的に記述していこうと色々やってみています。
今回はCloudFormationテンプレートをもっと柔軟に使いまわせるように、超細分化して、リソースごとにテンプレートを分けて、スタックを構成してみました。
キーワード
- AWS
- CloudFormation
- S3
- ルートスタックテンプレート
- クロススタック参照
- Outputsセクション
- ImportValue関数
- GetAtt関数
AWS CloudFormationとは?
シンプルなテキストファイル(テンプレート)を使用して、あらゆるリージョンとアカウントでアプリケーションに必要とされるAWSリソースをプロビジョニングできるサービスです。
CloudFormationに関する追加料金は発生しません。(プロビジョニングされたAWSリソース分の料金のみ発生します。)
今回の基本構成方針図
- マルチAZ構成(トリプルAZ)
- ELBによる負荷分散
VPC
まずはVPCです、
超細分化する際に基準にしたのは一テンプレートにおいて極力一リソースまでを出力すると言うことを意識しました。
また細分化するにあたって工夫した点は、
CloudFormationを記述していると、細分化した際にOutputsをいちいち確認したり探したりするのに苦労すると思います。
特に今回のように超細分化してCloudFormationTemplateを記述するとなると、すぐに見るべきファイルを見失いがちです。
YAMLで記述するとJSONと違ってコメントを記述できるので、アスキーアートを使ってコメントで看板的なものを記述してあります。ダラダラとDescriptionを記述するよりは視認性が上がって個人的にはいいんじゃないかと気に入っています。
こんな感じ↓
__ __
\ \ / /
\ \ / / _ __ ___
\ \/ / | '_ \ / __|
\ / | |_) || (__
\/ | .__/ \___|
| |
|_|
では、一行目から順にみていきましょう。
AWSTemplateFormatVersion: 2010-09-09
この宣言に関してはCloundFormationを記述する上で、例えばShell等のShebang的な役割を持っていて、どのバージョンで実行させるかと言うことを宣言できます。
Resources:
以下にはAWSにおける実際のリソース定義を記述します。
ではVPCのTemplateを見ていきましょう。
AWSTemplateFormatVersion: 2010-09-09
# __ __
# \ \ / /
# \ \ / / _ __ ___
# \ \/ / | '_ \ / __|
# \ / | |_) || (__
# \/ | .__/ \___|
# | |
# |_|
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: true
EnableDnsSupport: true
InstanceTenancy: default
Tags:
- Key: Name
Value: cfn-training-vpc
Outputs:
VpcOut:
Value: !Ref VPC
Export:
Name: CreateVpc
今回はYAMLで記述するので、インデントに意味があります。
また、文字列型の記述に関してYAMLはシングルクウォート、ダブルクウォート、文字列を区別しないので、以下の宣言は全て同じ意味として解釈されます。
AWSTemplateFormatVersion: "2010-09-09"
AWSTemplateFormatVersion: '2010-09-09'
AWSTemplateFormatVersion: 2010-09-09
VPC:
に関しては自分の好きな定義名を宣言できます。
またこのフィールドはAWSの何のリソースについての記述なのか、
と言うことを宣言するために次の記述が必要になります。
Type: AWS::EC2::VPC
ここでは、VPCを定義するためTypeにVPCを宣言しましょう。
次に、Properties:
ですが、この予約語以下インデント区切りのパラメータがVPCの定義に必要なプロパティーを記述できます。
では順にプロパティーを見ていきましょう。
CidrBlock
CidrBlock: 10.0.0.0/16
ここでは、
クラスレスインタードメインルーティングブロックを定義します、今回はクラスA、
10.0.0.0 ~ 10.255.255.255 (10.0.0.0/8)
として定義しておきますが、 Cidr
と言う名前の通りネットワーククラスを深く意識する必要はないと思います。
ちなみに、このブロックの意味は
10.0.0.0/16は、10.0.0.0から10.0.255.255までの65,536個のアドレス
256 x 256 = 65,536
8 + 8なので第二オクテット
までという意味。
EnableDns*
EnableDnsHostnames: true
単純にDNSホスト名を有効にするための宣言です。
EnableDnsSupport: true
こちらも単純にDNSサポートを有効にするか否かの宣言です。
InstanceTenancy
InstanceTenancy: default
VPC内に起動されたインスタンスは、インスタンスの起動時に別のテナント属性を明示的に指定しない限り、
今回のデフォルト指定では共有ハードウェアで実行されます。
VPCに関するそのほかのプロパティーに関しては公式ドキュメントを参考にしましょう。
Outputsセクション
Outputs:
VpcOut:
Value: !Ref VPC
Export:
Name: CreateVpc
他のCloudFormationテンプレートから出力した値を使用するという宣言をすることができます。
これは、例えばあるリソースを、別のもしくは同名のエクスポート名に置き換えると、別のテンプレートからこのリソースのIDを取得できます。
具体的には、他のCloudFormationテンプレートからどのように扱えば良いのか?
ポイントは二つです。
-
!ImportValue: エクスポート名 といった形式でインポートする
-
他のCloudFormationテンプレート依存するStackに当たるので、Outputsするスタックを取り込むには依存関係を設定する
例えば今回作成したVPCを他のテンプレートでインポートするには次のように記述します。
Resources:
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !ImportValue CreateVpc
InternetGatewayId: !ImportValue CreateIgw
簡単ですね、これをクロススタックと呼んだりします。
同一テンプレート内で参照するには!Ref
等を使ったりするのですが、基本的には同じ考え方です。
詳しくは公式ドキュメントを参照しましょう。
同じ要領でその他、必要なテンプレートを記述していきます。
- InternetGateway
- Subnet
- NatGateway
- Route
- SecurityGroup
- EC2
- ELB
InternetGateway
AWSTemplateFormatVersion: 2010-09-09
# _____
# |_ _|
# | | __ _ __ __
# | | / _` |\ \ /\ / /
# _| |_ | (_| | \ V V /
# |_____| \__, | \_/\_/
# __/ |
# |___/
Resources:
InternetGW:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: cfn-training-igw
Outputs:
IgwOut:
Value: !Ref InternetGW
Export:
Name: CreateIgw
Subnet
AWSTemplateFormatVersion: 2010-09-09
# _____ _ _
# / ____| | | | |
# | (___ _ _ | |__ _ __ ___ | |_
# \___ \ | | | || '_ \ | '_ \ / _ \| __|
# ____) || |_| || |_) || | | || __/| |_
# |_____/ \__,_||_.__/ |_| |_| \___| \__|
Resources:
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !ImportValue CreateVpc
InternetGatewayId: !ImportValue CreateIgw
PublicSubnet1a:
Type: AWS::EC2::Subnet
DependsOn: VPCGatewayAttachment
Properties:
AvailabilityZone: !Sub ${AWS::Region}a
CidrBlock: 10.0.0.0/24
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: cfn-training-publicsubnet1a
VpcId: !ImportValue CreateVpc
PublicSubnet1c:
Type: AWS::EC2::Subnet
DependsOn: VPCGatewayAttachment
Properties:
AvailabilityZone: !Sub ${AWS::Region}c
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: cfn-training-publicsubnet1c
VpcId: !ImportValue CreateVpc
PublicSubnet1d:
Type: AWS::EC2::Subnet
DependsOn: VPCGatewayAttachment
Properties:
AvailabilityZone: !Sub ${AWS::Region}d
CidrBlock: 10.0.2.0/24
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: cfn-training-publicsubnet1d
VpcId: !ImportValue CreateVpc
PrivateSubnet1a:
Type: AWS::EC2::Subnet
DependsOn: VPCGatewayAttachment
Properties:
AvailabilityZone: !Sub ${AWS::Region}a
CidrBlock: 10.0.10.0/24
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: cfn-training-privatesubnet1a
VpcId: !ImportValue CreateVpc
PrivateSubnet1c:
Type: AWS::EC2::Subnet
DependsOn: VPCGatewayAttachment
Properties:
AvailabilityZone: !Sub ${AWS::Region}c
CidrBlock: 10.0.11.0/24
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: cfn-training-privatesubnet1c
VpcId: !ImportValue CreateVpc
PrivateSubnet1d:
Type: AWS::EC2::Subnet
DependsOn: VPCGatewayAttachment
Properties:
AvailabilityZone: !Sub ${AWS::Region}d
CidrBlock: 10.0.20.0/24
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: cfn-training-privatesubnet1d
VpcId: !ImportValue CreateVpc
Outputs:
PublicSubnet1aOut:
Value: !Ref PublicSubnet1a
Export:
Name: CreatePublicSubnet1a
PublicSubnet1cOut:
Value: !Ref PublicSubnet1c
Export:
Name: CreatePublicSubnet1c
PublicSubnet1dOut:
Value: !Ref PublicSubnet1d
Export:
Name: CreatePublicSubnet1d
PrivateSubnet1aOut:
Value: !Ref PrivateSubnet1a
Export:
Name: CreatePrivateSubnet1a
PrivateSubnet1cOut:
Value: !Ref PrivateSubnet1c
Export:
Name: CreatePrivateSubnet1c
PrivateSubnet1dOut:
Value: !Ref PrivateSubnet1d
Export:
Name: CreatePrivateSubnet1d
NatGateway
AWSTemplateFormatVersion: 2010-09-09
# _ _ _ _____ __ __
# | \ | | | | / ____|\ \ / /
# | \| | __ _ | |_ | | __ \ \ /\ / /
# | . ` | / _` || __|| | |_ | \ \/ \/ /
# | |\ || (_| || |_ | |__| | \ /\ /
# |_| \_| \__,_| \__| \_____| \/ \/
Resources:
NATGateway1a:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt EIP1a.AllocationId
SubnetId: !ImportValue CreatePublicSubnet1a
Tags:
- Key: Name
Value: cfn-training-nat-gateway-1a
EIP1a:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: cfn-training-eip-1a
NATGateway1c:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt EIP1c.AllocationId
SubnetId: !ImportValue CreatePublicSubnet1c
Tags:
- Key: Name
Value: cfn-training-nat-gateway-1c
EIP1c:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: cfn-training-eip-1c
NATGateway1d:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt EIP1d.AllocationId
SubnetId: !ImportValue CreatePublicSubnet1d
Tags:
- Key: Name
Value: cfn-training-nat-gateway-1d
EIP1d:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: cfn-training-eip-1d
Outputs:
NATGateway1aOut:
Value: !Ref NATGateway1a
Export:
Name: CreateNATGateway1a
NATGateway1cOut:
Value: !Ref NATGateway1c
Export:
Name: CreateNATGateway1c
NATGateway1dOut:
Value: !Ref NATGateway1d
Export:
Name: CreateNATGateway1d
Route
AWSTemplateFormatVersion: 2010-09-09
# _____ _
# | __ \ | |
# | |__) | ___ _ _ | |_ ___
# | _ / / _ \ | | | || __| / _ \
# | | \ \ | (_) || |_| || |_ | __/
# |_| \_\ \___/ \__,_| \__| \___|
Resources:
RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !ImportValue CreateVpc
Tags:
- Key: Name
Value: cfn-training-route-table
InternetGWRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !ImportValue CreateIgw
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
Tags:
- Key: Name
Value: cfn-training-public-route-table
VpcId: !ImportValue CreateVpc
PublicRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !ImportValue CreateIgw
RouteTableId: !Ref PublicRouteTable
PrivateRouteTable1a:
Type: AWS::EC2::RouteTable
Properties:
Tags:
- Key: Name
Value: cfn-training-private-route-table-1a
VpcId: !ImportValue CreateVpc
PrivateRoute1a:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !ImportValue CreateNATGateway1a
RouteTableId: !Ref PrivateRouteTable1a
PrivateRouteTable1c:
Type: AWS::EC2::RouteTable
Properties:
Tags:
- Key: Name
Value: cfn-training-private-route-table-1c
VpcId: !ImportValue CreateVpc
PrivateRoute1c:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !ImportValue CreateNATGateway1c
RouteTableId: !Ref PrivateRouteTable1c
PrivateRouteTable1d:
Type: AWS::EC2::RouteTable
Properties:
Tags:
- Key: Name
Value: cfn-training-private-route-table-1d
VpcId: !ImportValue CreateVpc
PrivateRoute1d:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !ImportValue CreateNATGateway1d
RouteTableId: !Ref PrivateRouteTable1d
PublicSubnet1aRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !ImportValue CreatePublicSubnet1a
PublicSubnet1cRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !ImportValue CreatePublicSubnet1c
PublicSubnet1dRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !ImportValue CreatePublicSubnet1d
PrivateSubnet1aRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable1a
SubnetId: !ImportValue CreatePrivateSubnet1a
PrivateSubnet1cRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable1c
SubnetId: !ImportValue CreatePrivateSubnet1c
PrivateSubnet1dRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable1d
SubnetId: !ImportValue CreatePrivateSubnet1d
VPCGatewayEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
RouteTableIds:
- !Ref PrivateRouteTable1a
- !Ref PrivateRouteTable1c
- !Ref PrivateRouteTable1d
- !Ref PublicRouteTable
ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
VpcId: !ImportValue CreateVpc
SecurityGroup
AWSTemplateFormatVersion: 2010-09-09
# _____ _____
# / ____| / ____|
# | (___ ___ ___ | | __ _ __
# \___ \ / _ \ / __|| | |_ || '_ \
# ____) || __/| (__ | |__| || |_) |
# |_____/ \___| \___| \_____|| .__/
# | |
# |_|
Resources:
SecurityGp:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: DefaultRule
VpcId: !ImportValue CreateVpc
SecurityGroupIngress:
- { IpProtocol: tcp, FromPort: 22, ToPort: 22, CidrIp: 0.0.0.0/0 }
- { IpProtocol: tcp, FromPort: 80, ToPort: 80, CidrIp: 0.0.0.0/0 }
ELBSecurityGp:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !ImportValue CreateVpc
GroupDescription: "-"
SecurityGroupIngress:
- { IpProtocol: tcp, FromPort: 80, ToPort: 80, CidrIp: 0.0.0.0/0 }
- { IpProtocol: tcp, FromPort: 443, ToPort: 443, CidrIp: 0.0.0.0/0 }
Outputs:
SecurityGpOut:
Value: !Ref SecurityGp
Export:
Name: CreateSecGp
ELBSecurityGpOut:
Value: !Ref ELBSecurityGp
Export:
Name: CreateELBSecGp
EC2
AWSTemplateFormatVersion: 2010-09-09
# | __| __|_ )
# | _| ( / Amazon Linux 2 AMI
# |___|\___|___|
Parameters:
EC2InstanceType:
Type: String
Default: t2.nano
EC2KeyName:
Type: AWS::EC2::KeyPair::KeyName
Description: SSH public key
Default: cfn-training
Resources:
EC2Instance1a:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-097473abce069b8e9
InstanceType: !Ref EC2InstanceType
KeyName: !Ref EC2KeyName
SubnetId: !ImportValue CreatePublicSubnet1a
SecurityGroupIds:
- !ImportValue CreateSecGp
UserData: !Base64 |
#! /bin/bash
yum update -y
amazon-linux-extras install nginx1.12 -y
systemctl start nginx
systemctl enable nginx
Tags:
- Key: Name
Value: cfn-training-ec2-1a
EC2Instance1c:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-097473abce069b8e9
InstanceType: !Ref EC2InstanceType
KeyName: !Ref EC2KeyName
SubnetId: !ImportValue CreatePublicSubnet1c
SecurityGroupIds:
- !ImportValue CreateSecGp
UserData: !Base64 |
#! /bin/bash
yum update -y
amazon-linux-extras install nginx1.12 -y
systemctl start nginx
systemctl enable nginx
Tags:
- Key: Name
Value: cfn-training-ec2-1c
EC2Instance1d:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-097473abce069b8e9
InstanceType: !Ref EC2InstanceType
KeyName: !Ref EC2KeyName
SubnetId: !ImportValue CreatePublicSubnet1d
SecurityGroupIds:
- !ImportValue CreateSecGp
UserData: !Base64 |
#! /bin/bash
yum update -y
amazon-linux-extras install nginx1.12 -y
systemctl start nginx
systemctl enable nginx
Tags:
- Key: Name
Value: cfn-training-ec2-1d
Outputs:
Region:
Value: !Ref AWS::Region
EC2Instance1a:
Value: !Ref EC2Instance1a
Export:
Name: CreateEC21a
EC2Instance1c:
Value: !Ref EC2Instance1c
Export:
Name: CreateEC21c
EC2Instance1d:
Value: !Ref EC2Instance1d
Export:
Name: CreateEC21d
CLB
AWSTemplateFormatVersion: 2010-09-09
Description: Create CLB
Resources:
InternetELB:
Type: AWS::ElasticLoadBalancing::LoadBalancer
Properties:
LoadBalancerName: cfn-training-elb
Scheme: internet-facing
CrossZone: true
HealthCheck:
Target: "TCP:80"
HealthyThreshold: "2"
UnhealthyThreshold: "2"
Interval: "30"
Timeout: "5"
Listeners:
- LoadBalancerPort: "80"
InstancePort: "80"
Protocol: HTTP
Instances:
- !ImportValue CreateEC21a
- !ImportValue CreateEC21c
- !ImportValue CreateEC21d
SecurityGroups:
- !ImportValue CreateELBSecGp
Subnets:
- Fn::ImportValue: CreatePublicSubnet1a
- Fn::ImportValue: CreatePublicSubnet1c
- Fn::ImportValue: CreatePublicSubnet1d
最後にこれらパーツを使って先程の構成図のような環境を構築しましょう。
今回はルートスタックテンプレートを使ってパーツをスタックとして定義し、実際にdeployまでしていきます。
まず、ルートスタックテンプレートを記述しましょう。
ディレクトリ構成は次のようにします。
template
├── child_stack_template
│ ├── alb.yml
│ ├── autoscaling.yml
│ ├── clb.yml
│ ├── ec2.yml
│ ├── internetgateway.yml
│ ├── natgateway.yml
│ ├── nlb.yml
│ ├── route.yml
│ ├── securitygroup.yml
│ ├── subnet.yml
│ └── vpc.yml
└── root-stack.yml
ルートスタックテンプレートは次のようになります。
AWSTemplateFormatVersion: "2010-09-09"
# _____ _ _____ _ _
# | __ \ | | / ____|| | | |
# | |__) | ___ ___ | |_ | (___ | |_ __ _ ___ | | __
# | _ / / _ \ / _ \ | __| \___ \ | __| / _` | / __|| |/ /
# | | \ \ | (_) || (_) || |_ ____) || |_ | (_| || (__ | <
# |_| \_\ \___/ \___/ \__||_____/ \__| \__,_| \___||_|\_\
Parameters:
TemplateVPC:
Type: String
TemplateInternetGateway:
Type: String
TemplateSubnet:
Type: String
TemplateNatGateway:
Type: String
TemplateRoute:
Type: String
TemplateSecurityGroup:
Type: String
TemplateEC2:
Type: String
TemplateCLB:
Type: String
Resources:
VPC:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateVPC
InternetGateway:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateInternetGateway
DependsOn: VPC
Subnet:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateSubnet
DependsOn: InternetGateway
NatGateway:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateNatGateway
DependsOn: Subnet
Route:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateRoute
DependsOn: NatGateway
SecurityGroup:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateSecurityGroup
DependsOn: Route
EC2:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateEC2
DependsOn: SecurityGroup
CLB:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateCLB
DependsOn: EC2
デプロイ
ルートスタックテンプレートが準備できたら次はデプロイを行ってみましょう。
デプロイにはいくつか方法があって、AWSマネジメントコンソールを用いる方法が一般的だと思いますが、今回はAWS CLIを用いてデプロイを行ってみましょう。
S3へのアップロード
このような形でShellScriptにしておくと便利ですね。
#!/bin/sh -eux
readonly BUCKETNAME
readonly REGIONNAME
aws s3api create-bucket \
--bucket "${BUCKETNAME}" \
--create-bucket-configuration "LocationConstraint=${REGIONNAME}"
aws s3api list-buckets
デプロイ
#!/bin/sh -eux
readonly BUCKETNAME
aws s3 sync template/child_stack_template "s3://${BUCKETNAME}"/
readonly REGIONNAME
readonly STACKNAME
readonly FILENAME="template/root-stack.yml"
readonly S3URL
echo "${S3URL}"
changeset_option=""
aws cloudformation validate-template --template-body file://${FILENAME}
aws cloudformation deploy \
${changeset_option} \
--stack-name "${STACKNAME}" \
--template-file "${FILENAME}" \
--parameter-overrides \
TemplateVPC="${S3URL}/vpc.yml" \
TemplateInternetGateway="${S3URL}/internetgateway.yml" \
TemplateSubnet="${S3URL}/subnet.yml" \
TemplateNatGateway="${S3URL}/natgateway.yml" \
TemplateRoute="${S3URL}/route.yml" \
TemplateSecurityGroup="${S3URL}/securitygroup.yml" \
TemplateEC2="${S3URL}/ec2.yml" \
TemplateCLB="${S3URL}/clb.yml"
最後にデプロイが成功するとこのようにAWSマネジメントコンソール上でも確認できます。
今回は各EC2にあらかじめNginxをインストール済みなので次のようにCLBのドメインにアクセスするとこのようなwelcomeメッセージを表示することができると思います。
またトリプルAZ構成かどうかはhostコマンド等を打ってみるとより実感できるかもしれません。
host cfn-training-elb-省略.ap-northeast-1.elb.amazonaws.com
cfn-training-elb-省略.ap-northeast-1.elb.amazonaws.com has address 35.XX.省略
cfn-training-elb-省略.ap-northeast-1.elb.amazonaws.com has address 13.XX.省略
cfn-training-elb-省略.ap-northeast-1.elb.amazonaws.com has address 54.XX.省略
最後に
今回作成したテンプレートには、AWSのベストプラクティス的には非推奨の構成があります。
お気づきですね、CLB(クラシックロードバランサー)の利用は好ましくありません。
ロードバランサーのテンプレートをALBに書き換えてみましょう。
こんな感じで、手を動かして、テンプレートを改良していくことでCloudFormationの感覚を掴めると思います。
今回はEC2にマルチAZ構成と言う昔ながらの王道な構成でしたが、そもそもこの構成は状況により必ずしもコスト最適ではありません。
と言うことで、次回はSAMにおけるCloudFormationの書き方等をまとめていきたいと思います。
参考文献
- 「INTRODUCTION TO AMAZON ECS」https://ecs-for-aws-summit-online.workshop.aws/
- 「AWS サンプルソリューション」https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/sample-templates-applications-ap-northeast-1.html
- 「組み込み関数リファレンス」https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html
- 「アスキーアートを作成」https://rakko.tools/tools/68/
- 「Elastic Load Balancing の特徴」https://aws.amazon.com/jp/elasticloadbalancing/features/
- 「CloudFormationでネストされたスタックを作成してみる」https://dev.classmethod.jp/articles/create-own-verification-environment-using-cfn-nested-stacks/