この記事では、CloudFormationで様々な環境構築に転用できそうな、ベースとなる「基本のだし」のような構成を作成します。
仕事柄、様々な製品のデモを行うことが多いのですが、AWS学習の狙いとしてはCloudFormationテンプレートのストックを溜めておき、デモのシナリオや製品の動作要件に応じて、ささっと環境を作れるようになるのが当面の目標であり、願望です(^^
※構成やテンプレートの書き方等、至らない点も多々あると思いますがご容赦ください・・・・
##通信要件
- AWS東京リージョン(ap-northeast-1)固定
- Public SubnetとPrivate Subnetを1つずつ作成
- それぞれのSubnetにEC2インスタンスを1つずつ作成
- AMIはAmazon Linux 2、インスタンスタイプはt2.micro固定
- Private SubnetのEC2インスタンスは、Public Subnetに設置したNAT Gatewayを介してインターネット接続
- Public Subnetに設置されたEC2インスタンスには、Elastic IPを設定する
- Security Groupは、SSHインバウンド通信のみ許可する
##CloudFormationテンプレート
今回は以下の4つのStackで環境構築を行いました。
備忘録のため、テンプレートにやや多めにコメントを入れています。
- stack_Network.yml
- stack_NATgateway.yml
- stack_RouteTable.yml
- stack_Instance.yml
######テンプレートを分けた理由
ルーティングは、今後汎用的に作るであろう構成によって柔軟に変更できるようにしたいのと、NAT GatewayとElastic IPおよびEC2インスタンスは、Runしたままだと課金が絡んでくるため、不要時はStackごと削除できるようにテンプレートを分離しました。
AWSTemplateFormatVersion: 2010-09-09
Resources:
# Create a VPC
myVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/21
EnableDnsSupport: true
Tags:
- Key: Name
Value: myVPC
# Create an Internet Gateway
internetGW:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: internetGW
# Attach the internet gateway to the VPC
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref myVPC
InternetGatewayId: !Ref internetGW
# Create a Public Subnet
publicsubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: "ap-northeast-1a"
VpcId: !Ref myVPC
CidrBlock: 10.0.0.0/24
Tags:
- Key: Name
Value: PublicSubnetFromCF
# Create a Private Subnet
privatesubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: "ap-northeast-1a"
VpcId: !Ref myVPC
CidrBlock: 10.0.1.0/24
Tags:
- Key: Name
Value: PrivateSubnetFromCF
# Create a Security Group allowing Inbound SSH only
SecGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: mySGdesc
GroupName: mySGName
VpcId: !Ref myVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: mySG
# List Outputs for cross reference
Outputs:
VPC:
Value: !Ref myVPC
Export:
Name: VPCId
inetGW:
Value: !Ref internetGW
Export:
Name: internetGWName
Subnet1:
Value: !Ref publicsubnet
Export:
Name: Subnet1Name
Subnet2:
Value: !Ref privatesubnet
Export:
Name: Subnet2Name
SG1:
Value: !GetAtt SecGroup.GroupId
Export:
Name: SG1Name
ここまでで
- VPC
- Internet Gateway
- Subnet
- Security Group
の作成完了です。
次に、NAT Gatewayの設定を行います。
AWSTemplateFormatVersion: 2010-09-09
Resources:
# Create a NAT Gateway in Public Subnet
NATGW:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt EIpNATGW.AllocationId # Associate EIP with NATGW
SubnetId: !ImportValue Subnet1Name # Associate NATGW with the Public Subnet
Tags:
- Key: Name
Value: NATGW
EIpNATGW:
Type: AWS::EC2::EIP # Allocate an Elastic IP with NAT Gateway
Properties:
Domain: vpc
Outputs:
NatGW:
Value: !Ref NATGW
Export:
Name: NATGWId
ここまでで
- NAT Gatewayの作成
が完了しました。
次は、
- Public SubnetがInternet Gatewayを経由してインターネット接続を行うためのルートテーブル作成およびルートの設定
- Private SubnetがNAT Gatewayを経由してインターネット接続を行うためのルートテーブル作成およびルートの設定
を行います。
AWSTemplateFormatVersion: 2010-09-09
Resources:
# Create a Route Table for Public Subnet
RouteTablePublic:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !ImportValue VPCId
Tags:
- Key: Name
Value: RouteTablePublic
# Attach the Route Table to Public Subnet
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref RouteTablePublic
SubnetId: !ImportValue Subnet1Name
# Create a Route from Public Subnet to Internet
routeINET:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTablePublic
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !ImportValue internetGWName
# Create a Route Table for Private Subnet
RouteTablePrivate:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !ImportValue VPCId
Tags:
- Key: Name
Value: RouteTablePrivate
# Attach the Route Table to Private Subnet
PrivateSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref RouteTablePrivate
SubnetId: !ImportValue Subnet2Name
# Create a Route from Private Subnet to NATGateway
routeNATGW:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTablePrivate # Associate NATGW with RouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !ImportValue NATGWId # Property Name must be NatGatewayID when NAT Gateway is the next hop.
Outputs:
RouteTableName:
Value: !Ref RouteTablePrivate
Export:
Name: RouteTablePrivate # NATGW needs to refer this RouteTable Name
ここまでで、
- Route Tableの作成およびルートの設定
- が完了しました。
最後に、Public SubnetとPrivate SubnetのそれぞれでEC2インスタンスを起動します。
AWSTemplateFormatVersion: 2010-09-09
Resources:
# Create a Public EC2 instance
PublicEC2:
Type: AWS::EC2::Instance
Properties:
KeyName: mykey01 # Set keypair
ImageId: ami-09d28faae2e9e7138 # Set Amazon Linux 2 free AMI
NetworkInterfaces:
- AssociatePublicIpAddress: true # Attach IPv4 Public Address
DeviceIndex: 0
GroupSet: [!ImportValue SG1Name] # Refer external SG, need to set as List
SubnetId: !ImportValue Subnet1Name # Refer external Subnet
InstanceType: t2.micro
Monitoring: false
Tags:
- Key: Name
Value: PublicEC2fromCF
EIPPublic:
Type: AWS::EC2::EIP #Allocate an EIP with Public EC2
Properties:
Domain: vpc
InstanceId: !Ref PublicEC2
# Create a Private EC2 instance
PrivateEC2:
Type: AWS::EC2::Instance
Properties:
KeyName: mykey01 # Set keypair
ImageId: ami-09d28faae2e9e7138 # Set Amazon Linux 2 free AMI
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: 0
GroupSet: [!ImportValue SG1Name] # Refer external SG, need to set as List
SubnetId: !ImportValue Subnet2Name # Refer external Subnet
InstanceType: t2.micro
Monitoring: false
# SecurityGroupIds:
Tags:
- Key: Name
Value: PrivateEC2fromCF
#ハマった点
AWS Management Consoleでの操作はわかっても、CloudFormationでの記法がわからない点が多々あり、多くの時間を費やしました。
- EC2インスタンスにPublicIPv4を割り当てる設定の記法がわからず、特にGroupSetにSecurity Groupを設定する際はリストの形で渡さなくてはならない点
- Elastic IPをEC2インスタンスやNAT Gatewayに割り当てる際、Type::EC2::EIPをどのインデントに書けばいいのかがわからなかった点
- Public SubnetからInternetへのルーティングを記載するRoute Tableは、CloudFormationでは、VPC DefaultのRoute Tableを参照することはできない(別のRoute Tableを作らないといけない) < これが最もハマった(参考URL)
- NAT GatewayをNext Hopにするルートを記載する際、Property NameはGatewayIdではなく、NatGatewayIdを使用しなくてはならない点
#感想
AWSもCloudFormationもほぼ初見で、あれこれ調べながらやりましたが、非常に勉強になりました。今後のAWS学習もぜひCloudFormationとセットで進めていきたいです。Qiita投稿も初めてだったので読みづらかったかもしれません。今後少しずつ改善したいと思います。
その他感想としては、
- LogicalIDやOutputなどの名前付けが難しい
- 当初長大なテンプレートを作ってしまい、Stack実行時のエラーのデバッグが大変になり、考えた結果テンプレートを4つに分けることにしたこと。
- テンプレートを修正し、エラーを確認し、ドキュメントを調べて修正し、また試す。という繰り返し作業は(大変ですが)面白かったこと
#最後に
この記事はAWS初学者を導く体系的な動画学習サービス
「AWS CloudTech」の課題カリキュラムで作成しました。
https://aws-cloud-tech.com