本記事の対象者
-
AWS初心者の方
-
AWS初心者向けのチュートリアル、ハンズオンなどでマネジメントコンソール画面を用いてシンプルな構成を構築した経験のある方
-
上記をマネジメントコンソールではなく、ファイルを使って構築することに関心のある方
本記事で行うこと
AWSのサービスのひとつであるCloudFormation用のテンプレートファイルを作成し、パブリックサーバー、プライベートサーバーを持つシンプルな構成を構築していきます。
テンプレートファイルとは
CloudFormationは、テンプレートファイルというものを読み込ませることでAWSのリソースを一括して構築してくれます。
テンプレートファイルの形式はJSONかYAMLを選択できますが、今回はYAMLで記述することにします。
エディタ
私は普段JetBrainsのIDEを使用しているのですが、CloudFormationのプラグインが存在するので今回導入しました。
プラグインを導入することで、リソースタイプやプロパティを補完してくれるようになります(リソースタイプやプロパティがそもそもどういったものなのかについては後述)。
また、2つ目のスクリーンショットではPropeties
にエラー(赤い下線)が表示されていますが、これは各AWSリソースタイプに対して必須のプロパティが記述されているかどうかをチェックしてくれています。
1. テンプレートファイルの作成
では、ここからテンプレートファイルの作成に入ります。
1.1. テンプレートフォーマットバージョンの指定
まず、テンプレートのフォーマットバージョンを指定します。
指定可能な値は2010-09-09
のみです。
AWSTemplateFormatVersion: 2010-09-09
なお、この行の記述は任意なので、無かったとしてもCloudFormationはエラーにはなりません。
1.2. リソースの記述方法
ここからVPC等、各種のAWSリソースを作成するための記述を行なっていきます。
リソースの記述方法は以下になります。
Resources:
<Logical ID>:
Type: <Resource type>
Properties:
<Set of properties>
Logical ID
Logical ID
は、そのリソースに付ける名前です。
同一テンプレートファイル内で、他と被らない一意なものにする必要があります。
Resource Type
Resource Type
の部分には、作成するAWSリソースの種類を指定します。
具体的にはリソースタイプ識別子というものを指定します。
リソースタイプ識別子は、
service-provider::service-name::data-type-name
という形式になっていて、例えば
- VPCだったら、
AWS::EC2::VPC
- サブネットだったら
AWS::EC2::Subnet
となっています。
一覧は以下から確認できます。
- [AWS リソースおよびプロパティタイプのリファレンス - AWSドキュメント] (https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html)
Set of propeties
Set of propeties
の部分には、各リソースのプロパティを記述します。
リソースの種類に応じて必須のプロパティと任意のプロパティがあります。
1.3. VPCの定義
まず、はじめにVPCを定義します。
# 最終行に以下を追加
Resources:
CfVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
Tags:
-
Key: Name
Value: CfVpc
マネジメントコンソールの場合
上記は、マネジメントコンソールのVPCの作成
において、下記の指定でVPCを作成するのと同等です。
項目 | 設定内容 |
---|---|
名前タグ | CfVpc |
IPv4 CIDRブロック | 10.0.0.0/16 |
IPv6 CIDRブロック | IPv6 CIDRブロックなし |
テナンシー | デフォルト |
2. テンプレートファイルを使って実際にAWSリソースを作成してみる
まだVPCしか定義はしていませんが、いったんこの段階でCloudFormationを使ってAWSリソースを作成してみます。
2.1. スタックの作成
CloudFormationでは、テンプレートファイルで定義されたAWSリソース群をスタックという単位で管理します。
テンプレートファイルを使ってAWSリソースを作成するには、CloudFormationの画面でまずスタックの作成
を選択します。
2.2. テンプレートの指定
テンプレートの準備完了
> テンプレートのファイルのアップロード
> ファイルの選択
を選び、作成したテンプレートファイルをアップロードしたら、次へ
を押します。
2.3. スタックの詳細を指定
スタックの名前
を決めます。ここではtraining
としておきます。
パラメータ
は本記事では使わないので、そのまま次へ
を押します。
2.4. スタックオプションの設定/レビュー
続いて、スタックオプションの設定画面が表示されるので、そのまま次へ
を押します。
最後に、レビュー画面(確認画面)が表示されるので、スタックの作成
を押します。
2.5. 各リソースの作成状況の確認
スタックの作成
を押すと、スタックの詳細画面のイベントタブが表示された状態になります。
画面下部のイベント欄に、リソースの作成状況が順次表示されていきます(丸矢印ボタンで更新できます)。
緑色のCREATE_COMPLETE
が表示されれば、そのリソースは作成完了です。
最終的にスタック名(今回の場合であればtraining
)で、CREATE_COMPLETE
が表示されれば全てのリソースの作成が正常に完了したことになります。
2.6. スタックの削除
スタック画面か、もしくはスタックの詳細画面画面上部の削除
を押し、さらに確認のダイアログで表示されたスタックの削除
を押すと、このスタックに紐付く全てのリソースが一括削除されます。
3. テンプレートファイル作成の続き
CloudFormationでのリソースの作成方法はわかったので、テンプレートファイルの作成を再開します。
本記事では割愛しますが、この先でリソースを追記したテンプレートファイルは、追記の都度CloudFormationの画面でアップロードして、実際にリソースを作成できるか確認してみても良いかと思います。(リソースを作成した後は、適宜スタックごと削除してください)。
3.1. サブネットの定義
VPCの次は、サブネットを2つ定義します。
1つ目はパブリックサブネット、2つ目はプライベートサブネットとします。
# 最終行に以下を追加
CfPublicSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.5.0/24
MapPublicIpOnLaunch: true
VpcId: !Ref CfVPC
AvailabilityZone: ap-northeast-1a
Tags:
- Key: Name
Value: CfPublicSubnet
CfPrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.10.0/24
MapPublicIpOnLaunch: false
VpcId: !Ref CfVPC
AvailabilityZone: ap-northeast-1c
Tags:
- Key: Name
Value: CfPrivateSubnet
マネジメントコンソールの場合
1つ目のパブリックサブネットの内容については、マネジメントコンソールの各画面で以下を指定した場合と同等です(2つ目のプライベートサブネットについては割愛)。
サブネットの作成画面
項目 | 設定内容 |
---|---|
名前タグ | CfPublicSubnet |
VPC | CIDRが10.0.0.0/16 である、作成済みのVPC |
アベイラビリティゾーン | ap-northeast-1a |
IPv4 CIDR ブロック | 10.0.5.0/24 |
アクション > 自動割り当てIP設定
項目 | 設定内容 |
---|---|
パブリックIPv4アドレスの自動割り当てを有効にする | チェックを入れる |
MapPublicIpOnLaunchについて
YAML内のMapPublicIpOnLaunch
は、パブリックIPアドレスの自動割り当てに該当します。
MapPublicIpOnLaunch: true
### !Refについて
YAML内の`!Ref`についてはCloudFormationで使用できる組み込み関数のひとつです。
>```yaml:
Resources:
CfVPC:
# 略
CfPublicSubnet:
Type: AWS::EC2::Subnet
Properties:
# 略
VpcId: !Ref CfVPC
!Ref リソースの論理ID
とすることで、そのリソースの物理IDが返ります。
マネジメントコンソール画面で順次リソースを作成していく場合と異なり、CloudFormationで一括してリソースを定義する場合には、このサブネットが属するVPCの物理IDがまだ決まっていませんので、このように!Ref
関数を使います。
!Refによるリソース作成順序への影響
また、!Ref
を使用することで、参照先のリソースが先に作成されるようになります。
例えば、
Resources:
CfPublicSubnet:
Type: AWS::EC2::Subnet
Properties:
# 略
VpcId: !Ref CfVPC
# 略
CfVPC:
Type: AWS::EC2::VPC
# 略
のようにテンプレートファイル中でのリソース定義順序が
1. サブネット
2. VPC
の順だったとしても、サブネットがVPCを`!Ref`で参照していると、リソースの作成順序は
1. VPC
2. サブネット
の順となります。
- [参考: Ref - AWSドキュメント](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html)
## 3.2. インターネットゲートウェイの定義とVPCへのアタッチ
続いて、インターネットゲートウェイを定義し、VPCへアタッチします。
```yaml
# 最終行に以下を追加
CfInternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: CfInternetGateway
CfAttachCfInternetGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId : !Ref CfInternetGateway
VpcId: !Ref CfVPC
-
まず
Type
にAWS::EC2::InternetGateway
を指定し、インターネットゲートウェイを定義します。 -
次に
Type
にAWS::EC2::VPCGatewayAttachment
を指定し、インターネットゲートウェイをVPCにアタッチします。-
InternetGatewayId
とVpcId
で、どのインターネットゲートウェイをどのVPCにアタッチするのか指定しています。
-
マネジメントコンソールの場合
上記は、マネジメントコンソールにおいて、下記の指定でインターネットゲートウェイを作成するのと同等です。
インターネットゲートウェイの作成画面
項目 | 設定内容 |
---|---|
名前タグ | CfInternetGateway |
アクション > VPCにアタッチ の画面
項目 | 設定内容 |
---|---|
VPC | CIDRが10.0.0.0/16 である、作成済みのVPC |
3.3. パブリックサブネット用ルートテーブルの定義およびルートの定義
# 最終行に以下を追加
CfRouteTableForPublicSubnet:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref CfVPC
Tags:
- Key: Name
Value: CfRouteTableForPublicSubnet
CfRouteForPublicSubnet:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref CfRouteTableForPublicSubnet
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref CfInternetGateway
CfAssocciateRouteTableForPublicSubnet:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref CfRouteTableForPublicSubnet
SubnetId: !Ref CfPublicSubnet
-
まず
Type
にAWS::EC2::RouteTable
を指定し、ルートテーブルを作成しています。 -
次に
Type
にAWS::EC2::Route
を指定し、ルートを追加しています。-
DestinationCidrBlock
(送信先)を、0.0.0.0/0
(フルオープン)としています。 - ターゲット(
GatewayId
)に、定義済みのインターネットゲートウェイ(!Ref CfInternetGateway
)を指定しています。
-
-
続いて
Type
にAWS::EC2::SubnetRouteTableAssociation
を指定し、ルートテーブルをサブネットに関連付けしています。-
RouteTableId
とSubnetId
で、どのルートテーブルをどのサブネットに関連付けするのか指定しています。
-
マネジメントコンソールの場合
マネジメントコンソールの場合、下記の指定でルートテーブルを作成するのと同等です。
ルートテーブルの作成画面
項目 | 設定内容 |
---|---|
名前タグ | CfRouteTableForPublicSubnet |
VPC | CIDRが10.0.0.0/16 である、作成済みのVPC |
アクション > ルートの編集 > ルートの追加 の画面
項目 | 設定内容 |
---|---|
送信先 | 0.0.0.0/0 |
ターゲット | 名前タグ(Name)がCfInternetGateway であるインターネットゲートウェイ |
アクション > サブネットの関連付けの編集 の画面
- 名前タグ(Name)が
CfPublicSubnet
であるサブネットを選択して、保存
3.4. パブリックサーバー用セキュリティグループの定義
# 最終行に以下を追加
CfSecurityGroupForPublicServer:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: CfSecurityGroupForPublicServer
GroupDescription: CfSecurityGroupForPublicServer
VpcId: !Ref CfVPC
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
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: CfSecurityGroupForPublicServer
-
セキュリティグループを作成するため、
Type
にAWS::EC2::SecurityGroup
を指定します。 -
インバウンドルールは、
SecurityGroupIngress
プロパティに指定します。- ここでは、SSH(22), HTTP(80), HTTPS(443)でのアクセスを許可しています。
マネジメントコンソールの場合
マネジメントコンソールの場合、下記の指定でセキュリティグループを作成するのと同等です。
セキュリティグループの作成画面
項目 | 設定内容 |
---|---|
セキュリティグループ名 | CfSecurityGroupForPublicServer |
説明 | CfSecurityGroupForPublicServer |
VPC | CIDRが10.0.0.0/16 である、作成済みのVPC |
アクション > インバウンドのルールの編集画面
以下のルールを設定
タイプ | プロトコル | ポート範囲 | ソース |
---|---|---|---|
SSH | TCP | 22 | 0.0.0.0/0 |
HTTP | TCP | 80 | 0.0.0.0/0 |
HTTPS | TCP | 443 | 0.0.0.0/0 |
3.5. パブリックサーバーの定義
# 最終行に以下を追加
CfPublicServer:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0ff21806645c5e492
InstanceType: t2.micro
KeyName: cf-key-pair
# SubnetId: !Ref CfPublicSubnet
# SecurityGroupIds:
# - !Ref CfSecurityGroupForPublicServer
NetworkInterfaces:
- SubnetId: !Ref CfPublicSubnet
GroupSet:
- !Ref CfSecurityGroupForPublicServer
AssociatePublicIpAddress: true
DeviceIndex : 0
Tags:
- Key: Name
Value: CfPublicServer
-
EC2を作成するため、
Type
にAWS::EC2::Instance
を指定します。 -
ImageId
でAMI(Amazonマシンイメージ)を指定します。- ここでは、
Amazon Linux 2(x86)
のAMIを指定しています。 - 具体的にどんなIDを指定するかについてですが、AMI IDはマネジメントコンソールのEC2作成画面の途中に表示されているので、今回は事前にそちらを確認して指定しています。
- ここでは、
-
KeyName
にはキーペア名を指定します。ここでは事前にマネジメントコンソール画面で作成しておいたキーペア名を指定しています。- なお、キーペアは、マネジメントコンソールの
EC2 > キーペア > キーペアの作成
で作成できます。
- なお、キーペアは、マネジメントコンソールの
自動割り当てパブリックIPの設定を有効にする場合の注意点
SubnetId: !Ref CfPublicSubnet
SecurityGroupIds:
- !Ref CfSecurityGroupForPublicServer
`SubnetId`と`SecurityGroup`がコメントアウトしてありますが、自動割り当てパブリックIPを有効に**しない**のであれば、コメントアウト前の記述で特に問題ありません。
今回のように自動割り当てパブリックIPを有効にする場合は、以下のように`NetworkInterfaces`プロパティ内の`Subnet`プロパティと`GroupSet`プロパティに、サブネットとセキュリティグループそれぞれを記述する必要があります。
>```yaml
NetworkInterfaces:
- SubnetId: !Ref CfPublicSubnet
GroupSet:
- !Ref CfSecurityGroupForPublicServer
AssociatePublicIpAddress: true
DeviceIndex : 0
もし、上記のように記述しなかった場合、以下のエラーになります。
Network interfaces and an instance-level subnet ID/security groups may not be specified on the same request.(ネットワークインターフェイスとインスタンスレベルのサブネットID/セキュリティグループは、同じリクエストで指定できません)
自動割り当てパブリックIPの有効化そのものはAssociatePublicIpAddress
をtrue
とすることで設定できます。
また、NetworkInterfaces
プロパティ内の配列の各要素(つまり個々のNetworkInterface
)には、DeviceIndex
プロパティが必須となっているので、これも指定しています。
なお、DeviceIndex
が無いと、
Property DeviceIndex cannot be empty
というエラーになります
参考: NetworkInterface - AWSドキュメント
3.6. Elastic IPの定義
パブリックサーバーのパブリックIPアドレスについて、起動のたびに変更されることが無いよう、Elastic IPを定義し、パブリックサーバーに関連付けします。
# 最終行に以下を追加
CfElasticIpForPublicServer:
Type: AWS::EC2::EIP
Properties:
InstanceId: !Ref CfPublicServer
-
Elastic IPを作成するため、
Type
にAWS::EC2::EIP
を指定します。 -
このElastic IPをパブリックサーバーに関連付けするため、
InstanceId
にパブリックサーバーを指定します。
3.7. プライベートサーバー用セキュリティグループの定義
# 最終行に以下を追加
CfSecurityGroupForPrivateServer:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: CfSecurityGroupForPrivateServer
GroupDescription: CfSecurityGroupForPrivateServer
VpcId: !Ref CfVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
SourceSecurityGroupId: !Ref CfSecurityGroupForPublicServer
Tags:
- Key: Name
Value: CfSecurityGroupForPrivateServer
-
インバウンドルールを、
SecurityGroupIngress
プロパティに指定します。- パブリックサーバーからのみプライベートサーバーにアクセスできるよう、ソース(
SourceSecurityGroupId
)には、パブリックサーバー用のセキュリティグループを指定しています。
- パブリックサーバーからのみプライベートサーバーにアクセスできるよう、ソース(
マネジメントコンソールの場合
マネジメントコンソールの場合、下記の指定でインバウンドのルールを編集するのと同等です。
アクション > インバウンドのルールの編集画面
以下のルールを設定
タイプ | プロトコル | ポート範囲 | ソース |
---|---|---|---|
SSH | TCP | 22 | パブリックサーバー用セキュリティグループのグループID |
3.8. プライベートサーバーの定義
# 最終行に以下を追加
CfPrivateServer:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0ff21806645c5e492
InstanceType: t2.micro
KeyName: cf-key-pair
SubnetId: !Ref CfPrivateSubnet
SecurityGroupIds:
- !Ref CfSecurityGroupForPrivateServer
Tags:
- Key: Name
Value: CfPrivateServer
-
SubnetId
でプライベートサブネットを、SecurityGroupIds
でプライベートサーバー用セキュリティグループを指定しています。 -
パブリックサーバーの場合は、自動割り当てパブリックIPアドレスを有効にする関係で、サブネットとセキュリティグループを以下のように
NetworkInterfaces
プロパティの下に指定する必要がありましたが、今回はそのようにする必要はありません。
NetworkInterfaces:
- SubnetId: !Ref CfPublicSubnet
GroupSet:
- !Ref CfSecurityGroupForPublicServer
AssociatePublicIpAddress: true
DeviceIndex : 0
# 4. もう一度テンプレートファイルを使ってAWSリソースを作成する
ここまで作成してきたテンプレートファイルを使って、実際にAWSリソースを作成してみます。
- なお、テンプレートファイル全体はこちら:point_down:
- https://github.com/shonansurvivors/CloudFormation-training/blob/master/training.yml
CloudFormation上の操作の流れは、`2. テンプレートファイルを使って実際にAWSリソースを作成してみる`と同じです。
スタックの作成開始から完了までにかかる時間は、1分半ほど。
☕️でも飲んで待ちましょう。
# 最後に
以上で、本記事でのCloudFormationによるパブリックサーバー/プライベートサーバーの構築は完了です。
パラメータなど、CloudFormationの機能をまだまだ活用はできていないので、もし第二弾的な記事を書くことがあればその辺りにも触れたいと思います。
- [参考: パラメータ - AWSドキュメント](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html)
また、TypeScriptやPythonのコードでCloudFormationのテンプレートファイルを生成できる、AWS CDKについても今後記事を書ければと思います。
# 参考
- [AWS CloudFormation ユーザーガイド](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/Welcome.html)
- [【CloudFormation入門1】5分と6行で始めるAWS CloudFormationテンプレートによるインフラ構築 - Developers.IO](https://dev.classmethod.jp/cloud/aws/cloudformation-beginner01/)
- [CloudFormationで自動割り当てパブリックIPを有効にしてインスタンスを構築する - かべぎわブログ](https://www.kabegiwablog.com/entry/2017/10/17/201613)
- [【AWS初心者向け】AWSでWebサーバーとデータベースを構築して、ブラウザにデータを表示する【ハンズオン】](https://qiita.com/MayForBlue/items/c96674fb6aac5852a978)