この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので十分ご注意ください。
CloudFormationテンプレートに興味を持ち、過去に手で構築したものをテンプレートを使って再現できるか試してみました。
CloudFormationテンプレートはスタック結果が同じでも、テンプレート作成者によってパラメータや出力が違いますよね。
第3回は、EC2インスタンスを起動するテンプレートを紹介します。
CloudFormationテンプレートでVPCを構築
■前提
CloudFormationのスタックを作成する前に、以下が前提条件ですのでご確認ください。
- キーペア
■構築図
今回の構築図は上の画像です。
■CloudFormationテンプレート
今回のテンプレートを使用すると、以下のサービスが起動します。
- VPC
- パブリックサブネット2つ
- プライベートサブネット2つ
- インターネットゲートウェイ
- ルートテーブル3つ
- EC2インスタンス
- セキュリティグループ
AWSTemplateFormatVersion: "2010-09-09"
Description:
Create VPC,Subnet,IGW,RouteTable,SecurityGroups,andEC2
Parameters:
myVPCCIDR:
Type: String
Default: 10.0.0.0/16
myPublicSubnet1CIDR:
Type: String
Default: 10.0.1.0/24
myPublicSubnet2CIDR:
Type: String
Default: 10.0.3.0/24
myPrivateSubnet1CIDR:
Type: String
Default: 10.0.2.0/24
myPrivateSubnet2CIDR:
Type: String
Default: 10.0.4.0/24
myKeyName:
Type: "AWS::EC2::KeyPair::KeyName"
Resources:
myVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref myVPCCIDR
EnableDnsHostnames: 'true'
EnableDnsSupport: 'true'
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-vpc
myInternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-IGW
attachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId:
Ref: myVPC
InternetGatewayId:
Ref: myInternetGateway
myPublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: myVPC
CidrBlock: !Ref myPublicSubnet1CIDR
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref 'AWS::Region'
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-subnet-public1
myPublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: myVPC
CidrBlock: !Ref myPublicSubnet2CIDR
AvailabilityZone: !Select
- 1
- Fn::GetAZs: !Ref 'AWS::Region'
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-subnet-public2
myPrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: myVPC
CidrBlock: !Ref myPrivateSubnet1CIDR
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref 'AWS::Region'
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-subnet-private1
myPrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: myVPC
CidrBlock: !Ref myPrivateSubnet2CIDR
AvailabilityZone: !Select
- 1
- Fn::GetAZs: !Ref 'AWS::Region'
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-subnet-private2
myPublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: myVPC
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-rtb-public
myPrivateRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: myVPC
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-rtb-private1
myPrivateRouteTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: myVPC
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-rtb-private2
myPublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId:
Ref: myPublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId:
Ref: myInternetGateway
myPublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId:
Ref: myPublicSubnet1
RouteTableId:
Ref: myPublicRouteTable
myPublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId:
Ref: myPublicSubnet2
RouteTableId:
Ref: myPublicRouteTable
myPrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId:
Ref: myPrivateSubnet1
RouteTableId:
Ref: myPrivateRouteTable1
myPrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId:
Ref: myPrivateSubnet2
RouteTableId:
Ref: myPrivateRouteTable2
myEc2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: EC2 SG
VpcId: !Ref myVPC
SecurityGroupIngress:
- IpProtocol: -1
FromPort: -1
ToPort: -1
CidrIp: 10.0.0.0/16
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
GroupName: !Sub ${AWS::StackName}-sg
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-sg
myEc2Instance:
Type: AWS::EC2::Instance
DependsOn: attachGateway
Properties:
ImageId: ami-0bf97847fcd5c9f57
InstanceType: t2.micro
KeyName: !Ref myKeyName
NetworkInterfaces:
- SubnetId: !Ref myPublicSubnet1
GroupSet:
- !Ref myEc2SecurityGroup
AssociatePublicIpAddress: 'true'
DeviceIndex : 0
PrivateIpAddress: 10.0.1.10
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-EC2
UserData:
Fn::Base64: |
#!/bin/bash
sudo su -
dnf -y install nginx
systemctl start nginx.service
systemctl enable nginx.service
Outputs:
myVPC:
Value: !Ref myVPC
Export:
Name: !Sub ${AWS::StackName}-vpc
myVPCCIDR:
Value: !Ref myVPCCIDR
Export:
Name: !Sub ${AWS::StackName}-vpc-cidr
myPublicSubnet1:
Value: !Ref myPublicSubnet1
Export:
Name: !Sub ${AWS::StackName}-subnet-public1
myPublicSubnet1CIDR:
Value: !Ref myPublicSubnet1CIDR
Export:
Name: !Sub ${AWS::StackName}-subnet-public1-cidr
myPublicSubnet2:
Value: !Ref myPublicSubnet2
Export:
Name: !Sub ${AWS::StackName}-subnet-public2
myPublicSubnet2CIDR:
Value: !Ref myPublicSubnet2CIDR
Export:
Name: !Sub ${AWS::StackName}-subnet-public2-cidr
myPrivateSubnet1:
Value: !Ref myPrivateSubnet1
Export:
Name: !Sub ${AWS::StackName}-subnet-private1
myPrivateSubnet1CIDR:
Value: !Ref myPrivateSubnet1CIDR
Export:
Name: !Sub ${AWS::StackName}-subnet-private1-cidr
myPrivateSubnet2:
Value: !Ref myPrivateSubnet2
Export:
Name: !Sub ${AWS::StackName}-subnet-private2
myPrivateSubnet2CIDR:
Value: !Ref myPrivateSubnet2CIDR
Export:
Name: !Sub ${AWS::StackName}-subnet-private2-cidr
myPublicRouteTable:
Value: !Ref myPublicRouteTable
Export:
Name: !Sub ${AWS::StackName}-rtb-public
myPrivateRouteTable1:
Value: !Ref myPrivateRouteTable1
Export:
Name: !Sub ${AWS::StackName}-rtb-private1
myPrivateRouteTable2:
Value: !Ref myPrivateRouteTable2
Export:
Name: !Sub ${AWS::StackName}-rtb-private2
myEc2InstancePublicIp:
Value: !GetAtt myEc2Instance.PublicIp
Export:
Name: !Sub ${AWS::StackName}-myEc2InstancePublicIp
myEc2InstancePrivateIp:
Value: !GetAtt myEc2Instance.PrivateIp
Export:
Name: !Sub ${AWS::StackName}-myEc2InstancePrivateIp
↓詰まった点がいくつかあり、EC2一つ起動させるのに20回以上失敗しました。特に失敗したのが、"NetworkInterfaces"です。EC2インスタンスの定義の中に"NetworkInterfaces"を書きましたが、本当は独立させて!Refで引っ張てくるはずでした。何度やってもうまくいかず、結局インスタンスの定義の中に書いています。
NetworkInterfaces:
- SubnetId: !Ref myPublicSubnet1
GroupSet:
- !Ref myEc2SecurityGroup
AssociatePublicIpAddress: 'true'
DeviceIndex : 0
PrivateIpAddress: 10.0.1.10
■結果
今回のテンプレートを利用してスタックを作成してみます。
↓キーペアを選択してください。それ以外はデフォルトで大丈夫ですが、タグやオプションに関しては任意で設定してください。
↓1分ほどで作成完了します。
↓インスタンスも無事に作成されています。
↓ユーザーデータにnginxをインストール・起動のスクリプトを書いておきましたので、パブリックIPを確認しnginxが動いているか確認してみます。
↓
"Welcome to nginx!"が確認できました。
今回は以上です。
まとめ:AWS CloudFormationテンプレートのススメ#3 EC2編
CloudFormationのテンプレートを自在に作れるようになりたいと思い、現在関わっているプロジェクトの一部をテンプレート化してみました。
EC2インスタンスはマネージメントコンソールで簡単に起動できますよね。しかし、テンプレートを書くことで、インスタンスのパラメータを細かく確認することができました。
作成したテンプレートは公式ドキュメントを見ながら独自に書いたものです。こんな風にしたほうがいいよ、というのがありましたらどんどんご連絡ください!
↓第1回ALB編
https://cloud5.jp/saitou-cfn-template01-alb/
↓第2回VPC編
https://cloud5.jp/saitou-cfn-template04-vpc/
参考リンク:AWS公式ドキュメント
↓ほかの協栄情報メンバーもAWS CloudFormationに関する記事を公開しています。ぜひ参考にしてみてください。
■CloudFormation作成を効率化するValidate Template機能について(INAMURA)
https://cloud5.jp/cfn-validatetemplate-function/
■CloudFormationでRDS(Aurora PostgreSQL リージョン別クラスター)を構築する(小林 剛)
https://cloud5.jp/create-rds-aurora-postgresql-region-by-cfn/
■CloudFormationによる【S3】の構築(umemoto)
https://cloud5.jp/cf-s3/