CloudFormationのマッピング機能を使って理想のInfrastucture as Codeに近づける
こんにちは。DevOps推進1課に所属しています Sasagawaです。
AWS CloudFormationのマッピング機能を利用して理想のInfrastucture as Code(IaC)に近づける方法についてご紹介します。
Infrastructure as Codeはインフラのコード化、インフラをコードで管理することですが、それにより以下のメリットがあります。
本番環境/ステージング環境/開発環境などバージョンレベルまで同じ環境を構築できる。
大量のサーバ群を構築してもヒューマンエラーが発生しない。
ちなみにAWS公式サイトではCloudFormationを以下のように説明しています。
Excelで作った手順書で構築してもいいですが、こんなことはよくありますよね??
「Excelファイルの更新日付を確認したら3年前に更新が止まっていた…」
「自分が正しいと思う手順に勝手に変えるメンバーがいる…しかも障害が発生して調査後に発覚する。」
AWS CloudFormationを利用することでインフラの構築がコード化できるので誰がやっても同じ結果になります。
しかも何度繰り返しても同じ結果になります。(冪等性(べきとうせい))
しかしCloudFormationでコード化するとしても、運用していく中で本番環境、ステージング環境、開発環境でコードの違いによる環境の差異が出てきたら本末転倒です。
初期メンバーは分かっていてもあとから参加したメンバーが見た時に「何でこんなことしているんだろう・・・」って悩んだらせっかくコード化したメリットが消えてしまいます。
実はCloudFormationにはその環境の差異を吸収してくれるマッピング(Mappings)機能があります。
CloudFormationのテンプレートファイル1つに全環境を並べてコード化できるのでプログラムをかじったことがあればすぐに理解できますし、テンプレートファイルの運用管理も楽になります。
環境ごとに1つずつファイルを作って運用管理する必要がなくなります。
前置きが長くなりましたがマッピング機能を使って理想のInfrastucture as Codeに近づける方法をご紹介します。
CloudFormationのテンプレートは構造化されておりセクションで分けられています。マッピングはMappingsセクションで「キー」と「値」をマッピングします。
実際にやってみましょう。
マッピングする場合は3つのパラメータが必要になります。
分かりやすくシンプルに記載してみました。以下のようなイメージになります。
VpcSubnetMapping:
Prd:
Subnet: 10.0.1.0/24
Stg:
Subnet: 10.0.2.0/24
Dev:
Subnet: 10.0.3.0/24
いかがでしょうか?
プログラムが苦手な方でも初見で理解できるかと思います。
Prd環境のサブネット:10.0.1.0/24
Stg環境のサブネット:10.0.2.0/24
Dev環境のサブネット:10.0.3.0/24
分かりやすいですよね。
きれいに並んでいるので一目で理解できます。
VpcSubnetMappingはマッピングの名前になります。
ここで、どのマッピング(VpcSubnetMapping)からキー(Prd等)と値(Subnet: 10.0.1.0/24等)を拾ってくるか指定します。
例えばPrdはキーでキーによって名前と値が変わります。
Subnet: 10.0.1.0/24は名前と値です。
ここが一番混乱するポイントですが名前と値が1セットになっていることに注意してください。
■構文
名前: 値
■例
Subnet: 10.0.1.0/24
マッピング機能により解決される課題
ここでマッピング機能により解決される課題を上げてみます。
本番環境とステージング環境で別々のテンプレートを作らなくてもよい
→本番環境とステージング環境が同じ環境になります。環境ごとに別々の設定ができる
→上記と矛盾してしまいますが、Dev環境だけインスタンスタイプを変えるなどコスト削減などその他の課題も解決できます。要するに環境ごとに変更してはいけないものは変更できないようにし、目的があって変更したいものは変更できる
→設計通りに環境を構築できるます。しかもテンプレートの再利用性を意識した設計ができます。該当箇所までスクロールする必要がないので修正するのが簡単になる
→何気に重要なポイントです。テンプレートが長くなると該当箇所までスクロールして修正するのは面倒です。上部のセクションですべてを設定できると修正が楽になり品質が上がります。
実際にマッピング機能を使ってテンプレートを作ってみる
実際にやってみましょう。
テンプレートのフォーマットはYAMLとJSONフォーマットを選択できます。
可視性とコメントを書くことができるのでYAML形式をお勧めします。
【参考例】 シンプルな基本編
環境ごとにEC2インスタンスタイプを指定しています。
コストを意識して本番環境はスペックを高く、開発環境はスペックが低いインスタンスを選択してコスト削減をしています。
Prd環境:t2.large
Stg環境:t2.small
Dev環境:t2.micro
Parameters:
Environment:
Description: Type of Environment.
Type: String
AllowedValues:
- Prd
- Stg
- Dev
Default: Dev
Mappings:
InstanceTypeEnvironmentMapping:
Prd:
InstanceType: t2.large
Stg:
InstanceType : t2.small
Dev:
InstanceType : t2.micro
Resources:
SampleEC2Instance:
Type: AWS::EC2::LaunchTemplate
Properties:
InstanceType : !FindInMap [InstanceTypeEnvironmentMapping, !Ref Environment, InstanceType]
以下、吹き出しでコメントを入れています。
【参考例】 応用編
こちらも環境ごとに設定しています。
キーで構成を並べることにより一目で環境が把握できます。
Parameters:
Environment:
Description: Type of Environment.
Type: String
AllowedValues:
- Prd
- Stg
- Dev
Mappings:
EnvironmentMapping:
Prd:
VpcCidr: 10.0.1.0/24
InstanceName: prd-linux
InstanceType: t2.large
Stg:
VpcCidr: 10.0.2.0/24
InstanceName: stg-linux
InstanceType : t2.small
Dev:
VpcCidr: 10.0.3.0/24
InstanceName: dev-linux
InstanceType : t2.micro
Resources:
SampleEC2Instance:
Type: AWS::EC2::LaunchTemplate
Properties:
InstanceType : !FindInMap [EnvironmentMapping, !Ref Environment, InstanceType]
以下、吹き出しでコメントを入れています。
スタックの中でMappingsセクションを配置する場所はどこでもいいですが、ParametersとResourcesセクションの間に配置すると分かりやすいです。
マッピングの利用方法
今回のCloudFormationのスタックでは環境ごとに分けていますが、マッピングした値は!FindInMap関数で取得します。
!FindInMap関数で値を取得する際は3つのパラメータが必要になります。
構文
!FindInMap [MapName, TopLevelKey, SecondLevelKey]
例えば以下の関数を例に説明します。
InstanceTypeに設定するインスタンスタイプを取得します。
InstanceType : !FindInMap [EnvironmentMapping, !Ref Environment, InstanceType]
3つのパラメータ
MapName:EnvironmentMapping
TopLevelKey: Environment(PrdとかStgなど)
SecondLevelKey:InstanceType(t2.largeなど)
例えば、TopLevelKeyがPrdの場合は、インスタンスタイプはt2.largeになります。
「じゃあ、どうやってPrdとかStgとかが決まるの?」
CloudFormationでスタックを作成する際にパラメータ(PrdとかStgなど)を渡します。
AWSマネジメントコンソールでもパラメータを渡せますが、今回は理想のIaCに近づけるがテーマなのでコマンドラインから実施してみます。
コマンドで環境のパラメータを渡してCloudFormationのスタックを作成する
※各環境にAWS CLIがインストールされている前提です。
コマンドの構文
※LinuxとPowerShellの違いはコマンドの改行が「\」か「`」の違いだけです。
■Linuxの場合
aws cloudformation create-stack \
--stack-name [スタック名] \
--template-url https://[S3バケット名].s3.ap-northeast-1.amazonaws.com/[テンプレートファイル名] \
--parameters ParameterKey=Environment,ParameterValue=[環境]
■PowerShellの場合
aws cloudformation create-stack `
--stack-name [スタック名] `
--template-url https://[S3バケット名].s3.ap-northeast-1.amazonaws.com/[テンプレートファイル名] `
--parameters ParameterKey=Environment,ParameterValue=[環境]
例:Prd環境をパラメータで渡したい場合
スタック名:test-stack
バケット名:test-bucket
テンプレートファイル名:test-env.yml
■Linuxの場合
aws cloudformation create-stack \
--stack-name test-stack \
--template-url https://test-bucket.s3.ap-northeast-1.amazonaws.com/test-env.yml \
--parameters ParameterKey=Environment,ParameterValue=Prd
■PowerShellの場合
aws cloudformation create-stack `
--stack-name test-stack `
--template-url https://test-bucket.s3.ap-northeast-1.amazonaws.com/test-env.yml `
--parameters ParameterKey=Environment,ParameterValue=Prd
実際に実行する
個人環境で実際に実行してみます。
従量課金なので構築後にすぐにスタックを削除すればほぼ料金は発生しません。
■実際に使用したテンプレート
テンプレートファイル名:test-env.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Sample Template in CloudFormation
Parameters:
Environment:
Description: Type of Environment
Type: String
AllowedValues:
- Prd
- Stg
- Dev
Mappings:
EnvironmentMapping:
Prd:
VpcCidr: 10.0.1.0/24
InstanceName: prd-web-server
InstanceType: t2.large
Stg:
VpcCidr: 10.0.2.0/24
InstanceName: stg-web-server
InstanceType : t2.micro
Dev:
VpcCidr: 10.0.3.0/24
InstanceName: dev-web-server
InstanceType : t2.nano
Resources:
SampleVpc:
Type: AWS::EC2::VPC
Description: Sample VPC
Properties:
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: Sample VPC
SampleSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: !FindInMap [EnvironmentMapping, !Ref Environment, VpcCidr]
MapPublicIpOnLaunch: true
VpcId: !Ref SampleVpc
SampleRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref SampleVpc
SampleInternetGateway:
Type: AWS::EC2::InternetGateway
SampleGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref SampleVpc
InternetGatewayId: !Ref SampleInternetGateway
InternetRoute:
Type: AWS::EC2::Route
DependsOn:
- SampleGatewayAttachment
Properties:
RouteTableId: !Ref SampleRouteTable
GatewayId: !Ref SampleInternetGateway
DestinationCidrBlock: 0.0.0.0/0
SampleSubnetRouteTableAssoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref SampleRouteTable
SubnetId: !Ref SampleSubnet
SampleInstance:
Type: AWS::EC2::Instance
DependsOn:
- InternetRoute
- SampleSubnetRouteTableAssoc
Properties:
InstanceType: !FindInMap [EnvironmentMapping, !Ref Environment, InstanceType]
KeyName: xxxxxxxx
SubnetId: !Ref SampleSubnet
ImageId: ami-xxxxxxxxxxxxxxxx
SecurityGroupIds:
- !Ref SampleSecurityGroup
Tags:
- Key: Name
Value: !FindInMap [EnvironmentMapping, !Ref Environment, InstanceName]
SampleSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Sample Security Group
VpcId: !Ref SampleVpc
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: icmp
FromPort: -1
ToPort: -1
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
FromPort: 22
ToPort: 22
■コマンド実行
■PowerShellの場合
aws cloudformation create-stack `
--stack-name test-stack `
--template-url https://xxxxxxxxxx.s3.ap-northeast-1.amazonaws.com/Test-Template.yml `
--parameters ParameterKey=Environment,ParameterValue=Prd
{
"StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/test-stack/xxxxxxxxxxxxxx"
}
1分後くらいに下図のように完成しました。
このようにマッピング機能を利用することでコード上より各環境の差分を一目で確認でき、且つ他の部分は全環境で統一することができるようになります。
例として環境ごとに異なる部分を抽出してテンプレートを作成しましたが、例えばAMIなどの可変の部分もパラメータとして引き渡すことができます。
いろいろ工夫して理想のInfrastucture as Codeを実現していきましょう!
最後にですが、AWSは従量課金なので今回のスタックを作成すると課金されます。その為試用の方は動作確認後にスタックの削除をしましょう。スタックを削除することでCloudFormationで構築した環境は綺麗に削除されます。
_________________________________
お問合せはお気軽に
https://service.shiftinc.jp/contact/
SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/
SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/
SHIFTの導入事例
https://service.shiftinc.jp/case/
お役立ち資料はこちら
https://service.shiftinc.jp/resources/
SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/