AWSでの新規環境構築時など便利なCloudformation
一度テンプレートを作ってしまえば同じような環境はサクッと作れてとても便利ですね
最初にcloudformationを使ったときは分割できることは知っていたものの納期などの兼ね合いがあってとりあえず1ファイルのテンプレートで頑張る、みたいなことをやっていました
1ファイルで頑張ることもできそうではあるのですが意図しないところでトラブルが起きたり、うっかり手動で変更してしまったところが戻されてしまったりとかまぁ地獄を見そうなことは想像に難しくないかなと思います
なので以前使っていたときは初期の構築時の一発のみといった感じで使っていました
ファイルを分割してスタックを細かくわけることで
- スタックの変更による影響範囲を狭めることができる
- ファイルが分離されていることでなんのテンプレートについての設定なのかわかるようになる
といったメリットがありそうです
運用していくなら必須そうですね
今回はサンプルのテンプレートを分割していきたいと思います
一般的なWebサービス用のテンプレート
今回の構成は下記
- VPC
- SecurityGroup
- Subnet
- RDS
- Redis
PVCとRDSなどのミドルウェアを分離させてみます
実際に運用するならEC2、AutoScalingGroup、etc... も必要ですが参照をしてみることを目的としてやってみます
分割するには
参考
AWS CloudFormation の更新 – YAML、クロススタック参照、簡略化された置換 | Amazon Web Services ブログ aws.amazon.com
分割するには下記作業が必要そうです
- 参照される側のテンプレートで
Outputsを定義して他スタックから参照可能にする - 参照する側のテンプレートで
!ImportValueorFn::ImportValue関数をつかって参照する
Export
Outputs:で他スタックから参照可能にする
例
Outputs:
SubBackend16C:
Description: Subnet For Middleware, range:10.0.16.0/24, az: c
Value: !Ref backend16C
Export:
Name: !Sub "${ResourceName}-SubBackend16C"
余談ですがDescriptionは日本語で入力すると文字化けするようで使えませんでした.....
Import
Fn::ImportValue:もしくは短縮形の!ImportValueで他スタックがOutputした情報を読み込む
関数をネストする場合は短縮形は使えないようです
例
Fn::ImportValue !Sub "${ResourceName}-SubBackend16C"
1枚のテンプレートからの分割
ということで分割です
Parametersでプロジェクト名(demo)を渡してそれを接頭辞としてスタックを作成していきます
- demo-vpc
- demo-db
- demo-redis
参照先の指定も接頭辞(demo)をつけるようにします
分割前のファイルは下記です
VPC
VPCの部分を切り出します
cloudformation-cross-stack-ref/vpc-template.yml at master · swfz/cloudformation-cross-stack-ref
- 一部抜粋
Parameters:
ProjectNameParameter:
Type: String
Default: 'Sample'
Description: Project name for tag
.....
.....
.....
Resources:
myVPC:
Type: AWS::EC2::VPC
Properties:
.....
.....
.....
middleware19A:
Type: "AWS::EC2::Subnet"
Properties:
MapPublicIpOnLaunch: true
.....
.....
.....
middleware20C:
Type: "AWS::EC2::Subnet"
Properties:
MapPublicIpOnLaunch: true
.....
.....
.....
defaultSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Default SecurityGroup
.....
.....
.....
Outputs:
VPCId:
Description: 'VPC ID'
Value: !Ref myVPC
Export:
Name: !Sub "${ProjectNameParameter}-VPC"
MiddleWare19A:
Description: 'Subnet For Middleware,range: 10.0.19.0/24, az: a'
Value: !Ref middleware19A
Export:
Name: !Sub "${ProjectNameParameter}-MiddleWare19A"
MiddleWare20C:
Description: 'Subnet For Middleware, range: 10.0.20.0/24, az: c'
Value: !Ref middleware20C
Export:
Name: !Sub "${ProjectNameParameter}-MiddleWare20C"
DefaultSecurityGroup:
Description: 'Default Security Group'
Value: !Ref defaultSecurityGroup
Export:
Name: !Sub "${ProjectNameParameter}-DefaultSecurityGroup"
これを実行すると出力の項目でOutputsで定義した値が出てきます

RDS
RDSの部分を切り出します
- 一部抜粋
Parameters:
ProjectNameParameter:
Type: String
Default: 'Sample'
Description: Project name for tag
.....
.....
.....
Resources:
RDSSubnetGroup:
Type: "AWS::RDS::DBSubnetGroup"
Properties:
DBSubnetGroupDescription: !Sub ${ProjectNameParameter} RDS subnet group
SubnetIds:
- {"Fn::ImportValue": !Sub "${ProjectNameParameter}-MiddleWare19A"}
- {"Fn::ImportValue": !Sub "${ProjectNameParameter}-MiddleWare20C"}
.....
.....
.....
RDSSecurityByEC2SecurityGroup:
Type: "AWS::RDS::DBSecurityGroup"
Properties:
GroupDescription: "Ingress for Amazon EC2 security group"
EC2VpcId: {"Fn::ImportValue": !Sub "${ProjectNameParameter}-VPC"}
.....
.....
.....
RDSInstance:
Type: "AWS::RDS::DBInstance"
Properties:
VPCSecurityGroups:
- {"Fn::ImportValue": !Sub "${ProjectNameParameter}-DefaultSecurityGroup"}
.....
.....
.....
Redis
Redisの部分を切り出します
cloudformation-cross-stack-ref/redis-template.yml at master · swfz/cloudformation-cross-stack-ref
実行
VPC,db,redisの順に実行して作成します
./bin/execute.sh vpc create ./bin/execute.sh db create ./bin/execute.sh redis create
シェルスクリプトにまとめてしまってますが実際に叩いているコマンドは
aws cloudformation create-stack \
--stack-name demo-vpc \
--template-body file://$(pwd)/vpc-template.yml \
--parameters \
ParameterKey=AZ1Parameter,ParameterValue=ap-northeast-1a \
ParameterKey=AZ2Parameter,ParameterValue=ap-northeast-1c \
ParameterKey=ProjectNameParameter,ParameterValue=Demo
- db
aws cloudformation create-stack \
--stack-name demo-db \
--template-body file://$(pwd)/db-template.yml \
--parameters \
ParameterKey=ProjectNameParameter,ParameterValue=Demo \
ParameterKey=RDSInstanceTypeParameter,ParameterValue=db.t2.small \
ParameterKey=RDSUserParameter,ParameterValue=root \
ParameterKey=RDSPasswordParameter,ParameterValue=aaaabbbbcccc
- redis
aws cloudformation create-stack \
--stack-name demo-redis \
--template-body file://$(pwd)/redis-template.yml \
--parameters \
ParameterKey=AZ1Parameter,ParameterValue=ap-northeast-1a \
ParameterKey=ProjectNameParameter,ParameterValue=Demo \
ParameterKey=RedisInstanceTypeParameter,ParameterValue=cache.t2.micro
こんな感じです
無事、VPC内にRDS、Redisが作成されたことを確認しました
まとめ
RDSとVPCの部分だけですがクロススタック参照を使うことでテンプレートファイルを分割することができました
やってみるまでは面倒だなーと思っていたのですがやってみたらとても簡単ですね
インフラの構成をすべてcloudformationで賄うってなかなか大変そうだなとは思うもののやはりコードで管理できるのは便利だと思うので頑張っていきたいなと思います