Serverのリソース管理(WebサーバーとかDBサーバーとかのマシン管理)ってみなさん何で管理していますか?
- Ansible
- AWS CLI(Shell)
- etc....
色々あると思いますが、AWSを使用しているならばCloudFormationも一つの選択です。
ただCloudFormationをいざ始めてみようとすると
- ドキュメントの量が膨大
- 謎のyml記法がある(DSL的な)
とかで億劫になって敬遠されがちかと思うのでこれさえ覚えおけば使えるよ!!ってのをまとめてみました。
対象者
- AWSを使っている
- CloudFormationってナニソレ美味しいの?って人
- サーバーのリソース管理方法を選んでいる人
事前に必要なもの
- aws cli
- macな方は黙って
brew install awscli
と打とう
- macな方は黙って
やらないこと
- ポチポチとGUIでCloudFormationを作らない
CloudFormationでまず知っておくことその1 スタック
簡単にいうとサービス単位とかでAWSリソースを管理する単位のことです。
例でいうとWEBサービスを作りたいと思った時に大抵の場合下記のような構成になると思います。
(最近はDockerっていう選択もありますが)
- WEBサーバー(EC2)
- DBサーバー(RDS)
- ロードバランサー(ELB)
もう少し細かいところまでだと
- セキュリティグループ
- Route53
- etc....
これらを管理する塊のことです。(各リソースユニットを管理する塊)
CloudFormationでまず知っておくことその2 エクスポート
先程のスタックでサービス単位で管理をするとどのサービスでも共通して使用するものがあると思います。
例として
- 共通のセキュリティグループ
- VPC
CloudFormationは原則としてスタック間での共有はできません。
(例: スタックAで定義したセキュリティグループはスタックBで重複管理することはできません)
これを実現するためにはエクスポート機能を使用して値をエクスポートして共有します。
プログラム的に言うとグローバル変数を宣言するイメージに近いです。
WEBサーバー x DBサーバー を作るチュートリアル
よくある構成パターンですね。
かつ今回はテスト環境と本番環境で別々のリソース管理をしてみましょう。
ディレクトリを掘る
{path/to/dir}
はお好みのディレクトリを指定してください。
cd {path/to/dir}
mkdir -p cf-tutorial/parameters
touch cf-tutorial/template.yml
touch cf-tutorial/parameters/{production,testing}.json
echo '[]' >> cf-tutorial/parameters/{production,testing}.json
リソースを定義するためのTemplateを書く
管理するAWSリソースを定義するテンプレートです。(yml)
まずはこんな形のymlを用意しましょう
AWSTemplateFormatVersion: '2010-09-09'
Description: cf tutorial stack
Parameters:
# Parameters以下に変数を定義する
Resources:
# Resources以下に管理したいResourceを管理する
Outputs:
# エクスポートしたい値を書く
Parameters (変数)
そのまんまです。
ではサービス名でも定義してみましょう
Parameters:
Prefix:
Type: String
Default: testing
Description: Prefix Name
ServiceName:
Type: String
Default: cf-tutorial
型指定とDefault値を指定できます。
詳しいオプションは
公式ドキュメント Parametersを参照してください。
Resouces
EC2やRDS、セキュリティグループ等AWSで使うサービスの定義をすべてしていきます。
ではまずはWEBサーバーを作成するにあたってのAWSリソースで最低限必要なものは下記です。
- SecurityGroup
- EC2
これを定義していきましょう
Resources:
Ec2SecurityGroupApp:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupName: !Sub "${Prefix}_${ServiceName}_app"
GroupDescription: "web server security group"
VpcId:
"{your VPCID}"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: "0.0.0.0/0"
Tags:
- Key: Name
Value: !Sub "${Prefix}_${ServiceName}_app"
Ec2InstanceServer:
Type: "AWS::EC2::Instance"
Properties:
AvailabilityZone: "ap-northeast-1c"
# AmazonLinuxを指定しています
ImageId: "ami-06cd52961ce9f0d85"
InstanceType: "t2.micro"
KeyName: "your キーペア名"
SecurityGroupIds:
- !Ref Ec2SecurityGroupApp
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: 8
VolumeType: gp2
Tags:
- Key: Name
Value: !Sub "${Prefix}_${ServiceName}_app"
上から順番に説明していきます
Ec2SecurityGroupApp, Ec2InstanceServer (ResourceKeyName)
リソースの名前です。任意の名前をつけてください。
Type
なんのリソースを配置するかです。
名前がわからない場合はgoogleさんに「cloudformation EC2」とかでぐぐればgoogleさんが教えてくれます。
Properties:xxxxxxxx
指定したリソースのプロパティーです。プロパティがわからない場合は
cloudformation EC2」とかでぐぐればgoogleさんが教えてくれます。
!Sub,!Ref (CloudFormationの組込関数)
CloudFormationでは組込み関数が用意されています。
書き方に少し癖があるのですが下記を覚えてしまえば難しくないです。
!{関数名}はショート記法
例えばショート記法を使わない場合でRefを使用したい場合だと
SecurityGroupIds:
- Fn::Ref: Ec2SecurityGroupApp
と書きます。
:を段落区切りを表すyamlの場合この方式で書くと少々可読性が悪くなります。
なのでショート記法で書いている人のほうが多いです。
Ref
!Ref {パラメーター名}
でパラメーターの値を参照できます。
!Ref {リソース名}
で配置するリソースの返り値を取得できます。
リソース名の返り値は 「cloudformation EC2 リソース返り値」とかでぐぐれば.....
大概の場合リソースを参照したい場合は乱暴に
!Ref {リソース名}
と宣言すればよしなに参照できます。
Sub
Parameterと文字列のテンプレートリテラルです。
文字列 + Parameterで値を作成したいときにつかってください。
実際に作ってみる
ではAWS cliを使ってCloudFormationを実際に動かしてみましょう。
ここまでのtemplateファイルは下記です。
aws cloudformation create-stack \
--stack-name {任意のスタック名} \
--template-body file://`pwd`/template.yml \
--parameters file://`pwd`/parameters/testing.json
もし一回目にエラーになった場合は2回目以降は下記です。
aws cloudformation update-stack \
--stack-name {任意のスタック名} \
--template-body file://`pwd`/template.yml \
--parameters file://`pwd`/parameters/testing.json
EC2のGUIを確認してください。t2.microで {Prefix}-{ServiceName}_app
というサーバーが立ち上がっているのが確認できます。
セキュリティグループもできていますね。
RDSを作成してみる
ここまでできたら要領は把握したのでRDSも作ってみましょう
# RDSのセキュリティグループ
RdsSecurityGroupApp:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupName: !Sub "${Prefix}_${ServiceName}-rds"
GroupDescription: "dbserver security group"
VpcId:
"{your VPCID}"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
SourceSecurityGroupName: !Sub "${Prefix}_${ServiceName}_app"
Tags:
- Key: Name
Value: !Sub "${Prefix}_${ServiceName}-rds"
RdsInstace:
Type: "AWS::RDS::DBInstance"
DeletionPolicy: Delete
Properties:
DBInstanceIdentifier: !Sub "${Prefix}-${ServiceName}-db"
AvailabilityZone: "ap-northeast-1c"
DBInstanceClass: "t2.small"
AllocatedStorage: 6
StorageType: gp2
VPCSecurityGroups:
- !Ref RdsSecurityGroupApp
Engine: MySQL
EngineVersion: 5.7.23
DBName: cf-tutorial
MasterUsername: cf-tutorial
MasterUserPassword: {your password}
Tags:
- Key: Name
Value: !Sub "${Prefix}_${TagServiceGroupName}-db-app-${CFVersion}"
ここまでのtemplate.ymlは下記です
template.yml
ではコマンドを実行してみましょう。
aws cloudformation update-stack \
--stack-name {任意のスタック名} \
--template-body file://`pwd`/template.yml \
--parameters file://`pwd`/parameters/testing.json
RDSとEC2が作成されてそれぞれのセキュリティグループが作成されています。
EC2のセキュリティグループに22番ポートが開いていなくてsshログインできないぽよ。。。。
困りましたね。。。
出来上がったEC2の内容を確認できません。
ですがセキュリティグループは慎重にやらないと事故のもとです。
そういった場合に便利なcreate-change-setというものがあります。
create change setとは?
create stackやupate stackのようにtemplateやパラメーターをスタックに送信しますが
リソースの変更を行わずにリソース変更部分の差分を出してくれます。
dry-runをイメージしてもらえるとわかりやすいと思います。
では実際にやってみましょう。
EC2インスタンスにSSHポートを追加する
template.ymlに追記しましょう
Resources:
Ec2SecurityGroupApp:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupName: !Sub "${Prefix}_${ServiceName}_app"
GroupDescription: "web server security group"
VpcId:
"{your VPCID}"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: "0.0.0.0/0"
# 追加内容
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: "0.0.0.0/0"
Tags:
- Key: Name
Value: !Sub "${Prefix}_${ServiceName}_app"
ここまでのtemplateです。
awscliの create-change-set
を実行します。
aws cloudformation create-change-set \
--stack-name {任意のスタック名} \
--change-set-name add-securitey-group-ssh \
--template-body file://`pwd`/template.yml \
--parameters file://`pwd`/parameters/testing.json
AWS CloudformationのGUIのスタックの項目を開いてください。
変更セットの部分に先程実行した
change-set-name
の項目が出てきます。
このリンクを開くとリソースの変更内容がでてきます。
変更した内容がEC2のセキュリティグループだけなのがわかりますね。
では変更セットの詳細画面右上の実行ボタンを押して反映させてみましょう。
スタックの状況が
UPDATE_COMPLETE
になったら正しく反映できています。
RDSに接続できるか確認してみましょう
ssh -i ~/.ssh/{your キーペア名} -p22 ec2-user@{Your IP Adress}
sudo yum install -y mysql-devel
mysql -u cf_tutorial -p -h {your rds host}
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 5.7.23-log Source distribution
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| cf_tutorial |
| innodb |
| mysql |
| performance_schema |
| sys |
+--------------------+
6 rows in set (0.00 sec)
DBに入れました。
スタックの掃除をしてみましょう
これでCloudFormationの流れがつかめてきたと思います。
では最後にCloudFormationの削除を実行してみましょう。
CloudFormationで定義されたリソースの削除は非常に簡単にできます。
aws cloudformation delete-stack --stack-name {任意のスタック名}
これだけです。
定義したリソースすべてをキレイサッパリ削除してくれます。
リソースの消し忘れがなくて良いですね!!
Parameterのオーバーライドによるtest環境,production環境を別々に作る
今まで作成したものはtest環境用のリソースです。
そこでproduction用に作るやり方を紹介します。
今回は一つのテンプレートで変数のみを変更して複数のenv環境を作るやり方です。
production.jsonを変更する
Prefixの値を上書きしましょう。
[
{
"ParameterKey": "Prefix",
"ParameterValue": "production"
}
]
こうすることによって template.yml
で定義されているParameter Prefixをオーバーライドすることができます。
コマンドはいままで --parametersで指定していた testing.json
を production.json
に変更します。
コマンドは下記です。
(stack-nameはtesting環境と違う名前で登録してください。CloudFormationがtesting環境のリソースを上書きます)
aws cloudformation create-stack \
--stack-name {任意のスタック名} \
--template-body file://`pwd`/template.yml \
--parameters file://`pwd`/parameters/prodcution.json
一つのテンプレートで複数環境ができました。
最後に
ちょっとドキュメントを読んだだけではわかりにくいCloudFormationですが
手を動かしてしまえば意外とシンプルです。
それでは楽しい?CloudFormationライフを!!