目次
記事の概要
ネットオンで開発エンジニアをしている竹田と言います。
最近AWS資格の勉強をしている中で特に気になったCloudFormationについて初心者向けのハンズオンを作成しました。
目的
CloudFormtionで2層アーキテクチャ構造を構築し、パブリックなインスタンスからプライベートのインスタンスにSSH接続するという所までやっていこうと思います。今回は詳細なプロパティな説明はあまりせず、こんな感じなんだなぁと体験して頂きたいです。
この記事はこういう方にオススメ
AWS初心者でCloudFormationに初めて触れる方。
AWSコンソールからVPC、サブネット、EC2設定をした経験がある方だと理解しやすいと思います
CloudFormationとは?
AWS CloudFormation によって、AWS 関連リソースおよびサードパーティーリソースの集合体を Infrastructure as Code として扱うことでモデリングし、高速かつ安定的にプロビジョニングし、ライフサイクル全体にわたって管理することが容易になります。必要なリソースとその依存関係を CloudFormation テンプレートに記述すれば、1 つのスタックとして一括で起動し、設定できます。必要なときにはいつでもテンプレートを使用し、スタック全体を 1 つのユニットとして作成、更新、削除できます。リソースを個別に管理する必要はありません。複数の AWS アカウント、複数の AWS リージョンにわたって、スタックの管理とプロビジョニングが可能です。
https://aws.amazon.com/jp/cloudformation/features/
EC2インスタンスやVPCやサブネット等のAWS上のリソースの作成をJSONやYAMLファイルを使ってコード上で管理できることできます。同じ環境を何度も作成する際に便利です。
今回作るもの
VPC+[プライベートサブネット+EC2]+[パブリックサブネット+EC2]構築を行い最後にプライベートサブネット内のEC2にSSH接続するまでを行いたいと思います。
結果だけぱっと見たい方は完成物を置いてあるのでそれ以下の手順を実行してみてください
VScodeの拡張機能をインストール。今回はなくても大丈夫です。
拡張機能をインストールしておくことでテンプレートを作る際の雛形や構文チェックを行うことができます。
拡張機能のページにある説明文にあるとおりにvscodeのsetting.jsonに説明通りにコピペしておく必要があります。
これでvscodeがcloudformationの関数を認識できるようになります。
手順1 ひな形を作る
では早速作っていこうと思います。まず初めにcloudformation.ymlファイルを新規作成します。
そしてstartと入力すると先ほどインストールした拡張機能によりひな形を一気に作成することができます。
以下のようにyamlファイルにcloudformation用のテンプレートが展開されます。
AWSTemplateFormatVersion: 2010-09-09 Description: --- Metadata: Parameters: Mappings: Conditions: Resources: Outputs:
今回のCloudFormation作成に必要なAWSTemplateFormatVersion、Description、Resources以外の項目は消してしまいます。他の項目は他のテンプレートと連携したり、再利用性を高めるために使われる項目ですので一旦無視します。
AWSTemplateFormatVersionはテンプレートの形式バージョンです。2010-09-09のままで構いません。
Descriptionはこのテンプレートの説明文を書く項目でtestCfnTemplateとしておきましょう
Resourcesは言葉の通りAWSのリソースのことでVPCやEC2のリソースの情報を書き込んでいきます。
AWSTemplateFormatVersion: 2010-09-09 Description: TestCfnTemplate Resources:
手順2 VPCの作成
CIDRブロック 20.0.0.0/16 のVPCを作成します。
testVPCというのは論理IDでyamlファイル内でのVPCの名前です。他のリソースから参照するために使います。
Typeはリソースの種類を示しています。AWS::EC2::VPC
はVPCのリソースになります。
Tagsはこのリソースに名前付け(タグ付け)を行っています。タグは書いておいた方が後々見やすくなります。
AWSTemplateFormatVersion: 2010-09-09 Description: TestCfnTemplate Resources: # VPC testVPC: #論理ID Type: AWS::EC2::VPC Properties: CidrBlock: 20.0.0.0/16 EnableDnsSupport: true Tags: - Key: Name Value: CFN_TEST_VPC
手順3 サブネットの作成
CIDRブロック 20.0.1.0/24 のプライベートサブネットと
CIDRブロック 20.0.2.0/24のパブリックサブネットを作成します
VpcId: !Ref testVPC ←この記述は手順2で作成したVPCに属するサブネットだという記述です。
# subnet testPrivateSubnet: #論理ID Type: AWS::EC2::Subnet Properties: AvailabilityZone: us-west-1a VpcId: !Ref testVPC CidrBlock: 20.0.1.0/24 Tags: - Key: Name Value: CFN_TEST_PRIVATE_SUBNET # subnet testPublicSubnet: #論理ID Type: AWS::EC2::Subnet Properties: AvailabilityZone: us-west-1a VpcId: !Ref testVPC CidrBlock: 20.0.2.0/24 Tags: - Key: Name Value: CFN_TEST_PUBLIC_SUBNET
手順4 セキュリティグループの作成
SSHで接続するため22番ポートに全てのIPからの通信を許可するセキュリティグループを作成します。
# security group testSecGroup: #論理ID Type: AWS::EC2::SecurityGroup Properties: GroupName: testSG_1 GroupDescription: testSG_1 VpcId: !Ref testVPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 0.0.0.0/0 Tags: - Key: Name Value: CFN_TEST_SG
手順 5 ネットワーク ACL の作成
プライベートサブネットとパブリックサブネット用に2つのネットワークACLを作成します。
VpcId: !Ref testVPC
は手順2で作成したVPC内のACLだという記述です。
# networkACL testNetworkAclPublic: Type: AWS::EC2::NetworkAcl Properties: VpcId: !Ref testVPC Tags: - Key: Name Value: CFN_TEST_NETWORK_ACL_PUBLIC testNetworkAclPrivate: Type: AWS::EC2::NetworkAcl Properties: VpcId: !Ref testVPC Tags: - Key: Name Value: CFN_TEST_NETWORK_ACL_PRIVATE
手順6 ネットワークACLとサブネットの紐づけ
パブリックサブネット「testPublicSubnet」とネットワークACL「testNetworkAclPublc」の紐づけと
プライベートサブネット「testNetworkAclPrivate」とネットワークACL「testNetworkAclPublc」の紐づけを行います。
# networkACLとsubnetの関連付け testNetworkAssocPublic: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: NetworkAclId: !Ref testNetworkAclPublic SubnetId: !Ref testPublicSubnet testNetworkAssocPrivate: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: NetworkAclId: !Ref testNetworkAclPrivate SubnetId: !Ref testPrivateSubnet
手順7 インバウンドルールとアウトバウンドルールの作成
全ての通信を許可するアウトバウンドルールとインバウンドルールをプライベートサブネットとパブリックサブネット用それぞれに作成します。今回本筋ではないのでセキュリティの設定は全て許可しています。
# インバウンドルール全許可(パブリックサブネット) testACLInRulepublic: Type: AWS::EC2::NetworkAclEntry Properties: Egress: false RuleNumber: 100 RuleAction: allow Protocol: -1 CidrBlock: 0.0.0.0/0 NetworkAclId: !Ref testNetworkAclPublic # アウトバウンドルール全許可(パブリックサブネット) testAclOutRulePublic: Type: AWS::EC2::NetworkAclEntry Properties: Egress: true RuleNumber: 100 RuleAction: allow Protocol: -1 CidrBlock: 0.0.0.0/0 NetworkAclId: !Ref testNetworkAclPublic # インバウンドルール全許可(プライベートサブネット) testACLInRulePrivate: Type: AWS::EC2::NetworkAclEntry Properties: Egress: false RuleNumber: 100 RuleAction: allow Protocol: -1 CidrBlock: 0.0.0.0/0 NetworkAclId: !Ref testNetworkAclPrivate # アウトバウンドルール全許可(プライベートサブネット) testAclOutRulePrivate: Type: AWS::EC2::NetworkAclEntry Properties: Egress: true RuleNumber: 100 RuleAction: allow Protocol: -1 CidrBlock: 0.0.0.0/0 NetworkAclId: !Ref testNetworkAclPrivate
手順8 ルートテーブルの作成
ルートテーブルを作成します。
VpcId: !Ref testVPC
手順2で作成したVPC内のルートテーブルだと示しています。
# ルートテーブル testRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref testVPC Tags: - Key: Name Value: CFN_TEST_ROUTETABLE
手順9 ルートテーブルのルート設定
ルートテーブルのルート設定です。
0.0.0.0/0の通信を全て手順11で作成するインターネットゲートウェイに向けます。
testRouteSetting: Type: AWS::EC2::Route Properties: RouteTableId: !Ref testRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref testIGW
手順10 ルートテーブルとパブリックサブネットの関連付け
ルートテーブルtestRouteTable
とパブリックサブネットtestPublicSubnet
を関連付けします
# ルートテーブルとパブリックサブネットの関連付け testRouteTableAssoc: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref testPublicSubnet RouteTableId: !Ref testRouteTable
手順11 インターネットゲートウェイの作成
ルートテーブルの通信の向き先であるインターネットゲートウェイを作成します。手順9で関連付けられる予定だったインターネットゲートウェイです。
# インターネットゲートウェイ testIGW: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: CFN_TEST_IGW
手順12 インターネットゲートウェイとVPCの関連付け
手順2で作成したVPCとインターネットゲートウェイを関連付けします。
VpcId: !Ref testVPC
testAttachIGW: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref testVPC InternetGatewayId: !Ref testIGW
手順13 インスタンスの作成(パブリックサブネット内)
ようやくインスタンス設定です。
パブリックサブネット内にt2.microのインスタンスを立てます。
testEC2InstancePublic: Type: AWS::EC2::Instance Properties: KeyName: testkeypair DisableApiTermination: false ImageId: ami-018d291ca9ffc002f InstanceType: t2.micro Monitoring: false NetworkInterfaces: - AssociatePublicIpAddress: true DeviceIndex: "0" SubnetId: !Ref testPublicSubnet #パブリックサブネット内に配置 GroupSet: - !Ref testSecGroup #セキュリティグループの設定 Tags: - Key: Name Value: CFN_TEST_PUBLIC_INSTANCE
手順14 インスタンスの作成(プライベートサブネット内)
こちらはプライベートサブネット内のEC2インスタンスになります。
# インスタンス(プライベート) testEC2InstancePrivate: Type: AWS::EC2::Instance Properties: KeyName: testkeypair DisableApiTermination: false ImageId: ami-018d291ca9ffc002f InstanceType: t2.micro Monitoring: false NetworkInterfaces: - AssociatePublicIpAddress: false DeviceIndex: "0" SubnetId: !Ref testPrivateSubnet GroupSet: - !Ref testSecGroup Tags: - Key: Name Value: CFN_TEST_PRIVATE_INSTANCE
完成形
最終の成果物は以下になります。
AWSTemplateFormatVersion: 2010-09-09 Description: TestCfnTemplate Resources: # VPC testVPC: # 論理ID Type: AWS::EC2::VPC Properties: CidrBlock: 20.0.0.0/16 EnableDnsSupport: true Tags: - Key: Name Value: CFN_TEST_VPC # subnet testPrivateSubnet: Type: AWS::EC2::Subnet Properties: AvailabilityZone: us-west-1a VpcId: !Ref testVPC CidrBlock: 20.0.1.0/24 Tags: - Key: Name Value: CFN_TEST_PRIVATE_SUBNET # subnet testPublicSubnet: Type: AWS::EC2::Subnet Properties: AvailabilityZone: us-west-1a VpcId: !Ref testVPC CidrBlock: 20.0.2.0/24 Tags: - Key: Name Value: CFN_TEST_PUBLIC_SUBNET # security group testSecGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: testSG_1 GroupDescription: testSG_1 VpcId: !Ref testVPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 0.0.0.0/0 Tags: - Key: Name Value: CFN_TEST_SG # networkACL testNetworkAclPublic: Type: AWS::EC2::NetworkAcl Properties: VpcId: !Ref testVPC Tags: - Key: Name Value: CFN_TEST_NETWORK_ACL_PUBLIC testNetworkAclPrivate: Type: AWS::EC2::NetworkAcl Properties: VpcId: !Ref testVPC Tags: - Key: Name Value: CFN_TEST_NETWORK_ACL_PRIVATE # networkACLとsubnetの関連付け testNetworkAssocPublic: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: NetworkAclId: !Ref testNetworkAclPublic SubnetId: !Ref testPublicSubnet testNetworkAssocPrivate: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: NetworkAclId: !Ref testNetworkAclPrivate SubnetId: !Ref testPrivateSubnet # インバウンドルール全許可(パブリックサブネット) testACLInRulepublic: Type: AWS::EC2::NetworkAclEntry Properties: Egress: false RuleNumber: 100 RuleAction: allow Protocol: -1 CidrBlock: 0.0.0.0/0 NetworkAclId: !Ref testNetworkAclPublic # アウトバウンドルール全許可(パブリックサブネット) testAclOutRulePublic: Type: AWS::EC2::NetworkAclEntry Properties: Egress: true RuleNumber: 100 RuleAction: allow Protocol: -1 CidrBlock: 0.0.0.0/0 NetworkAclId: !Ref testNetworkAclPublic # インバウンドルール全許可(プライベートサブネット) testACLInRulePrivate: Type: AWS::EC2::NetworkAclEntry Properties: Egress: false RuleNumber: 100 RuleAction: allow Protocol: -1 CidrBlock: 0.0.0.0/0 NetworkAclId: !Ref testNetworkAclPrivate # アウトバウンドルール全許可(プライベートサブネット) testAclOutRulePrivate: Type: AWS::EC2::NetworkAclEntry Properties: Egress: true RuleNumber: 100 RuleAction: allow Protocol: -1 CidrBlock: 0.0.0.0/0 NetworkAclId: !Ref testNetworkAclPrivate # ルートテーブル testRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref testVPC Tags: - Key: Name Value: CFN_TEST_ROUTETABLE # ルートテーブルのルート設定 testRouteSetting: Type: AWS::EC2::Route Properties: RouteTableId: !Ref testRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref testIGW # ルートテーブルとパブリックサブネットの関連付け testRouteTableAssoc: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref testPublicSubnet RouteTableId: !Ref testRouteTable # インターネットゲートウェイ testIGW: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: CFN_TEST_IGW # インターネットゲートウェイとVPCの関連付け testAttachIGW: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref testVPC InternetGatewayId: !Ref testIGW # インスタンス(パブリック) testEC2InstancePublic: Type: AWS::EC2::Instance Properties: KeyName: testkeypair DisableApiTermination: false ImageId: ami-018d291ca9ffc002f InstanceType: t2.micro Monitoring: false NetworkInterfaces: - AssociatePublicIpAddress: true DeviceIndex: "0" SubnetId: !Ref testPublicSubnet GroupSet: - !Ref testSecGroup Tags: - Key: Name Value: CFN_TEST_PUBLIC_INSTANCE # インスタンス(プライベート) testEC2InstancePrivate: Type: AWS::EC2::Instance Properties: KeyName: testkeypair DisableApiTermination: false ImageId: ami-018d291ca9ffc002f InstanceType: t2.micro Monitoring: false NetworkInterfaces: - AssociatePublicIpAddress: false DeviceIndex: "0" SubnetId: !Ref testPrivateSubnet GroupSet: - !Ref testSecGroup Tags: - Key: Name Value: CFN_TEST_PRIVATE_INSTANCE
手順15 キーペアの作成
以下より作業は北カリフォルニアリージョンにて行ってください。リソースを北カリフォルニアリージョンで展開する用のCloudFormationになっているためです。
こちらはAWSコンソール画面から手作業で作成します。SSH接続するのにキーペアをDLする必要があります。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-console-create-keypair.html
EC2のキーペア作成画面から名前をtestkeypair、キーペアのタイプをRSA、キーファイル形式を.pemで作成します。
キーペアを作成するとtestkeypair.pemがダウンロードされます。このキーファイルを用いてSSH接続します。
手順16 作成したファイルをもとにAWSリソースを作成する。
CloudFormationを開きスタック作成を選択しテンプレートの指定を「テンプレートファイルのアップロード」で今回作成したcloudformation.ymlを選択し次を押します。
スタックの名前を「testCF」とします
スタックのオプション設定は何もせず「次へ」を選択し最後の画面でスタックの作成を押します。
手順17 パブリックサブネット内のEC2にSSH接続する
手順16が上手くいくと2つのインスタンスが起動されています。パブリックサブネット内にEC2インスタンスにSSH接続するコマンドが以下の画像のように「CFN_TEST_PUBLIC_INSTANCE」にチェックを入れた状態で「接続」を押すと
インスタンスに接続という画面が開き「SSHクライアント」タグを押すと「ssh -i “testkeypair.pem” ec2-user@xx.xxx.xxx.xxx」が表示されますのでそのコマンドを実行します。手順15で落としてきたtestkeypairを使用します。
SSH接続して以下のように表示されると成功です。
$ ssh -i "testkeypair.pem" ec2-user@54.219.152.135 Last login: Wed Sep 14 06:23:13 2022 from fsa056f3b4.oski405.ap.nuro.jp __| __|_ ) _| ( / Amazon Linux 2 AMI ___|\___|___| https://aws.amazon.com/amazon-linux-2/ 4 package(s) needed for security, out of 11 available Run "sudo yum update" to apply all updates.
手順18 パブリックサブネットのEC2にキーペアをファイル送信する。
SCPコマンドを使ってキーペアをインスタンスに送信します。送信先は/tmpです。
$ scp -i testkeypair.pem -r testkeypair.pem ec2-user@54.219.152.135:/tmp testkeypair.pem 100% 1674 11.6KB/s 00:00
パブリックサブネットのec2にログインしキーペアがある事を確認します。
$ ssh -i "testkeypair.pem" ec2-user@54.219.152.135 Last login: Wed Sep 14 06:26:12 2022 from fsa056f3b4.oski405.ap.nuro.jp __| __|_ ) _| ( / Amazon Linux 2 AMI ___|\___|___| https://aws.amazon.com/amazon-linux-2/ 4 package(s) needed for security, out of 11 available Run "sudo yum update" to apply all updates. [ec2-user@ip-20-0-2-89 ~]$ ls /tmp systemd-private-ed0f4ef458d44e4e80fbaff3361e2e37-chronyd.service-JYnZGb testkeypair.pem
パブリックインスタンス内のキーペアの権限を他のユーザーから使用できないようにするコマンドを実行します。
chmod 600 /tmp/testkeypair.pem
手順19 プライベートサブネットのEC2インスタンスにSSH接続する。
手順17の作業をプライベートサブネット内のEC2インスタンスに対しても行い接続コマンドを取得します。
手順18の作業をしているとパブリックサブネット内のec2インスタンスにSSH接続している状態なので、パブリックサブネット内のEC2に配置したキーペアを使用してパブリックサブネット内のEC2からプライベートサブネット内のec2にssh接続を行います。
キーペアを使用してssh接続します。
ssh -i /tmp/testkeypair.pem ec2-user@20.0.1.249
コマンドを実行するとプライベートサブネット内のEC2インスタンスにログインできます
[ec2-user@ip-20-0-2-89 ~]$ ssh -i /tmp/testkeypair.pem ec2-user@20.0.1.249 __| __|_ ) _| ( / Amazon Linux 2 AMI ___|\___|___| https://aws.amazon.com/amazon-linux-2/
手順20 後片付け
展開したリソースをそのままにしておくと課金が発生してしまうので、リソースを全て削除していきます。CloudFormationを開くとtestCFスタックが作成されているのでラジオボタンを選択し「削除」を押します
「スタックの削除」を押すとCloudFormationで作成されたリソースが全て削除されます。
AWSコンソールから作成したキーペアも削除しておきましょう
EC2のキーペアからtestkeypairを選択し右上の「アクション」から削除を選択し削除しておきます。
おわりに
いかがだったでしょうか?AWSネットワーク周りの理解をしていないと今書いているリソースはそもそも何なのか、何のプロパティなのかがわからなくなるなと感じました。
2層アーキテクチャのリソースを記述しましたがVPC等の理解がある程度あるおかげで完成させることができたと思います。