CloudFormation設計を一段レベルアップ!ネスト構成で快適インフラ運用

目次
はじめに
你好ニーハウ!(こんにちは!)クラウドインテグレーション部の盧(ろ)です。
本記事はCloudFormationスタックのネスト構成について紹介していきます。
CloudFormationについてまだご存じでない方は、井川さんが執筆していたこちらの記事をご参照いただければと思います。
ネスト構成について
ネストスタックは、CloudFormationテンプレートの中で、別のCloudFormationテンプレートを呼び出して利用する仕組みです。
つまり、1つの大きなテンプレートを小さなテンプレートに分けて、親テンプレートからまとめて使う方法なんです。
例えば、ネットワーク設定、アプリケーション設定、データベース設定などを、それぞれ別々のテンプレートに分けて、必要な部分だけを親テンプレートで呼び出すことができます。
ネスト化するメリットについて
-
テンプレートの分割で保守性アップ
CloudFormationテンプレートは機能が増えるにつれて巨大化しがちです。
ネスト化すれば「VPC」「EC2」「RDS」などを個別ファイルに分割でき、テンプレートの見通しが良くなり、修正も部分的に行いやすくなります。 -
再利用性の向上
ネストスタックはモジュール化されたパーツのように使えます。
たとえば「共通のVPC定義」や「標準構成のセキュリティグループ」など、複数環境で使いまわしたい場合に非常に便利です。 -
チーム開発に強い
テンプレートを分割できることで、担当チームごとに管理を分担できます。
たとえば、以下のようにできます。- ネットワーク担当 → vpc.yaml
- インフラ担当 → ec2.yaml
- DB担当 → rds.yaml
-
変更影響範囲の限定
親スタックで子スタックを参照している場合、一部の子スタックだけを更新できます。
これにより、不要な再デプロイ(=ダウンタイムやリスク)を減らすことができます。
子スタックの更新方法は以下の2通りがありますが、基本的には「親スタックからの更新」が推奨されます。- 親スタックから更新する(推奨)
子スタックのテンプレートを新しいものに差し替え、親スタックを更新します。
操作上は親スタックの更新になりますが、実際に変更されるのは子スタックだけです。 - 子スタック単体で更新する
子スタックを選択して直接テンプレートを更新する方法です。
可能ではありますが、依存関係や整合性の観点から通常はあまり使いません。
このように、子スタックだけを更新する場合でも、操作は親スタック経由になることが多い点にご注意ください。
- 親スタックから更新する(推奨)
-
構造の明確化と理解しやすさ
ネスト化により、テンプレートに論理的な階層構造が生まれます。
例:root.yaml
├── vpc.yaml
├── ec2.yaml
└── rds.yaml
これにより、初見の人にも「どこで何をしているか」がすぐに把握できるようになります。
具体例
では、実例を見ていきましょう!
構成図
作成する構成は以下とします。
テンプレート
テンプレートはサービス単位で分割して管理します。
ここでは、親スタック用のテンプレートをご紹介します。
子スタック用のテンプレート(VPC、EC2、RDS)については、記事の最後にある付録セクションでまとめて掲載していますので、ぜひそちらもチェックしてみてください。
親スタック用テンプレート
AWSTemplateFormatVersion: "2010-09-09"
Description: 'Main template that creates VPC, EC2, and RDS resources using nested stacks'
Parameters:
VPCTemplateURL:
Description: template url for vpc
Type: String
Default: https://xxxxx/vpc.yml #使用するVPCテンプレートのS3 URLに置き換えてください
EC2TemplateURL:
Description: template url for ec2
Type: String
Default: https://xxxxx/ec2.yml #使用するEC2テンプレートのS3 URLに置き換えてください
RDSTemplateURL:
Description: template url for rds
Type: String
Default: https://xxxxx/rds.yml #使用するRDSテンプレートのS3 URLに置き換えてください
Resources:
# VPC用の子スタックを呼び出し
VPCStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref VPCTemplateURL
# EC2用の子スタックを呼び出し
EC2Stack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref EC2TemplateURL
Parameters:
VpcId: !GetAtt VPCStack.Outputs.VpcId # VPCスタックで出力されたVPC IDを参照
PublicSubnetId: !GetAtt VPCStack.Outputs.PublicSubnetId # VPCスタックで出力されたPublic Subnet IDを参照
DependsOn: VPCStack # VPCの構築完了後にEC2スタックの作成を開始
# RDS用の子スタックを呼び出し
RDSStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref RDSTemplateURL
Parameters:
VpcId: !GetAtt VPCStack.Outputs.VpcId
PrivateSubnet1Id: !GetAtt VPCStack.Outputs.PrivateSubnet1Id # VPCスタックで出力されたPrivate Subnet IDを参照
PrivateSubnet2Id: !GetAtt VPCStack.Outputs.PrivateSubnet2Id # VPCスタックで出力されたPrivate Subnet IDを参照
EC2SecurityGroupId: !GetAtt EC2Stack.Outputs.EC2SecurityGroupId # EC2スタックの出力からセキュリティグループを取得
DependsOn: VPCStack # VPCの構築完了後にRDSスタックの作成を開始
Outputs:
# 親スタックのOutputsとして、子スタックの重要な情報を集約して出力
EC2InstanceId:
Description: ID of the EC2 instance
Value: !GetAtt EC2Stack.Outputs.EC2InstanceId
EC2PublicIP:
Description: Public IP of the EC2 instance
Value: !GetAtt EC2Stack.Outputs.EC2PublicIP
RDSEndpoint:
Description: Endpoint of the RDS instance
Value: !GetAtt RDSStack.Outputs.RDSEndpoint
さいごに
CloudFormationスタックのネスト構成について紹介しましたが、いかがでしたでしょうか。
複雑なシステム構成を作成する際には、ネスト構成をぜひ活用してみてください。
本記事が少しでもご参考になれば幸いです。
付録:子スタック用のテンプレート
VPC テンプレート(vpc.yml)
AWSTemplateFormatVersion: '2010-09-09'
Description: 'VPC with public and private subnets'
Resources:
# VPC本体の作成
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: nested-test-vpc
# インターネットゲートウェイ(IGW)とVPCへのアタッチ
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: nested-test-igw
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
# パブリックサブネット(NAT Gateway配置用)
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [0, !GetAZs '']
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: nested-test-public-subnet
# プライベートサブネット1
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [0, !GetAZs '']
CidrBlock: 10.0.2.0/24
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: nested-test-private-subnet1
# プライベートサブネット2
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [1, !GetAZs '']
CidrBlock: 10.0.3.0/24
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: nested-test-private-subnet2
# パブリックサブネット用のルートテーブルとルート設定(IGW経由のインターネット通信)
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: nested-test-public-rtb
PublicRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
# NAT GatewayとそのEIP(プライベートサブネット用)
NatGatewayEIP:
Type: AWS::EC2::EIP
DependsOn: InternetGatewayAttachment
Properties:
Domain: vpc
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGatewayEIP.AllocationId
SubnetId: !Ref PublicSubnet
Tags:
- Key: Name
Value: nested-test-ngw
# プライベートサブネット用のルートテーブル(NAT経由でインターネットアクセス)
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: nested-test-private-rtb
PrivateRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref PrivateRouteTable
PrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet2
RouteTableId: !Ref PrivateRouteTable
Outputs:
VpcId:
Description: VPC ID
Value: !Ref VPC
PublicSubnetId:
Description: Public Subnet ID
Value: !Ref PublicSubnet
PrivateSubnet1Id:
Description: Private Subnet 1 ID
Value: !Ref PrivateSubnet1
PrivateSubnet2Id:
Description: Private Subnet 2 ID
Value: !Ref PrivateSubnet2
EC2 テンプレート(ec2.yml)
AWSTemplateFormatVersion: '2010-09-09'
Description: 'EC2 instance in a public subnet'
Parameters:
VpcId:
Type: String
Description: VPC ID
PublicSubnetId:
Type: String
Description: Public Subnet ID
Resources:
# EC2用のセキュリティグループ(SSH許可)
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for EC2 instance
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: nested-test-ec2-sg
# パブリックサブネット内のEC2インスタンス
EC2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-03598bf9d15814511 # 東京リージョンのAmazon Linux 2023
SubnetId: !Ref PublicSubnetId
SecurityGroupIds:
- !Ref EC2SecurityGroup
Tags:
- Key: Name
Value: nested-test-ec2
Outputs:
EC2InstanceId:
Description: ID of the EC2 instance
Value: !Ref EC2Instance
EC2PublicIP:
Description: Public IP of the EC2 instance
Value: !GetAtt EC2Instance.PublicIp
EC2SecurityGroupId:
Description: ID of the EC2 security group
Value: !Ref EC2SecurityGroup
RDS テンプレート(rds.yml)
AWSTemplateFormatVersion: '2010-09-09'
Description: 'RDS instance in a private subnet'
Parameters:
VpcId:
Type: String
Description: VPC ID
PrivateSubnet1Id:
Type: String
Description: Private Subnet 1 ID
PrivateSubnet2Id:
Type: String
Description: Private Subnet 2 ID
EC2SecurityGroupId:
Type: String
Description: EC2 Security Group ID
Resources:
# RDS用セキュリティグループ(EC2からのMySQL接続を許可)
RDSSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for RDS instance
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
SourceSecurityGroupId: !Ref EC2SecurityGroupId
Tags:
- Key: Name
Value: nested-test-rds-sg
# RDS用サブネットグループ(Multi-AZ用で2つのAZにまたがる)
DBSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: Subnet group for RDS instance
SubnetIds:
- !Ref PrivateSubnet1Id
- !Ref PrivateSubnet2Id
Tags:
- Key: Name
Value: nested-test-rds-subnetgroup
# RDSインスタンスの作成(MySQL)
RDSInstance:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceIdentifier: my-rds-instance
AllocatedStorage: 20
DBInstanceClass: db.t3.micro
BackupRetentionPeriod: 7
Engine: mysql
MasterUsername: masteruser
MasterUserPassword: masterpassword
DBSubnetGroupName: !Ref DBSubnetGroup
VPCSecurityGroups:
- !Ref RDSSecurityGroup
MultiAZ: false
Tags:
- Key: Name
Value: nested-test-rds
Outputs:
RDSEndpoint:
Description: Endpoint of the RDS instance
Value: !GetAtt RDSInstance.Endpoint.Address
アジアクエスト株式会社では一緒に働いていただける方を募集しています。
興味のある方は以下のURLを御覧ください。