【AWS】 CloudFormationで基本的なシングル構成を自動構築する
はじめに
ご覧いただきありがとうございます。阿河です。
社内で技術検証をする機会が多いですが、同じようなAWS環境を建てる場面が度々発生していて無駄な時間が発生しているなと思ってます。
今回はCloudFormationを調べて一から書いてみました。CloudFormationのドキュメントを参照しながらシングル構成(パブリックサブネットにEC2 1台)を自動構築してみます。
対象者
- AWSを運用中
- 手作業でAWSリソースを作成するのが面倒な人
概要
(★)がついているセクションは、今回手を動かして頂く項目です。
- 今回のハンズオン構成
- VPCの自動構築/スタック作成方法(★)
- ネットワークまわりのリソース自動作成(★)
- EC2を自動作成してApacheテストページを表示(★)
各種公式ドキュメントを参照しながら進めていきます。自力でテンプレートを作成できるように、公式ドキュメントをどのように参照していくかに重点を置いた記事になっています。
事前準備
- AWSアカウント作成
- AdministratorAccessを付与したIAMユーザーの作成
1.今回のハンズオン構成
【構成】
- VPC(パブリックサブネット×1)
- EC2インスタンス
CloudFormationを利用して、シンプルなシングル構成を作成することがゴールです。
自動作成したEC2インスタンスにアクセスしてApacheのテストページを表示させます。
2.VPCの自動構築/スタック作成方法
まずVPCを作成します。
適当なフォルダに「vpc.yml」という名前でymlファイルを作成します。
vpc.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: The template is vpc
Parameters:
SystemName:
Type: String
Default: CFn-Test
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: 'true'
EnableDnsSupport: 'true'
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub ${SystemName}-vpc
それぞれドキュメントと照らし合わせながら、ざっと説明していきます。
AWSTemplateFormatVersion: "2010-09-09"
Description: The template is vpc
Parameters:
SystemName:
・・・・
Resources:
VPC:
・・・・
使用するAWSリソースを記述するテンプレートを作成すれば、CloudFormationがそれらのリソースのプロビジョニングを代行してくれます。
テンプレートの構造
テンプレートの構造については、テンプレートの分析に記載があります。
まず冒頭で形式バージョン(AWSTemplateFormatVersion)と、簡単な説明(Description)を補足します。
形式バージョンは現時点で有効な"2010-09-09"を指定します。
パラメータ(Parameters)は、実行時にテンプレートに渡すことができる値です。
今回作成したテンプレートでは、「SystemName」というパラメータを作り、デフォルト値で「CFn-Test」を設定しました。
テンプレート内で!Subを用いて、下記のように読み替えます。
(ex. !Sub ${SystemName}-vpc)
⇒CFn-Test-vpc と読み替えます。
Resources(リソース)は、VPCやEC2などスタックに含めたいAWSリソースを宣言します。
VPCに関する細かい設定については、後述します。
その他にもセクションはありますが、必須なのはResourcesセクションです。
Resourcesセクション(VPC)
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: 'true'
EnableDnsSupport: 'true'
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub ${SystemName}-vpc
VPCの設定について、内容を見ていきます。
まずこちらの設定は、公式の記述例をもとに変更を加えたものです。
お手持ちのブラウザから「VPC CloudFormation」と検索すれば、該当のページが出てくるはずです。
こちらのページにYAMLでの記述例が載っています。
Type: AWS::EC2::VPC
Properties:
CidrBlock: String
EnableDnsHostnames: Boolean
EnableDnsSupport: Boolean
InstanceTenancy: String
Ipv4IpamPoolId: String
Ipv4NetmaskLength: Integer
Tags:
- Tag
なので各プロパティの意味を確認しながら、どのプロパティが必要か、値をどう設定するかを検討します。
-
CidrBlock
CIDR表記でのVPCのIPv4ネットワーク範囲を指します。普段AWSを触っている方であれば、お使いのリージョンにVPCがあると思うので、CIDRが被らないように設定ください。 -
EnableDnsHostnames
VPCで起動されたインスタンスがDNSホスト名を取得するかどうかを示します。有効にすると、VPC内のインスタンスはDNSホスト名を取得します。
trueに設定します。 -
EnableDnsSupport
DNS解決がVPCでサポートされているかどうかを示します。trueにします。 -
InstanceTenancy
特に専用ハードウェアでインスタンスの起動を実行する必要はないので、defaultを選択します。 -
Tags
VPCにつけるタグ。
今回は上記の5つのプロパティを設定しました。
テンプレートの記述が終わりましたら、ファイルの保存をします。
ymlファイルをS3バケットにアップロード
適当なS3バケットを作成します。
そしてS3バケットにvpc.ymlをアップロードします。
S3⇒※該当のバケットを選択⇒※vpc.ymlファイルを選択すると、アップロードしたvpc.ymlファイルの詳細が確認できます。
オブジェクトURLをコピーしておきます。
CloudFormationでスタック作成
CloudFormationのページを開きます。
スタックの作成を押します。
テンプレートの保存先として、先程コピーしたオブジェクトURLを指定します。
任意のスタック名を入力します。
後程の工程でスタック参照を行いますので、整合性をとるために「CFn-Test-vpc」と名付けます。
次へを選択。
次のページもデフォルトのまま、次へ進みます。
最後に確認画面が出ますので、入力内容に間違いがないかを確認します。
問題なければ、スタックを作成をクリックします。
スタックが作成されていきます。
「CREATE_IN_PROGRESS」が「CREATE_COMPLETE」に移行するのを待ちます。
スタックが作成されました。
リソースタブを確認すると、VPCが作成されたことが分かります。
VPCのページに飛ぶと、実際にリソースが作成されていることが分かります。
これでVPC作成はできました。
このスタックは削除してかまいません。
※S3にアップロードしたymlファイルも併せて削除します。
次のセクションで、テンプレートにサブネットなどの設定を追加します。
3. ネットワークまわりのリソース自動作成
vpc.ymlのResourcesセクションに、インターネットゲートウェイを追加します。
InternetGateway:
Type: "AWS::EC2::InternetGateway"
Properties:
Tags:
- Key: Name
Value: !Sub "${SystemName}-igw"
InternetGatewayAttachment:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
ブラウザから「Internet Gateway CloudFormation」で検索をかけます。
インターネットゲートウェイのCloudFormation記述例を確認します。
タイプで "AWS::EC2::InternetGateway"を指定。
プロパティはTagsのみです。
上記の参考ページの最後あたりに、参照情報として「AWS :: EC2 :: VPCGatewayAttachmentリソースを使用して、インターネットゲートウェイをVPCに関連付けます」とあります。
インターネットゲートウェイのアタッチメントが必要です。
インターネットゲートウェイのIDは、組み込み関数!Refを使用して、先ほど作成したインターネットゲートウェイを指定します。Refでリソースの論理名を指定すると、そのリソースを参照するために通常使用できる値(物理ID)を返します。
同様にVPCを指定します。
これでインターネットとVPC間の接続を有効にします。
PublicSubnetA:
Type: "AWS::EC2::Subnet"
Properties:
AvailabilityZone: "ap-northeast-3a"
CidrBlock: 10.0.0.0/24
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${SystemName}-public-subnet-a"
PublicRouteTableA:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${SystemName}-public-route-a"
サブネット作成します。
今回は大阪リージョンを使用しています。
Webブラウザで「Subnet CloudFormation」でWeb検索をかければ、該当ページがでてきます。
必要なプロパティはこのあたりでしょうか。
- AvailabilityZone(AZ)
- CidrBlock(CIDR)
- VpcId(VPC)
- Tags(タグ)
IPv6関連のプロパティは今回必要ないので、省きます。
参考ページの最後まで読んでも、特に追加で参照するページは載っていないようです。
次のアクションとしては、ルートテーブルを作成することです。
Webブラウザで「RouteTable CloudFormation」でWeb検索をかけると、該当のページがあります。
必要なプロパティはVpcIDとTagsです。
ページの最後にAWS::EC2::Routeを参照するように案内があります。
PublicRouteA:
Type: "AWS::EC2::Route"
Properties:
RouteTableId: !Ref PublicRouteTableA
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref InternetGateway
PublicSubnetARouteTableAssociation:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref PublicSubnetA
RouteTableId: !Ref PublicRouteTableA
「AWS :: EC2 :: Route」は、VPC内のルートテーブルでルートを指定します。
プロパティは多いですが、最低限のみ設定します。
- DestinationCidrBlock(宛先の一致に使用されるIPv4CIDRブロック)
- GatewayId(インターネットゲートウェイのID)
- RouteTableId(ルートテーブルのID)
ルートテーブルの宛先を「0.0.0.0/0の宛先はインターネットゲートウェイ」とします。
参考ページの最後あたりに特に追加で参照すべき情報は載っていません。
現時点ではルートテーブルとサブネットは関連づいていません。
Webブラウザで「routetable subnet CloudFormation」とWeb検索をかけます。
AWS::EC2::SubnetRouteTableAssociationがあります。サブネットとルートテーブルを関連づけます。
プロパティはシンプルにサブネットIDとルートテーブルIDです。
参考ページの最後には、特に追加で参照すべきページは載ってません。
では、vpc.ymlのコードをまとめます
vpc.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: The template is vpc
Parameters:
SystemName:
Type: String
Default: CFn-Test
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: 'true'
EnableDnsSupport: 'true'
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub ${SystemName}-vpc
InternetGateway:
Type: "AWS::EC2::InternetGateway"
Properties:
Tags:
- Key: Name
Value: !Sub "${SystemName}-igw"
InternetGatewayAttachment:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
PublicSubnetA:
Type: "AWS::EC2::Subnet"
Properties:
AvailabilityZone: "ap-northeast-3a"
CidrBlock: 10.0.0.0/24
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${SystemName}-public-subnet-a"
PublicRouteTableA:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${SystemName}-public-route-a"
PublicRouteA:
Type: "AWS::EC2::Route"
Properties:
RouteTableId: !Ref PublicRouteTableA
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref InternetGateway
PublicSubnetARouteTableAssociation:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref PublicSubnetA
RouteTableId: !Ref PublicRouteTableA
Outputs:
VPC:
Value: !Ref VPC
Export:
Name: !Sub ${AWS::StackName}-VPC
PublicSubnetA:
Value: !Ref PublicSubnetA
Export:
Name: !Sub ${AWS::StackName}-PublicSubnetA
コードの最後にOutputsセクションを追加しています。
この後EC2をテンプレートから作成しますが、その際にvpc.ymlの「VPC」と「PublicSubnetA」を参照する必要があります。
スタック参照ができるように出力します。
実際の構成では、マルチAZでの運用/プライベートサブネットを用意することが多いかと思いますが、必要な場合はドキュメントを参照して構築ください。今回と近い手順で構築できるはずです。
テンプレートが完成したので、S3にアップロードします。
前回と同じ手順でCloudFormationスタックを作成しましょう。
それぞれ作成されたリソースを確認します。
VPC
インターネットゲートウェイ
パブリックサブネット
ルートテーブルとの関連付け
以上ネットワークまわりの自動構築ができました。
またoutputsによって出力もなされています。
次はEC2を自動構築していきます。
4. EC2を自動作成してApacheテストページを表示
新規で「ec2.yml」ファイルを作成します。
ec2.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: The template is ec2
Parameters:
SystemName:
Type: String
Default: CFn-Test
EC2ImageId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-kernel-5.10-hvm-x86_64-gp2
Resources:
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${SystemName}-ec2-sg
GroupDescription: !Sub ${SystemName}-ec2-sg
VpcId:
Fn::ImportValue: !Sub ${SystemName}-vpc-VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-ec2-sg
EC2SecurityGroupIngressHttp:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref EC2SecurityGroup
IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref EC2ImageId
InstanceType: t2.micro
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
SubnetId:
Fn::ImportValue: !Sub ${SystemName}-vpc-PublicSubnetA
GroupSet:
- !Ref EC2SecurityGroup
UserData:
Fn::Base64: |
#!/bin/bash
sudo yum update -y
sudo yum -y install httpd
sudo systemctl start httpd.service
sudo systemctl enable httpd.service
Tags:
- Key: Name
Value: !Sub ${SystemName}-EC2
上から1つずつ見ていきます。
AWSTemplateFormatVersion: "2010-09-09"
Description: The template is ec2
Parameters:
SystemName:
Type: String
Default: CFn-Test
EC2ImageId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-kernel-5.10-hvm-x86_64-gp2
テンプレートの冒頭部分は、基本的にはvpcテンプレートと変わりありません。
ただしEC2作成時に最新のAmazon Linux2 AMIを利用したいので、こちらのページを参考にしました。AWS Systems Manager Parameter Storeから、最新のAMIを取得します。
Resources:
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${SystemName}-ec2-sg
GroupDescription: !Sub ${SystemName}-ec2-sg
VpcId:
Fn::ImportValue: !Sub ${SystemName}-vpc-VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-ec2-sg
EC2SecurityGroupIngressHttp:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref EC2SecurityGroup
IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
今回EC2を作成するにあたり、必要なのはセキュリティグループです。
※必要であれば適宜ロールの作成等も行ってください。
Webブラウザで「cloudformation SecurityGroup」とWeb検索します。
参考ページを確認します。
必要なプロパティは
- GroupName
- GroupDescription
- VpcId
- Tags
です。
VPCIDについては、vpcスタックから出力された値を参照しています。
Fn::ImportValueは。別のスタックによってエクスポートされた出力の値を返します。
インバウンドルールは別途、追加します。
- CidrIp (CIDR)
- IpProtocol (使用プロトコル=TCP)
- FromPort (80番ポート)
- ToPort (80番ポート)
- GroupId (セキュリティグループ)
上記のプロパティを指定します。
Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref EC2ImageId
InstanceType: t2.micro
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
SubnetId:
Fn::ImportValue: !Sub ${SystemName}-vpc-PublicSubnetA
GroupSet:
- !Ref EC2SecurityGroup
UserData:
Fn::Base64: |
#!/bin/bash
sudo yum update -y
sudo yum -y install httpd
sudo systemctl start httpd.service
sudo systemctl enable httpd.service
Tags:
- Key: Name
Value: !Sub ${SystemName}-EC2
EC2インスタンスを作成します。
Webブラウザから「Instance cloudformation」とWeb検索し、こちらのページを参照します。
大量のプロパティがありますが、今回は最低限のみ設定します。
- ImageId (AMI)
- InstanceType(インスタンスタイプ。t2.microを指定)
- NetworkInterfaces(パブリックIPアドレスの動的割り当て設定に必要)
- UserData(ユーザーデータ。Apacheを立ち上げるのに必要)
- Tags
AMIは、Parametersで設定したAmazon Linux2 AMIを使用します。
NetworkInterfacesは、インスタンスに接続するネットワークインターフェースを指定します。AssociatePublicIpAddressでパブリックIPアドレスの動的割り当てを実現します。
また今回テストとしてApacheのテストページを表示させたいので、インスタンス起動時にApacheインストール/起動設定を行うようにします。Fn::Base64は通常UserDataプロパティを介して、AmazonEC2インスタンスにエンコードされたデータを渡すために使用されます。
ユーザーデータの設定方法は、こちらを参考にしました。
以上がテンプレートの説明です。
ymlファイルの保存が完了したら、ec2.ymlファイルをS3にアップロードを行います。
またCloudFormationのスタック作成の手順はVPCのときと同じですが、スタック名は「CFn-Test-ec2」としておいてください。
スタックの作成が完了したら、先に進みます。
作成されたリソースを確認してみましょう。
セキュリティグループ
インバウンド
アウトバウンド
設定どおり、80番ポートのインバウンド通信が許可されています。
EC2
1台無事に稼働しています。
パブリックIPアドレスがふられているので、アクセスしてみましょう。
Apacheのテストページが表示されました。
CloudFormationの画面で、スタックを削除・S3バケットのymlファイルを削除して終了です。
さいごに
今回は各種ドキュメントを参照しながら、CloudFormationでネットワークまわりとEC2を作成しました。
またCloudFormationをテーマとして記事を書きたいと思います。
お疲れ様でした!!
Discussion