【AWS】CloudFormationでWebサービスの冗長構成を構築する
はじめに
これまで、AWSでサービスを構築する際には、AWSのコンソール画面から手動で構築していました。
しかし、それだと、構築したサービスの構成を把握するのが難しいですし、構築したサービスを再利用することもできません。また、手動で構築すると、ミスが発生する可能性もあります。
さらに、手動で構築すると、構築したサービスの構成をドキュメント化するのも大変です。
そこで今回は、AWSのサービスの構成をテンプレート化することで、サービスの構成を把握しやすくし、再利用しやすくすることを目的として、Webサービスを展開する際の冗長的な構成を構築するためのCloudFormationテンプレートを作成しました。
※ IaCだとTerraformも有名ですが、今回は勉強のためにCloudFormationを利用しています。
構成図
- Private SubnetにWebサーバ、DBサーバ、キャッシュサーバを配置。
- WebサーバへのアクセスはApplication Load Balancerを利用して行います。
- Public SubnetにNAT Gatewayを配置し、Private Subnetからインターネットへのアクセスを可能にします。
Github
ソースコード一式です。
実行手順
下記のようにスタックファイルを分割しました。
1から6まで順番に実行することを想定しています。
- 1-network.yaml
- 2-securitygroup.yaml
- 3-ec2.yaml
- 4-rds.yaml
- 5-elasticache.yaml
- 6-applicationloadbalancer.yaml
各スタックファイルについて
各スタックファイルで作成しているリソースは下記の通りです。
-
1-network.yaml
* VPC * Subnet * InternetGateway * NatGateway * RouteTable * Route * SubnetとRouteTableのAssociation * PublicSubnetのNetworkAcl * PrivateSubnetのNetworkAcl * PublicSubnetとNetworkAclのAssociation * PrivateSubnetとNetworkAclのAssociation
-
2-securitygroup.yaml
* ALBのSecurityGroup * WebサーバのSecurityGroup * SSHサーバのSecurityGroup * RDSのSecurityGroup * ElastiCacheのSecurityGroup
セキュリティグループはインバウンドルールとアウトバウンドルールにセキュリティグループを指定することができますが、循環参照を避けるために、セキュリティグループを作成した後に、インバウンドルールとアウトバウンドルールを設定しています。
こちらのセキュリティグループを後ほどのスタックファイルで参照して紐づけていきます。
※ NatGatewayにはセキュリティグループは割り当てることはできません。 -
3-ec2.yaml
* EC2インスタンス * EC2インスタンスのキーペア * EC2インスタンスのUserData * EC2インスタンスのEIP * EC2インスタンスのEIPのAssociation
-
4-rds.yaml
* RDSインスタンス * RDSインスタンスのパラメータグループ * RDSインスタンスのサブネットグループ
-
5-elasticache.yaml
* ElastiCacheインスタンス * ElastiCacheインスタンスのパラメータグループ * ElastiCacheインスタンスのサブネットグループ
-
6-applicationloadbalancer.yaml
* Application Load Balancer * Application Load Balancerのリスナー * Application Load Balancerのターゲットグループ
ファイアウォール
AWS上では、ファイアウォールとして、セキュリティグループとネットワークACLが利用できます。今回は、セキュリティグループとネットワークACLの両方を利用しています。
簡単に分けるとそれぞれ以下のような特徴があります。
-
ネットワークACL
- サブネット単位で設定する。
- ステートレスのため、行きの通信と帰りの通信を許可する必要がある。
-
セキュリティグループ
- EC2などのインスタンス単位で設定する。
- ステートフルのため、行きの通信を許可すれば、帰りの通信も許可される。
実際に構成したいのは、以下のような構成です。
ネットワークACL
Public SubnetとPrivate SubnetでそれぞれネットワークACLを作成しました。
-
Inbound Rule(Public Subnet)
Rule Number Protocol Port Range Source Action 100 TCP (6) 22 ClientIP Allow 101 TCP (6) 80 0.0.0.0/0 Allow 102 TCP (6) 443 0.0.0.0/0 Allow 103 TCP (6) 1024-65535 0.0.0.0/0 Allow * ALL ALL 0.0.0.0/0 Deny -
Outbound Rule(Public Subnet)
Rule Number Protocol Port Range Destination Action 100 TCP (6) 22 0.0.0.0/0 Allow 101 TCP (6) 80 0.0.0.0/0 Allow 102 TCP (6) 443 0.0.0.0/0 Allow 103 TCP (6) 1024-65535 0.0.0.0/0 Allow * ALL ALL 0.0.0.0/0 Deny -
Inbound Rule(Private Subnet)
Rule Number Protocol Port Range Source Action 100 TCP (6) 22 0.0.0.0/0 Allow 101 TCP (6) 80 0.0.0.0/0 Allow 102 TCP (6) 3306 0.0.0.0/0 Allow 103 TCP (6) 11211 0.0.0.0/0 Allow 104 TCP (6) 1024-65535 0.0.0.0/0 Allow * ALL ALL 0.0.0.0/0 Deny -
Outbound Rule(Private Subnet)
Rule Number Protocol Port Range Destination Action 100 TCP (6) 80 0.0.0.0/0 Allow 101 TCP (6) 443 0.0.0.0/0 Allow 102 TCP (6) 1024-65535 0.0.0.0/0 Allow * ALL ALL 0.0.0.0/0 Deny
セキュリティグループ
各リソースに対してセキュリティグループを作成しました。
セキュリティグループのSourceとDestinationにセキュリティグループを指定することで、スケーラビリティとセキュリティが担保しています。
-
ALBのSecurityGroup
Inbound Rule
Type Protocol Port Source HTTP TCP 80 0.0.0.0/0 HTTPS TCP 443 0.0.0.0/0 Outbound Rule
Type Protocol Port Destination HTTP TCP 80 WebサーバのSecurityGroup -
WebサーバのSecurityGroup
Inbound Rule
Type Protocol Port Source HTTP TCP 80 ALBのSecurityGroup SSH TCP 22 SSHサーバのSecurityGroup Outbound Rule
Type Protocol Port Destination MYSQL TCP 3306 RDSのSecurityGroup Custom TCP TCP 11211 ElastiCacheのSecurityGroup HTTP TCP 80 0.0.0.0/0 HTTPS TCP 443 0.0.0.0/0 -
SSHサーバのSecurityGroup
Inbound Rule
Type Protocol Port Source SSH TCP 22 Client IP Outbound Rule
Type Protocol Port Destination SSH TCP 22 WebサーバのSecurityGroup HTTP TCP 80 0.0.0.0/0 HTTPS TCP 443 0.0.0.0/0 -
RDSのSecurityGroup
Inbound Rule
Type Protocol Port Source MYSQL TCP 3306 WebサーバのSecurityGroup Outbound Rule
Type Protocol Port Destination All Traffic All All WebサーバのSecurityGroup -
ElastiCacheのSecurityGroup
Inbound Rule
Type Protocol Port Source Custom TCP TCP 11211 WebサーバのSecurityGroup Outbound Rule
Type Protocol Port Destination All Traffic All All WebサーバのSecurityGroup
CloudFormationの備忘
- !Ref でリソースの参照ができる。
PublicSubnetInboundHTTPRule: Type: AWS::EC2::NetworkAclEntry Properties: NetworkAclId: !Ref PublicSubnetNetworkAcl RuleNumber: 101 Protocol: 6 # TCP RuleAction: allow Egress: false CidrBlock: 0.0.0.0/0 PortRange: From: 80 To: 80
- !Sub で文字列の置換ができる。
InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub "${Prefix}InternetGateway"
- Exportで変数をエクスポートし、!ImportValue で他のスタックファイルで定義した値を参照できる。
EC2WebSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: !Sub "${Prefix}EC2WebSecurityGroup" GroupDescription: Security Group for EC2(Web) VpcId: !ImportValue VPC # Import VPC ID from network stack Tags: - Key: Name Value: !Sub "${Prefix}EC2WebSecurityGroup"
所感
- 初めてCloudFormationを利用してみましたが、記述自体のハードルは低めに感じました。
ParametersやMappingsを利用することで、環境によって変更する必要がある値を変数化することができるようになるので、そこで汎用性を高めることができるのかと思います。 - 「yamlを作成 → 実行 → コンソールで確認 → 動作確認 → yamlを修正」のようなサイクルを回す作業を行いました。実行に時間がかかる(体感午前帯だと早く夜だと遅い?)ので、サイクルは15~20分程度かかりましたが、それでも手動で構築するよりは安定していると感じました。
- 参考[1]と参考[2]にある通り、AWSが提供しているサンプルテンプレートがあるので、大変参考になりました。
- 構成に複数のリソースがあるため、どのようにCloudFormationのスタックファイルを分割しようかと悩みましたが、今回は土台となる部分(network)、リソースのグループの作成(security group)、リソースの作成(ec2, rds, elasticache, alb)というように分類してみました。
- S3、Cloudfront、Route53、Autoscaling、IAMあたりのスタックファイルも作成してみたいです。
参考
[1] サンプルテンプレート
[2] aws-cloudformation-templates
Discussion