EC2の冗長構成をクリック一発で作りたい【AWS CloudFormation】
環境構築はCloudFormationで自動化だ!
AWSで環境をつくっていて、今どういったインスタンスがどの設定で動いているのか気がつけばわからなくなることがザラにあります。
このEC2何者?とか、このサブネット何用?とか
でも、CloudFormationなら大丈夫!
環境とテンプレートがイコールなので「こいつ何者?」がなくなる。
非インフラエンジニアからしてみればプログラム感覚で作れるので飲み込みやすいし(個人的な感覚)、AWSドキュメントを眺めるとコンソール上で見たことない(または見逃している)設定などもあって新たな発見もある。
ということで、冗長構成をCloudFormationで作る。
※時間の関係でテンプレートはシングルスタック。DBは無しの状態です。また時間ができたら作ります。
構成
アプリケーション用のサーバーがprivate subnetへ配置され、AutoScalingするという構成。
(AWSCloudTech内のハンズオン「冗長性のあるブログサービスを構築する」を参考にしています。)
個人的にログが見れないと嫌なのでcfnのログをCloudwatchで出力するようにしている。
CloudFormationとは
作る前にざっくりとCloudFormationとは
・テンプレートファイルを元にAWSの環境を構築してくれるAWSサービスの一つ
テンプレートファイルってどんなん?
・テンプレートファイルはYAMLまたはJSON形式で指定の書式がある
・簡単な関数や環境変数などが用意されていて使うことができる
書式ざっくり説明。
| セクション | 内容 |
|---|---|
| AWSTemplateFormatVersion | バージョン情報(2010-09-09が最新) |
| Description | テンプレートの説明 |
| Metadata | 追加情報を定義できる。(よくわかってない) |
| Parameters | パラメーターを定義できる。例えば、VPCの範囲など |
| Mappings | キーバリューのリストみたいなものを定義できる。 |
| Conditions | 条件を定義できる。例えば、開発環境向けとか本番向けなど |
| Transform | サーバレスアプリケーションのバージョン指定とかできる。 |
| Resources | リソースを定義できる。例えば、EC2をXXというImageIDで作成するとか |
| Outputs | 情報を出力できる。単純なリソースの情報出力と、リソース自体を外部から参照できるように出力することができる。 |
| https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-anatomy.html |
作成準備
準備① 必要なパラメーターを検討(Parameters:)
| パラメーター名(論理名) | 内容 |
|---|---|
| ProjectName | プロジェクト名。タグにプロジェクト名+vpcとかで名前つけるためのもの |
| VpcCIDR | VPCのIP範囲 |
| aSubnetCIDR | パブリックサブネットの範囲1 |
| bSubnetCIDR | パブリックサブネットの範囲2 |
| aPrivateSubnetCIDR | プライベートサブネットの範囲1 |
| bPrivateSubnetCIDR | プライベートサブネットの範囲2 |
| NATGatewayCreate | NATゲートウェイを作るか否か(お金かかるとことなので) |
| StepInstanceCreate | 踏み台サーバーを用意するか否か(お金かかるとことなので) |
| WebAllowLocation | Webサーバーが受け入れるIP範囲 |
| StepAllowLocation | 踏み台サーバーが受け入れるIP範囲 |
| MyAMI | Webサーバーが使用するAMI(未指定の場合はDefaultAMIが使用される) |
| DefaultAMI | AmazonLinux2 |
| WebKeyPair | WebサーバーへのSSHアクセス用KeyPair |
| StepKeyPair | 踏み台サーバーへのSSHアクセス用KeyPair |
| AutoScalingDesiredCapacity | AutoScallingの希望キャパシティ(0にすればインスタンスを作らない) |
自分用にある程度カスタマイズできるように色々と準備した。
これらのパラメーターを使用した構成でCloudFormationは動作する。
設定の例
パラメーター名・タイプ・説明・デフォルト値などを指定できる。
Parameters:
MyAMI:
Type: String
Description: your ami id
Default: 'ami-0000000000000000'
これをCloudFormationで読み込むとパラメータ入力画面で入力できるようになる。
準備② 必要なリソースをまとめる(Resources:)
| リソース名(論理名) | 内容ざっくり |
|---|---|
| VPC | VPC |
| aSubnet | パブルックサブネットA |
| bSubnet | パブルックサブネットB |
| aPrivateSubnet | プライベートサブネットA |
| bPrivateSubnet | プライベートサブネットB |
| aRouteTable | パブリックサブネットAのルートテーブル |
| bRouteTable | プライベートサブネットBのルートテーブル |
| bPrivateRouteTable | プライベートサブネットBのルートテーブル |
| aPrivateRouteTable | プライベートサブネットAのルートテーブル |
| aSubnetRouteTableAssociation | パブリックサブネットAとルートテーブルの紐づけ |
| bSubnetRouteTableAssociation | パブリックサブネットBとルートテーブルの紐づけ |
| aPrivateSubnetRouteTableAssociation | プライベートサブネットAとルートテーブルの紐づけ |
| bPrivateSubnetRouteTableAssociation | プライベートサブネットBとルートテーブルの紐づけ |
| aRoute | パブリックサブネットAのルート定義とルートテーブル紐付け |
| bRoute | パブリックサブネットBのルート定義とルートテーブル紐付け |
| aPrivateRoute | プライベートサブネットAのルート定義とルートテーブル紐付け |
| bPrivateRoute | プライベートサブネットBのルート定義とルートテーブル紐付け |
| aEIP | サブネットAのNATゲートウェイ用IPアドレス |
| bEIP | サブネットBのNATゲートウェイ用IPアドレス |
| aNatGateway | サブネットAのNATゲートウェイ |
| bNatGateway | サブネットBのNATゲートウェイ |
| InternetGateway | インターネットゲートウェイ |
| VPCGatewayAttachment | インターネットゲートウェイ紐付け |
| WebServerSecurityGroup | Webサーバ用のセキュリティグループ |
| LoadBalancer | ロードバランサ |
| TargetGroup | ロードバランサのターゲットグループ |
| Listener | ロードバランサとターゲットグループの紐付け |
| LoadBalancerSecurityGroup | ロードバランサ用のセキュリティグループ |
| AutoScalingGroup | オートスケーリンググループ |
| Ec2LaunchConfiguration | EC2の起動設定 |
| CloudWatchParameter | Cloudwatchでログを出力するための設定値 |
| AutoScalingEC2Profile | EC2にロールを紐付けるためのプロファイル。 |
| AutoScalingEC2Role | ロール(CloudWatchLogsとかを出力するためのロール) |
| StepEC2 | 踏み台サーバー |
| StepEIP | 踏み台サーバー用のIPアドレス |
| StepSecurityGroup | 踏み台サーバー用のセキュリティグループ |
設定の例
Parameters:
ProjectName:
Type: String
Default: "aws-cloud-tech"
VpcCIDR:
Type: String
Default: "192.168.0.0/16"
Resources:
VPC:
Type: "AWS::EC2::VPC"
Properties:
# Fn::Refという関数を使用して、パラメーターで入力された値が取得できる。
CidrBlock: !Ref VpcCIDR
EnableDnsSupport: "true"
EnableDnsHostnames: "true"
InstanceTenancy: default
Tags:
- Key: Name
# Fn::Subという関数を使用して文字列を作っている。
Value: !Sub "${ProjectName}-vpc"
aSubnet:
Type: "AWS::EC2::Subnet"
Properties:
AvailabilityZone: "ap-northeast-1a"
CidrBlock: !Ref aSubnetCIDR
# Fn::Refという関数を使用して、上で定義されているVPCのIDが取得できる。返却される値はTypeによって異なる。
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${ProjectName}-subnet-a"
# yamlだとコメントかけるからいいよね!
準備③ アウトプットする内容を決める(Outputs:)
とりあえずLBのアクセスURLを出しておく。
Outputsではテンプレート内で定義されたリソースの情報を出力できる。
クロススタックの場合はリソースの内容をもっと出力する必要がある。
クロススタックの分割ルールは、ライフサイクルの長短、依存関係の大小で分割を決めるといいらしい。(AWS Black Belt Online Seminarのセミナーの内容)
ちなみに、出力名変えたいなと思っても別テンプレートで使ってるから無理とか言われるので出力する場合は、名前をきちんと考えた方が良いっぽい。
設定の例
Resources:
LoadBalancer:
Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
Properties:
Tags:
- Key: Name
Value: !Sub "${ProjectName}-elb"
Name: !Sub "${ProjectName}-elb"
Subnets:
- !Ref aSubnet
- !Ref bSubnet
SecurityGroups:
- !Ref LoadBalancerSecurityGroup
Outputs:
URL:
Value: !Join
- ''
- - 'http://'
- !GetAtt LoadBalancer.DNSName
- /
出力はこんな感じ
テンプレートを作ってみる
...おらっ!
テンプレートを使ってスタック作成
リージョンは東京(ap-northeast-1)
事前準備
・SSH接続用のKeyPairの準備
・CloudFormationを実行するIAM(※)
(※)VPC,EC2,IAM,SSMパラメーターなどの作成権限を持ったIAMが必要。権限が足りなければCloudFormation実行時にエラーが出るので都度追加していけばいつかは成功するはず!
事前準備(Option:独自のEC2イメージを使いたい場合)
・EC2イメージ(AMI)のImageId
スタックの作成
デザイナーでテンプレートを作成
コードをアップロード(デザイナーの場合)
次へ
パラメーターを入力して
次へ
オプションをデフォルトのまま
次へ
レビューして
IAM作成承認にチェック入れてからスタックの作成
イベントがゴリゴリ進む
タイムは4分25秒
出力を確認
URLへアクセスしてみる。OK!
AutoScallingのインスタンスも良好
ログも2台分きちんと取得できてる
まとめ
CloudFormationいいところ
・設計変更に強い
・環境を複製できる
・AWSリソース間の関連性がわかるようになる
・コンソール上だけだと表面上気が付かなかったリソースの存在に気がつけるようになる
・インフラエンジニアでなくてもとっつきやすい(個人的な感覚ですが、プログラムを書いて何かが出力されてっていうのが吸収されやすいからなのかも)
CloudFormationわるいところ
・AWS以外で使えない
・勉強コストがそれなりに必要
CloudFormationまだわかってないところ
・テンプレートのバージョン管理(ドキュメント見てもなんのことだかよくわからない。。まさかとは思うけどテンプレートを別途バージョン管理使って管理してねということじゃないよね。わからん。)
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/best-practices.html#code
・テンプレートのネスト
関連性のあるテンプレートをネストすることができるらしいのだが、詳しく調べていない。
感想
アウトプットめちゃ大変。
Discussion