はじめに
記事を見ていただいて、ありがとうございます。
Webエンジニアをしているsannoと申します。
皆さん、AWSを使っていますか?
実はこのblogもAWSを使って構築しているのですよね。
それで、AWSを使っていると技術検証なんかで、サクッとEC2を立てて確認したいなってことはありませんか?
その時にポチポチとコンソール画面から立てても良いですが、あの設定が必要だったけれど忘れていてアクセスできなかったみたいなこと、よくあるんですよねー
記憶力が悪い私だけかもしれませんが…
また、ただの技術検証用のEC2といっても、EC2以外にサブネットだったりセキュリティグループだったりが必要なことがほとんどだと思います。
それらを逐一構築するのは、結構手間ですよね。
さらに使い終わった後にも、不要なものは削除しておくことが上手くシステムを運用していくコツだと思うので、それらを忘れずまとめてきれいにするという手間もあると思います。
そんな私と同じような悩みがある方におすすめなのは、EC2の構築をまるっとテンプレート化しておいてCloudFormationを使うことです。
CloudFormationの公式の説明については以下になります。
IaC(Infrastructure as Code)というやつですね。
CloudFormationのテンプレートを登録しておくだけで、必要な時にCloudFormationを実行して必要なEC2環境を立ち上げることができます。
削除もCloudFormationから実行すれば、もれなく不要な設定まとめて削除できます。
CloudFormation、素敵ですねー
それでは、このCloudFormationを使ってリソースの構築と削除の手順を見てゆきましょう!
もし、この記事で解説している内容に誤りがございましたら、コメント等でご指摘いただけると幸いです。
構築するAWS構成
構築時点の環境
$ aws --version
aws-cli/2.4.0 Python/3.8.8 Darwin/20.3.0 exe/x86_64 prompt/off
CloudFormation
準備
CloudFromationでEC2を立ち上げるにあたって、キーペアの作成だけはCloudFromationに組み込めないので、これは事前に準備する必要があります。
ですが、全部CloudFromationだけで完結させたいんだけど! という方がいましたら、以下の記事でAWS Systems Manager Session Managerを使った、キーペアなしでもEC2にアクセスする方法を解説しています。
もし興味を持っていただけましたら、こちらも見ていってください。
それで、ここではAWS CLIを使ってキーペアを作りたいと思います。
AWS CLIの設定が済んでいない方は以下の記事などを参考に、設定をしてみてください。
AWS CLIの設定が済みましたら、キーペアを作成してみましょう。
キーペアを作成するコマンドは以下です。
$ aws ec2 create-key-pair --key-name test-ec2-cfn-key --key-type rsa --query 'KeyMaterial' --output text > ~/.ssh/test-ec2-cfn-key.pem
コマンドについて、軽く説明します。
–key-nameで指定しているものはキーペアの名前で、他のキーペアと区別できるような名前にしたほうが良いと思います。
–key-typeで指定しているものは暗号アルゴリズムで、rsaの他にed25519が指定できます。
どちらが良いか時と場合によると思いますが、以下の記事などを参考に選択してもらうと良いと思います。
この記事では汎用性の高さからrsaを選択します。
–query ‘KeyMaterial’の指定についてはそいうものということで…
公式の説明では以下です。
「
Amazon EC2 を使用してキーペアを作成する--query "KeyMaterial"
」はプライベートキーのマテリアルを出力します。
–output text > ~/.ssh/test-ec2-cfn-key.pemで指定しているものは、秘密鍵の保存先です。
–key-nameで指定した名前と同じにしておくと良いと思います。
キーペアの作成コマンドの説明は以上になりますが、実際に作成できているか確認してみましょう。
以下のコマンドで確認できます。
$ aws ec2 describe-key-pairs --key-name test-ec2-cfn-key
{
"KeyPairs": [
{
"KeyPairId": "key-XXXXX",
"KeyFingerprint": "XX:XX:XX:...",
"KeyName": "test-ec2-cfn-key",
"KeyType": "rsa",
"Tags": []
}
]
}
AWSのコンソールでも確認してみましょう。
“ec2″と検索。
メニューから”キーペア”を選択。
以下のように作成したキーペアが表示されていればOK。
また、作成した秘密鍵は以下のようなパーミッションになっているかもしれません。
$ ls -l /your_ssh_dir/test-ec2-cfn-key.pem
-rw-r--r-- 1 owner group 1675 11 25 22:38 /your_ssh_dir/test-ec2-cfn-key.pem
その場合だとキーペアを使ってSSH接続しようと思っても以下のようなエラーが出て接続できないので、秘密鍵のパーミッションを変更しておきましょう。
$ ssh -i /your_ssh_dir/test-ec2-cfn-key.pem ec2-user@xx.xx.xx.xx
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/your_ssh_dir/test-ec2-cfn-key.pem' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "/your_ssh_dir/test-ec2-cfn-key.pem": bad permissions
ec2-user@xx.xx.xx.xx: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
秘密鍵を以下のパーミッションに変更します。
$ chmod 400 /your_ssh_dir/test-ec2-cfn-key.pem
$ ls -l /your_ssh_dir/test-ec2-cfn-key.pem
-r-------- 1 owner group 1675 11 25 22:38 /your_ssh_dir/test-ec2-cfn-key.pem
これで準備が整いました。
次はCloudFormationで実行するテンプレートについて見てゆきましょう。
CloudFromationのymlテンプレート
全体像
まずはドンとCloudFormationで実行するテンプレートを載せてしまうと、以下のようになります。
AWSTemplateFormatVersion: 2010-09-09
Description: Configure environment to use EC2
Parameters:
Ec2ImageId:
Type: AWS::SSM::Parameter::Value<String>
Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
Ec2InstanceType:
Type: String
Default: t3.nano
Resources:
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: false
InstanceTenancy: default
Tags:
- Key: Name
Value: test-ec2-01
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: test-ec2-igw
VpcGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref Vpc
Subnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Sub "${AWS::Region}a"
CidrBlock: 10.0.10.0/24
VpcId: !Ref Vpc
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: test-ec2-public-subnet-1a
RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: test-ec2-public-route
Route:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref Subnet
RouteTableId: !Ref RouteTable
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for attaching ec2
GroupName: test-ec2-sg-attach-ec2
VpcId: !Ref Vpc
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
FromPort: 22
ToPort: 22
NetworkAcl:
Type: AWS::EC2::NetworkAcl
Properties:
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: test-ec2-nacl
NetworkAclAssoc:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
NetworkAclId: !Ref NetworkAcl
SubnetId: !Ref Subnet
NetworkAclEntryInbound:
Type: AWS::EC2::NetworkAclEntry
Properties:
Egress: false
RuleNumber: 1
RuleAction: allow
Protocol: -1
CidrBlock: 0.0.0.0/0
NetworkAclId: !Ref NetworkAcl
NetworkAclEntryOutbound:
Type: AWS::EC2::NetworkAclEntry
Properties:
Egress: true
RuleNumber: 1
RuleAction: allow
Protocol: -1
CidrBlock: 0.0.0.0/0
NetworkAclId: !Ref NetworkAcl
Ec2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref Ec2ImageId
InstanceType: !Ref Ec2InstanceType
KeyName: test-ec2-cfn-key
Tags:
- Key: Name
Value: test-ec2-instance
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: 0
SubnetId: !Ref Subnet
GroupSet:
- !Ref SecurityGroup
順番にポイントとなる部分を解説してゆきます。
AMI ID
まずはParametersでAMI IDを定義して、それをEc2Instanceに設定している部分です。
Parameters:
Ec2ImageId:
Type: AWS::SSM::Parameter::Value<String>
Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 # リージョンごとに最新のAmazon Linux2のAMI IDを取得している
Ec2InstanceType:
Type: String
Default: t3.nano
Resources:
# ...間を省略...
Ec2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref Ec2ImageId # 取得したAMI IDを設定している
InstanceType: !Ref Ec2InstanceType
KeyName: test-ec2-cfn-key
Tags:
- Key: Name
Value: test-ec2-instance
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: 0
SubnetId: !Ref Subnet
GroupSet:
- !Ref SecurityGroup
EC2を立てるのに決まったAMIが用意されていれば、それを指定で良いと思いますが、検証用のEC2をサクッと立てたいといった場合にAMIまで用意してあることは少ないのではないでしょうか。
そんな場合に上記の記述のように、リージョンごとに最新のAmazon Linux2のAMI IDを動的に取得できたほうが便利かと思います。
記述内容の詳細については、以下の記事などを参照していただくと良いと思います。
VPCやパブリックサブネットのルーティング
次は、VPCやパブリックサブネットのルーティングの部分です。
# ...間を省略...
Resources:
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: false
InstanceTenancy: default
Tags:
- Key: Name
Value: test-ec2-01
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: test-ec2-igw
VpcGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref Vpc
Subnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Sub "${AWS::Region}a"
CidrBlock: 10.0.10.0/24
VpcId: !Ref Vpc
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: test-ec2-public-subnet-1a
RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: test-ec2-public-route
Route:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref Subnet
RouteTableId: !Ref RouteTable
今回はEC2から外のインターネットへの通信も想定しているので、VPCのインターネットゲートウェイを設定しています。
パブリックサブネットにもインターネットゲートウェイへのルーティングを設定しています。
このあたりの関係について、知識があいまいだなという方は以下の記事などで解説してくれている、コンソール画面からの設定と照らし合わせて理解していただくと良いかもしれません。
セキュリティグループとNetwork ACL
次は、セキュリティグループとNetwork ACLの部分です。
# ...間を省略...
Resources:
# ...間を省略...
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for attaching ec2
GroupName: test-ec2-sg-attach-ec2
VpcId: !Ref Vpc
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
FromPort: 22
ToPort: 22
NetworkAcl:
Type: AWS::EC2::NetworkAcl
Properties:
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: test-ec2-nacl
NetworkAclAssoc:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
NetworkAclId: !Ref NetworkAcl
SubnetId: !Ref Subnet
NetworkAclEntryInbound:
Type: AWS::EC2::NetworkAclEntry
Properties:
Egress: false
RuleNumber: 1
RuleAction: allow
Protocol: -1
CidrBlock: 0.0.0.0/0
NetworkAclId: !Ref NetworkAcl
NetworkAclEntryOutbound:
Type: AWS::EC2::NetworkAclEntry
Properties:
Egress: true
RuleNumber: 1
RuleAction: allow
Protocol: -1
CidrBlock: 0.0.0.0/0
NetworkAclId: !Ref NetworkAcl
Ec2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref Ec2ImageId
InstanceType: !Ref Ec2InstanceType
KeyName: test-ec2-cfn-key
Tags:
- Key: Name
Value: test-ec2-instance
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: 0
SubnetId: !Ref Subnet
GroupSet:
- !Ref SecurityGroup
セキュリティグループとNetwork ACLって、何にアタッチするものなんだっけ? ってよくわからなくなるんですよねー
(私だけかもしれませんが)
なので、セキュリティグループとNetwork ACLの関係について、まずはそれぞれ何にアタッチするのかと、Stateの関係について理解しておくと良いのかなと思います。
何にアタッチするのか | State | |
---|---|---|
セキュリティグループ | EC2などのインスタンス | State full |
Network ACL | サブネット | State less |
実際にCloudFormationのテンプレートを書いてみると、これらの関係について理解が深まるので、そういった意味でもCloudFormationを使うのはおすすめです。
これらの関係については以下の記事などが詳しく解説してくれています。
Network ACLやセキュリティグループのPropertiesについても、見てゆきましょう。
セキュリティグループで確認しておきたいは、VpcIdかなと思います。
# ...間を省略...
Resources:
# ...間を省略...
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for attaching ec2
GroupName: test-ec2-sg-attach-ec2
VpcId: !Ref Vpc
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
FromPort: 22
ToPort: 22
セキュリティグループのVpcIdについて、最初、設定していなかったのですね。
そうすると、CloudFormationの実行で以下のようなエラーが発生しました。
Security group sg-xxxxx and subnet subnet-xxxxx belong to different networks.
セキュリティグループにVpcIdを設定していないとデフォルトのVPCに属するような設定になります。
ですので、CloudFormationのテンプレート上で指定したVPCではなく、デフォルトのVPCにセキュリティグループが属してしまったことでこのエラーが発生しました。
セキュリティグループのVpcIdの設定について、以下の記事が示唆してくれています。
Network ACLで確認しておきたいは、Protocolかなと思います。
# ...間を省略...
Resources:
# ...間を省略...
NetworkAcl:
Type: AWS::EC2::NetworkAcl
Properties:
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: test-ec2-nacl
NetworkAclAssoc:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
NetworkAclId: !Ref NetworkAcl
SubnetId: !Ref Subnet
NetworkAclEntryInbound:
Type: AWS::EC2::NetworkAclEntry
Properties:
Egress: false
RuleNumber: 1
RuleAction: allow
Protocol: -1
CidrBlock: 0.0.0.0/0
NetworkAclId: !Ref NetworkAcl
NetworkAclEntryOutbound:
Type: AWS::EC2::NetworkAclEntry
Properties:
Egress: true
RuleNumber: 1
RuleAction: allow
Protocol: -1
CidrBlock: 0.0.0.0/0
NetworkAclId: !Ref NetworkAcl
Protocolには以下で定義されているProtocol Numberを指定する必要があります。
この数値ってどこから参照して設定すれば良いのかわからなかったので、確認ポイントとして書いてみました。
-1は任意のProtocolを指します。
また、セキュリティグループとNetwork ACLいずれでもinbound/outboundの制御はできるかもしれませんが、詳細な制御設定はセキュリティグループで行うほうが良いかもしれません。
その点の考え方については以下の記事で解説してくれていますので、気になる方は参照していただくと良いと思います。
EC2 Instance
最後に、EC2 Instanceの部分です。
# ...間を省略...
Resources:
# ...間を省略...
Ec2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref Ec2ImageId
InstanceType: !Ref Ec2InstanceType
KeyName: test-ec2-cfn-key
Tags:
- Key: Name
Value: test-ec2-instance
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: 0
SubnetId: !Ref Subnet
GroupSet:
- !Ref SecurityGroup
NetworkInterfacesについて確認しておきたいと思います。
NetworkInterfacesで設定しているのはElastic Network Interfaceの設定です。
今回はSSHでグローバルIPアドレスを使って接続するので、AssociatePublicIpAddress: trueを設定する必要があります。
また、NetworkInterfacesを明示的に設定しない場合は、サブネットやセキュリティグループの関連付けを以下のように設定できるのですが。
NetworkInterfacesを設定している場合には、NetworkInterfacesに対してサブネットやセキュリティグループの関連付けをする必要があります。
# ...間を省略...
Resources:
# ...間を省略...
Ec2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref Ec2ImageId
InstanceType: !Ref Ec2InstanceType
KeyName: test-ec2-cfn-key
Tags:
- Key: Name
Value: test-ec2-instance
SecurityGroupIds:
- !Ref SecurityGroup
SubnetId: !Ref Subnet
CloudFormationで実行するテンプレートの解説は以上です。
CloudFormationのリソース構築
CloudFormationをコンソール画面からリソース構築する手順を見てみましょう。
“cloudformation”と検索。
“スタック”を選択。
“スタックの作成”から”新しいリソースを使用(標準)”を選択。
ここではテンプレートを指定するのですが、実行環境に合わせて選択してください。
ローカルのPC内にテンプレートファイルが準備されているようでしたら、”テンプレートファイルのアップロード”を選択で良いと思います。
まだテンプレートファイルを準備していないようでしたら、”デザイナーでテンプレートを作成”からテンプレートファイルを作成しても良いと思います。
ただ、いずれにしても自分で作成したテンプレートファイルはS3に保存されるため、そのままテンプレートファイルを保存しておく場合には小額ながら料金がかかってしまうので、ご注意ください。
“スタックの名前”には任意のものを設定してください。
“パラメータ”については、特に変更がなければテンプレート上で設定してあるデフォルトのままで大丈夫です。
“スタックオプション設定”については、要件に合わせて設定してください。
特別な要件がなければデフォルトの設定のままで大丈夫です。
設定が必要な場合は、以下、公式の説明などを参照していただいて設定してみてください。
“レビュー”で、設定内容に問題がないようでしたら”スタックの作成”から実行。
ステータスが”CREATE_IN_PROGRESS”になっていることを確認。
ステータスが最終的に”CREATE_COMPLETE”になっていれば実行完了。
実際にEC2が構築されてSSH接続できるか確認してみましょう。
“ec2″と検索。
“インスタンス”を選択。
作成したEC2インスタンスで”パブリックIPv4アドレス”をコピー。
ローカルのPCからSSH接続。
$ ssh -i /your_ssh_dir/test-ec2-cfn-key.pem ec2-user@xx.xx.xx.xx
SSH接続できましたら、EC2からoutbound通信も正常にできることを確認してみましょう。
試しにEC2にgitをインストールしてみます。
gitが初めはインストールされていないことを確認。
[ec2-user@ip-xx-xx-xx-xx ~]$ git --version
-bash: git: コマンドが見つかりません
gitのインストール。
[ec2-user@ip-xx-xx-xx-xx ~]$ sudo yum install git
gitが正常にインストールされたことを確認。
[ec2-user@ip-xx-xx-xx-xx ~]$ git --version
git version 2.32.0
以上で、無事にEC2が構築できたことを確認できました。
CloudFormationのリソース削除
不要になったリソースは余計な混乱を避けるためにも、削除しておくことが重要です。
ですので、CloudFormationのリソース削除する手順も見ておきましょう。
“cloudformation”と検索。
削除したいスタックを選択して”削除”を選択。
ステータスが”DELETE_IN_PROGRESS”になっていることを確認。
スタックの検索で”削除済み”を選択。
削除を実行したスタックが”DELETE_COMPLETE”になっていることを確認。
以上がリソース削除の手順です。
おわりに
CloudFormationの便利さが少しでも伝わりましたでしょうか?
CloudFormationを使うとインフラをコードで管理できることもさることながら、AWSの構成や設定値について改めて理解するきっかけにもなります。
そういった意味でもCloudFormationを使うのはおすすめです。
皆さんもぜひ、CloudFormationを使ってみてください。
ここまで記事を読んでいただいて、ありがとうございました!
コメント