はじめに
AWS を触り初めて数ヶ月近くになるのですが、近頃、インフラ設定の再利用性と属人性の最小化を目的にインフラのコード化を強く意識するようになりました。 Docker を利用し初めたことも合間って個人のプロジェクトはほとんどコード管理するようにしています。(個人レベルで意味あるのかはさておきますが、、、)
まぁ、実際の業務では、さっぱり活かされていません、、、😅
と言うこともあって今回は AWS の Infarastructure as Code(IaC) サービスの1つである CloudFormation を使って、なるべく簡単に AWS の小さなサーバ環境を構築してみたいと思います。
CloudFormation とは
AWS CloudFormation では、プログラミング言語またはシンプルなテキストファイルを使用して、あらゆるリージョンとアカウントでアプリケーションに必要とされるすべてのリソースを、自動化された安全な方法でモデル化し、プロビジョニングできます。 公式より
上文ではイメージし辛いかなと思いますが、要するに AWS のコンソールでぽちぽちインフラ構築していたものを、YAML や JSON にインフラ構成を記述して CloudFormation にアップすることでインフラを自動構築するようなものです。
インフラがコードなので、バージョン管理できたり、似たようなインフラ構成を簡単にコピーできたり、それ自体が設計図にもなり得ます。
やりたいコト
- CloudFormation で 無料枠の t2.micro の EC2 1 台を構築
- AWS のコンソールをなるべく触れずに、AWS CLI とインフラコードをもってサーバ構築を完了
- 構築した EC2 に SSH 接続
対象環境とユーザー
- macOS Catalina バージョン 10.15.3
- AWS 初心者の方(コンソール上で EC2 を立てたことがあるなどなど)
- AWS CLI をローカル環境にインストール済み *1
*1:過去に AWS CLI を仮想環境にインストールする記事を書いています。必要に応じて参照いただければです。
手順
下準備
AWS CLI をインストール
前述でも触れましたが、以下の手順では AWS CLI を使って作業を行いますので、インストールをお願いします。私は Python の Pipenv で AWS CLI v1 を Mac にインストールしています。また、2020/02 から AWS CLI v2 もローンチされ、そちらをインストールしても大丈夫かと思います。
テンプレートをクローン
僭越ながら今回は GitHub にテンプレートを用意しているのでそちらのクローンをお願いします。長いプロジェクト名なので必要に応じてリネームしてください。
1$ git clone git@github.com:canji53/easy_aws_infrastructure_by_cloudformation.git
templates/
内に 5 つのテンプレートを用意しています。それぞれ、サービスごとにインフラ設定を ネスト
(入れ子)していて、main.yml
がそれらの処理を統括する親のテンプレートになります。今回は余計な情報を盛り込みたくなかったのでなるべく最小構成にしています。
1$ cd easy_aws_infrastructure_by_cloudformation
2
3# treeコマンドでディレクトリ構造をチラ見
4$ tree templates
5templates
6├── ec2.yml
7├── main.yml
8├── sg.yml
9└── vpc.yml
10
110 directories, 4 files
SSH キーを生成
構築後の EC2 に SSH 接続するために AWS CLI であらかじめキーペアを生成します。今回は hogehoge-key
と適当な名前にしていますので、必要に応じてリネームしてください。
1# キーペア生成
2# "--output text > ***"でキーをテキストに保存
3$ aws ec2 create-key-pair --key-name hogehoge-key --query 'KeyMaterial' --output text > ~/.ssh/hogehoge-key.pem
4
5# キーの存在確認
6$ aws ec2 describe-key-pairs --key-name hogehoge-key
7{
8 "KeyPairs": [
9 {
10 "KeyPairId": "key-***",
11 "KeyFingerprint": "**:**:**:...",
12 "KeyName": "hogehoge-key",
13 "Tags": []
14 }
15 ]
16}
17
18# 権限変更(しておかないと接続時に弾かれます)
19$ chmod 0700 ~/.ssh/hogehoge-key.pem
テンプレート保存用の S3 バケットを作成
CloudFormation では、設定を書いた YAML を テンプレート
と呼び、このテンプレートをローカル環境からアップするのか、それとも S3 の URL から参照するのかを選べます。ネスト
**されたテンプレートは URL 参照が必須となるため、**今回は S3 にテンプレートをアップロードするようにします。
また、下記コマンドで hogehoge-bucket
と言う名前の S3 バケットを作成するようにしていますが、バケット名は適当な名前に変更してください。S3 のバケット名は全 AWS 内で唯一の名前を振る必要があります。被ってる場合はエラーが返ってきます。
1# バケット作成
2$ aws s3 mb s3://hogehoge-bucket
3
4# バケット確認
5$ aws s3 ls | grep "hogehoge-bucket"
620**-**-** 00:00:00 hogehoge-bucket
テンプレートのパラメータを調整
クローンしたテンプレートはそのままでは利用できないので、main.yml
の Parameters
を調整します。
TemplateBucket > Default
を先ほど作成したバケット名に変更ClientCidr > Default
をお使いの端末のグローバル IP に変更KeyName > Default
を先ほど作成したキーペア名に変更
1$ sudo vim templates/main.yml
2
3AWSTemplateFormatVersion: 2010-09-09
4
5
6Parameters:
7 TemplateBucket:
8 Type: String
9 Default: "hogehoge-bucket"
10 ClientCidr:
11 Type: String
12 Default: "?.?.?.?/?"
13 KeyName:
14 Type: String
15 Default: "hogehoge-key"
16
17~~ 以下略 ~~
今回はセキュリティグループで ClientCidr
で指定したグローバル IP のみから SSH 接続できるようにしています。0.0.0.0/0
にしても良いですが、そうなった場合 EC2 のグローバル IP(AWS ではパブリック IP)が知られた場合に不正アクセスされてしまうため、絶対にお使いの端末のグローバル IP を指定してください。
1# curlでグローバルIPを取得する場合
2$ curl ifconfig.io
テンプレートの構文チェック
テンプレートを作成・編集した後は AWS CLI を使った構文チェックをすることを絶対にオススメします。CloudFormation ではテンプレート1枚でまとめられたインフラリソースを スタック
と呼びます。そして、構文エラーがあると CloudFormation 側でスタック作成する前にエラーが吐かれます。なので、あらかじめ構文チェックをしておくことで手戻りを減らせます。
構文にエラーがなければ、そのテンプレートでスタックされるリソース一覧が返ってきます。エラーがある場合は、行数指定でどこがおかしいか表示されます。
1$ aws cloudformation validate-template --template-url https://s3.amazonaws.com/hogehoge-bucket/main.yml
2{
3 "Parameters": [
4 {
5 "ParameterKey": "ClientCidr",
6 "DefaultValue": "?.?.?.?/?",
7 "NoEcho": false
8 },
9 {
10 "ParameterKey": "KeyName",
11 "DefaultValue": "hogehoge-key",
12 "NoEcho": false
13 },
14 {
15 "ParameterKey": "TemplateBucket",
16 "DefaultValue": "hogehoge-bucket",
17 "NoEcho": false
18 }
19 ],
20 "Capabilities": [
21 "CAPABILITY_NAMED_IAM",
22 "CAPABILITY_AUTO_EXPAND"
23 ],
24 "CapabilitiesReason": "The following resource(s) require capabilities: [AWS::CloudFormation::Stack]"
25}
テンプレートを S3 バケットに保存
先ほど作成したバケットに編集したテンプレートを S3 にコピーしていきます。
1# テンプレートをS3にコピー
2$ aws s3 cp --recursive templates s3://hogehoge-bucket
3upload: templates/vpc.yml to s3://hogehoge-bucket/vpc.yml
4upload: templates/ec2.yml to s3://hogehoge-bucket/ec2.yml
5upload: templates/main.yml to s3://hogehoge-bucket/main.yml
6upload: templates/sg.yml to s3://hogehoge-bucket/sg.yml
バケット内のファイルの中身を確認したい場合は下記コマンドになります。
1$ aws s3 cp s3://hogehoge-bucket/main.yml -
2
3AWSTemplateFormatVersion: 2010-09-09
4
5
6Parameters:
7 TemplateBucket:
8 Type: String
9 Default: "hogehoge-bucket"
10 ClientCidr:
11 Type: String
12 Default: "?.?.?.?/?"
13 KeyName:
14 Type: String
15 Default: "hogehoge-key"
16
17~~ 以下略 ~~
いざ CloudFormation でスタックを作成(インフラ自動構築)
AWS CLI を使えばスタック作成もコマンドで完結できます。
1$ aws cloudformation create-stack --stack-name hogehoge-stack --template-url https://s3.amazonaws.com/hogehoge-bucket/main.yml
2{
3 "StackId": "arn:aws:cloudformation:ap-northeast-1:******:stack/hogehoge-stack/*******************"
4}
スタック作成の過程を調べる場合は、
1$ aws cloudformation describe-stacks --stack-name hogehoge-stack --query "Stacks[0].StackStatus"
2
3# 完成している場合
4"CREATE_COMPLETE"
構築されたスタックのリソースを確認する場合は、
1$ aws cloudformation list-stack-resources --stack-name hogehoge-stack
2
3{
4 "StackResourceSummaries": [
5 {
6 "LogicalResourceId": "EC2",
7 "PhysicalResourceId": "arn:aws:cloudformation:ap-northeast-1:***:stack/hogehoge-stack-EC2-***/***",
8 "ResourceType": "AWS::CloudFormation::Stack",
9 "LastUpdatedTimestamp": "2020-**-**:**:**.***",
10 "ResourceStatus": "CREATE_COMPLETE",
11 "DriftInformation": {
12 "StackResourceDriftStatus": "NOT_CHECKED"
13 }
14 },
15 ~~ 長いので略 ~~
EC2 に SSH 接続してみる
まず、構築した EC2 の グローバル IP を調べます。下記コマンドはこちらのサイト様を参考にしています。是非チェックして見てください。インスタンスの一覧が出ると思いますが、今回はタグを何も設定したいないため、その中からおそらく Name
が null
のものが今回構築した EC2 になるかと思います。
1$ aws ec2 describe-instances --query 'Reservations[].Instances[].{InstanceId:InstanceId,Name:Tags[?Key==`Name`]|[0].Value,PrivateIp:PrivateIpAddress,PublicIp:PublicIpAddress}'
2
3[
4 {
5 "InstanceId": "i-****",
6 "Name": null,
7 "PrivateIp": "**.**.**.**",
8 "PublicIp": "**.**.**.**"
9 }
10~~ 以下略 ~~
ではでは、先ほどの PublicIp
を控えて EC2 に SSH で入ります。
1$ ssh ec2-user@**.**.**.** -i ~/.ssh/hogehoge-key.pem -p 22
あとは適当にサーバで遊んで見てください 😅
作成したスタックを消したい場合は?
1$ aws cloudformation delete-stack --stack-name hogehoge-stack
2
3# スタックが消えているか確認
4# エラーが出ればスタックが消えています
5$ aws cloudformation describe-stacks --stack-name hogehoge-stack --query "Stacks[0].StackStatus"
6
7An error occurred (ValidationError) when calling the DescribeStacks operation: Stack with id hogehoge-stack does not exist
おわりに
今回はなるべく小さなサーバを CloudFormation で構築してみました。テンプレートの詳細な構文に関しては触れていませんが、と言うより触れだすと止まりません(汗)。そのため、なんとなくスタック構築の流れと薄い構文を把握してもらえればと思います。
また、実は AWS CLI の構文チェックを行ってもスタックを構築している途中でエラーが出る時があります。これが厄介です。スタックが途中まで作成されてロールバックするため、その間に作成されたリソースが消えるのです。そうなると、無駄な課金が発生します、、、
ですので、AWS CLI のみで構文チェックするのではなく、詳細にドキュメントを追いかけながら構成チェックをすることをオススメしますが、これに慣れるまでが大変なんです 😢