初投稿です。お手柔らかにお願いします。
本日はAWSのインフラをコードで管理することでお馴染みのAWS CDK(AWS Cloud Development Kit)を利用して、さくっとALB×ECS(Fargate)でWebサーバを構築してみたいと思います。
ちなみに自分はCloudFormationはある程度習得済みで、CDK(v1)は少し齧った程度でほぼ忘れています。
実行環境
今回は、Cloud9(AWSのIDEサービス)の力をお借りします。
- OS : Amazon Linux2 (AL2)
- CDKで利用する開発言語:Python
CDKを利用する場合は、以下の環境を準備しておく必要がありますが、Cloud9(AL2)はすでにすべて揃っている状態で利用できます。
環境を整えるのって地味に面倒なので、大変ありがたいです。
- AWS CLI
- AWS Account and User
- Node.js
- IDE for your programming language
- AWS CDK Toolkit
- Python(開発言語)
※2022/03/19に起動したCloud9(AL2)のソフトウェアバージョンは以下の通り。
$ aws --version
aws-cli/1.19.112 Python/2.7.18 Linux/4.14.268-205.500.amzn2.x86_64 botocore/1.20.112
$ node -v
v16.14.0
$ cdk --version
2.16.0 (build 4c77925)
$ python3 --version
Python 3.7.10
とりあえずやってみる
1. プロジェクト作成
まずは、AWS CDKを利用してPython向けのプロジェクトを作成します。
$ mkdir cdk_sample $$ cd cdk_sample
$ cdk init --language python
...
Please run 'python3 -m venv .venv'!
Executing Creating virtualenv...
✅ All done!
成功するとAll done!
と表示されました。
そして作成されたプロジェクトフォルダ内のファイル構成はこんな感じです。
$ tree ./
./
├── app.py
├── cdk.json
├── cdk_sample
│ ├── cdk_sample_stack.py
│ └── __init__.py
├── README.md
├── requirements-dev.txt
├── requirements.txt
├── source.bat
└── tests
├── __init__.py
└── unit
├── __init__.py
└── test_cdk_sample_stack.py
3 directories, 11 files
プロジェクト内にはファイルが色々詰まっておりますが、今回編集するファイルは以下のファイルになりますので、それ以外の説明は今回は割愛します。(編集内容は以降の[4. ソースコード編集]参照)
-
cdk_sample/cdk_sample_stack.py
- メインで編集するクラスファイル。基本的にクラスの単位がCloudFormationのスタックになるはず。
- ファイル名などは適宜変更できますが今回はこのまま利用します。
- ここに利用するAWSリソースのコーディングしていきます。
-
app.py
- 上記クラスのインスタンス化を定義するクラスファイル。
- ここではデプロイ先のリージョン・アカウント指定やリソースタグ付け(一括)などを設定できます。
2. 仮想環境アクティブ化
既存環境または今後新規で建てる環境と干渉しないよう、本プロジェクト向けの仮想環境を起動し、その中で作業します。
$ source .venv/bin/activate
3. 必要モジュールのインストール
本開発に必要なAWS CDKのPythonモジュール群をインストールします。
必要なモジュールはrequirements.txt
にデフォルトで記載あるのでそれを叩けばよいです。
また、今回はECSを構築するので、ECS関連のモジュールもインストールします。
AWS CDKのモジュールはこちらのリファレンスから確認できます。
$ python -m pip install -r requirements.txt
$ python -m pip install aws-cdk.aws-ecs-patterns
なお、今回はaws-ecs
ではなく、aws-ecs-patterns
を利用します。
aws-ecs-patterns
は、ALBやNLBなどのECSと一緒に利用する可能性の高いAWSサービスの構築が盛り込まれているライブラリなので、コード数を抑えて手軽に構築ができそうです。
4. ソースコード編集
それではさっそくコードを書いていきます。
今回は、ALB×ECS(Fargate)
構成を構築したいので、aws-ecs-patterns
で用意されているコンストラクタのうちApplicationLoadBalancedFargateService
を利用します。(NLB×ECSやECSのスケジュール実行などのコンストラクタもありますので、気になる方はこちらをご確認ください)
そして実際に書いたソースコードはこちら。
- cdk_sample_stack.py
from aws_cdk import (
Stack,
aws_ecs as ecs,
aws_ecs_patterns as ecs_patterns,
)
from constructs import Construct
class ECSWebSampleStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
ecs_patterns.ApplicationLoadBalancedFargateService(self, "Nginx",
task_image_options=ecs_patterns.ApplicationLoadBalancedTaskImageOptions(
image=ecs.ContainerImage.from_registry("public.ecr.aws/nginx/nginx:latest")),
public_load_balancer=True
)
はい、たったの18行です。
本来であればCPU・メモリのリソースサイズやECSサービス内に起動するタスク数などの細かいパラメータを指定すると思いますが、デフォルトだとどういうものになるのかも気になるのであえてこれだけです。(設定できるパラメータはAPIリファレンス参照)
また、今回ECSで利用するコンテナイメージですが、今回は気分的にAWSで公開しているコンテナリポジトリからNginxを利用させてもらいます。
- app.py
#!/usr/bin/env python3
import aws_cdk as cdk
from cdk_sample.cdk_sample_stack import ECSWebSampleStack
app = cdk.App()
ecs_web_sample_stack = ECSWebSampleStack(app, "ECSWebSampleStack")
cdk.Tags.of(ecs_web_sample_stack).add("User", "matsumikan")
app.synth()
上記ソースはデフォルトでも利用できますがですが、今回クラス名を変更した(ECSWebSampleStack)のと、タグ付け設定も追加したかったので、その部分だけ修正しています。
5. 環境をデプロイ
準備ができたのでさっそくデプロイ…と言いたいところですが、AWS CDKを初めて利用する場合は以下のコマンドを実行する必要がありますので、とりあえず実行します。
$ cdk bootstrap
...
✅ Environment aws://{AWSAccountId}/{Region} bootstrapped.
本コマンドはCDKではソースをS3に配置してりようするなどの動作が含まれているので、CDKで利用するリソース群を設定してくれるおまじないです。難しいことは考えずにやってしまいます。
そして念願のデプロイ実行!
$ cdk deploy
実際に上記コマンドを実行すると、以下画像のように、事前に構築する際に確認などがあり、構築中や構築完了のステータスを確認できます。
今回は構築完了とともにALBのURLも出力してくれているので、そのURLを叩いてみました。
Nginxの画面が出力されています!成功です!
CloudFormation側を確認してみると、今回構築したスタックECSWebSampleStack
で計38個のリソースが無事に構築されていることが確認できました。
意外と多いように感じますが、ALB、ECSを構築するために必要なあれやこれ(VPCやセキュリティグループなどなど)が詰まっているのだと思います。
(ちなみに左側のスタックのリストにCDKToolkit
が表示されていると思いますが、こちらは先ほどcdk bootstrap
した際にできたスタックです)
そして地味に仕込んだタグ付けもうまくいっているようでした。(以下はAWS Resource Groups画面からタグ検索してみた結果です。)
6. 環境を削除
とりあえず目的は達成できたので、環境を消してしまいます。
CDKには環境を削除するコマンドも用意されているので、それを叩けば終了です。
※cdk bootstrap
で作成されたCDKToolkit
スタックは削除されないので、すべて消したい人は手動で削除する必要があります。
$ cdk destroy
しばらく待つと、無事に削除できたことを教えてくれます。
AWS CLIのスタック削除コマンドは非同期(削除実行した結果までは教えてくれない)なので、いちいちマネージメントコンソールにアクセスする必要があったのですが、CDKのコマンドは親切でいいですね。
X. 蛇足
cdk deploy
コマンドを実行すると環境が構築されますが、その前にどんな環境が作られるか確認したい!ということもあるかと思います。
そんな時にはcdk synth
というコマンドを利用するのもありのようです。
上記コマンドを実行すると、実際にデプロイされるCloudFormationスタックのテンプレートが出力されますので、その内容をみて具体的な構築内容を確認することができます。
(CloudFormationを通った人じゃないと確認しづらいかもしれませんが…)
ちなみに今回試した結果、700行弱のYAMLが出力されました。
cdk synthの結果(CloudFormation Tamplate YAML)
Resources:
NginxLB9C42F6B2:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
LoadBalancerAttributes:
- Key: deletion_protection.enabled
Value: "false"
Scheme: internet-facing
SecurityGroups:
- Fn::GetAtt:
- NginxLBSecurityGroupE007CBA8
- GroupId
Subnets:
- Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1Subnet3C273B99
- Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2Subnet95FF715A
Tags:
- Key: User
Value: matsumikan
Type: application
DependsOn:
- EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1DefaultRouteFF4E2178
- EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2DefaultRouteB1375520
Metadata:
aws:cdk:path: ECSWebSampleStack/Nginx/LB/Resource
NginxLBSecurityGroupE007CBA8:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Automatically created Security Group for ELB ECSWebSampleStackNginxLBB7FA19C6
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
Description: Allow from anyone on port 80
FromPort: 80
IpProtocol: tcp
ToPort: 80
Tags:
- Key: User
Value: matsumikan
VpcId:
Ref: EcsDefaultClusterMnL3mNNYNVpc7788A521
Metadata:
aws:cdk:path: ECSWebSampleStack/Nginx/LB/SecurityGroup/Resource
NginxLBSecurityGrouptoECSWebSampleStackNginxServiceSecurityGroup1FFA79D6808317A837:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId:
Fn::GetAtt:
- NginxLBSecurityGroupE007CBA8
- GroupId
IpProtocol: tcp
Description: Load balancer to target
DestinationSecurityGroupId:
Fn::GetAtt:
- NginxServiceSecurityGroup8D4F97B3
- GroupId
FromPort: 80
ToPort: 80
Metadata:
aws:cdk:path: ECSWebSampleStack/Nginx/LB/SecurityGroup/to ECSWebSampleStackNginxServiceSecurityGroup1FFA79D6:80
NginxLBPublicListener6F3FBDE4:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- TargetGroupArn:
Ref: NginxLBPublicListenerECSGroup8547737E
Type: forward
LoadBalancerArn:
Ref: NginxLB9C42F6B2
Port: 80
Protocol: HTTP
Metadata:
aws:cdk:path: ECSWebSampleStack/Nginx/LB/PublicListener/Resource
NginxLBPublicListenerECSGroup8547737E:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Port: 80
Protocol: HTTP
Tags:
- Key: User
Value: matsumikan
TargetGroupAttributes:
- Key: stickiness.enabled
Value: "false"
TargetType: ip
VpcId:
Ref: EcsDefaultClusterMnL3mNNYNVpc7788A521
Metadata:
aws:cdk:path: ECSWebSampleStack/Nginx/LB/PublicListener/ECSGroup/Resource
NginxTaskDefTaskRoleE7F42228:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Version: "2012-10-17"
Tags:
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/Nginx/TaskDef/TaskRole/Resource
NginxTaskDef7E40B47A:
Type: AWS::ECS::TaskDefinition
Properties:
ContainerDefinitions:
- Essential: true
Image: public.ecr.aws/nginx/nginx:latest
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group:
Ref: NginxTaskDefwebLogGroup726FAFBB
awslogs-stream-prefix: Nginx
awslogs-region:
Ref: AWS::Region
Name: web
PortMappings:
- ContainerPort: 80
Protocol: tcp
Cpu: "256"
ExecutionRoleArn:
Fn::GetAtt:
- NginxTaskDefExecutionRoleB15C3043
- Arn
Family: ECSWebSampleStackNginxTaskDefDECA1844
Memory: "512"
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Tags:
- Key: User
Value: matsumikan
TaskRoleArn:
Fn::GetAtt:
- NginxTaskDefTaskRoleE7F42228
- Arn
Metadata:
aws:cdk:path: ECSWebSampleStack/Nginx/TaskDef/Resource
NginxTaskDefwebLogGroup726FAFBB:
Type: AWS::Logs::LogGroup
Properties:
Tags:
- Key: User
Value: matsumikan
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
Metadata:
aws:cdk:path: ECSWebSampleStack/Nginx/TaskDef/web/LogGroup/Resource
NginxTaskDefExecutionRoleB15C3043:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Version: "2012-10-17"
Tags:
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/Nginx/TaskDef/ExecutionRole/Resource
NginxTaskDefExecutionRoleDefaultPolicy1DE03C9C:
Type: AWS::IAM::Policy
Properties:
PolicyDocument:
Statement:
- Action:
- logs:CreateLogStream
- logs:PutLogEvents
Effect: Allow
Resource:
Fn::GetAtt:
- NginxTaskDefwebLogGroup726FAFBB
- Arn
Version: "2012-10-17"
PolicyName: NginxTaskDefExecutionRoleDefaultPolicy1DE03C9C
Roles:
- Ref: NginxTaskDefExecutionRoleB15C3043
Metadata:
aws:cdk:path: ECSWebSampleStack/Nginx/TaskDef/ExecutionRole/DefaultPolicy/Resource
NginxServiceB96E6F5B:
Type: AWS::ECS::Service
Properties:
Cluster:
Ref: EcsDefaultClusterMnL3mNNYN926A5246
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 50
EnableECSManagedTags: false
HealthCheckGracePeriodSeconds: 60
LaunchType: FARGATE
LoadBalancers:
- ContainerName: web
ContainerPort: 80
TargetGroupArn:
Ref: NginxLBPublicListenerECSGroup8547737E
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: DISABLED
SecurityGroups:
- Fn::GetAtt:
- NginxServiceSecurityGroup8D4F97B3
- GroupId
Subnets:
- Ref: EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet1Subnet075EFF4C
- Ref: EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet2SubnetE4CEDF73
Tags:
- Key: User
Value: matsumikan
TaskDefinition:
Ref: NginxTaskDef7E40B47A
DependsOn:
- NginxLBPublicListenerECSGroup8547737E
- NginxLBPublicListener6F3FBDE4
Metadata:
aws:cdk:path: ECSWebSampleStack/Nginx/Service/Service
NginxServiceSecurityGroup8D4F97B3:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ECSWebSampleStack/Nginx/Service/SecurityGroup
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
Description: Allow all outbound traffic by default
IpProtocol: "-1"
Tags:
- Key: User
Value: matsumikan
VpcId:
Ref: EcsDefaultClusterMnL3mNNYNVpc7788A521
Metadata:
aws:cdk:path: ECSWebSampleStack/Nginx/Service/SecurityGroup/Resource
NginxServiceSecurityGroupfromECSWebSampleStackNginxLBSecurityGroup10EE3EE980B9A04806:
Type: AWS::EC2::SecurityGroupIngress
Properties:
IpProtocol: tcp
Description: Load balancer to target
FromPort: 80
GroupId:
Fn::GetAtt:
- NginxServiceSecurityGroup8D4F97B3
- GroupId
SourceSecurityGroupId:
Fn::GetAtt:
- NginxLBSecurityGroupE007CBA8
- GroupId
ToPort: 80
Metadata:
aws:cdk:path: ECSWebSampleStack/Nginx/Service/SecurityGroup/from ECSWebSampleStackNginxLBSecurityGroup10EE3EE9:80
EcsDefaultClusterMnL3mNNYN926A5246:
Type: AWS::ECS::Cluster
Properties:
Tags:
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Resource
EcsDefaultClusterMnL3mNNYNVpc7788A521:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: true
EnableDnsSupport: true
InstanceTenancy: default
Tags:
- Key: Name
Value: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/Resource
EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1Subnet3C273B99:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.0.0/18
VpcId:
Ref: EcsDefaultClusterMnL3mNNYNVpc7788A521
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs: ""
MapPublicIpOnLaunch: true
Tags:
- Key: aws-cdk:subnet-name
Value: Public
- Key: aws-cdk:subnet-type
Value: Public
- Key: Name
Value: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1/Subnet
EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1RouteTableA1FD6ACC:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: EcsDefaultClusterMnL3mNNYNVpc7788A521
Tags:
- Key: Name
Value: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1/RouteTable
EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1RouteTableAssociation8B583A17:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1RouteTableA1FD6ACC
SubnetId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1Subnet3C273B99
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1/RouteTableAssociation
EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1DefaultRouteFF4E2178:
Type: AWS::EC2::Route
Properties:
RouteTableId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1RouteTableA1FD6ACC
DestinationCidrBlock: 0.0.0.0/0
GatewayId:
Ref: EcsDefaultClusterMnL3mNNYNVpcIGW9C2C2B8F
DependsOn:
- EcsDefaultClusterMnL3mNNYNVpcVPCGW2447264E
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1/DefaultRoute
EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1EIP8704DB2F:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1/EIP
EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1NATGateway5E3732C1:
Type: AWS::EC2::NatGateway
Properties:
SubnetId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1Subnet3C273B99
AllocationId:
Fn::GetAtt:
- EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1EIP8704DB2F
- AllocationId
Tags:
- Key: Name
Value: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1/NATGateway
EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2Subnet95FF715A:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.64.0/18
VpcId:
Ref: EcsDefaultClusterMnL3mNNYNVpc7788A521
AvailabilityZone:
Fn::Select:
- 1
- Fn::GetAZs: ""
MapPublicIpOnLaunch: true
Tags:
- Key: aws-cdk:subnet-name
Value: Public
- Key: aws-cdk:subnet-type
Value: Public
- Key: Name
Value: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2/Subnet
EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2RouteTable263DEAA5:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: EcsDefaultClusterMnL3mNNYNVpc7788A521
Tags:
- Key: Name
Value: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2/RouteTable
EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2RouteTableAssociation43E5803C:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2RouteTable263DEAA5
SubnetId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2Subnet95FF715A
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2/RouteTableAssociation
EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2DefaultRouteB1375520:
Type: AWS::EC2::Route
Properties:
RouteTableId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2RouteTable263DEAA5
DestinationCidrBlock: 0.0.0.0/0
GatewayId:
Ref: EcsDefaultClusterMnL3mNNYNVpcIGW9C2C2B8F
DependsOn:
- EcsDefaultClusterMnL3mNNYNVpcVPCGW2447264E
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2/DefaultRoute
EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2EIPF0764873:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2/EIP
EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2NATGateway4C855E00:
Type: AWS::EC2::NatGateway
Properties:
SubnetId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2Subnet95FF715A
AllocationId:
Fn::GetAtt:
- EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2EIPF0764873
- AllocationId
Tags:
- Key: Name
Value: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2/NATGateway
EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet1Subnet075EFF4C:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.128.0/18
VpcId:
Ref: EcsDefaultClusterMnL3mNNYNVpc7788A521
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs: ""
MapPublicIpOnLaunch: false
Tags:
- Key: aws-cdk:subnet-name
Value: Private
- Key: aws-cdk:subnet-type
Value: Private
- Key: Name
Value: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet1
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet1/Subnet
EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet1RouteTable4F1D2E36:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: EcsDefaultClusterMnL3mNNYNVpc7788A521
Tags:
- Key: Name
Value: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet1
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet1/RouteTable
EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet1RouteTableAssociation34B92275:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet1RouteTable4F1D2E36
SubnetId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet1Subnet075EFF4C
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet1/RouteTableAssociation
EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet1DefaultRouteA5ADF694:
Type: AWS::EC2::Route
Properties:
RouteTableId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet1RouteTable4F1D2E36
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1NATGateway5E3732C1
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet1/DefaultRoute
EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet2SubnetE4CEDF73:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.192.0/18
VpcId:
Ref: EcsDefaultClusterMnL3mNNYNVpc7788A521
AvailabilityZone:
Fn::Select:
- 1
- Fn::GetAZs: ""
MapPublicIpOnLaunch: false
Tags:
- Key: aws-cdk:subnet-name
Value: Private
- Key: aws-cdk:subnet-type
Value: Private
- Key: Name
Value: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet2
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet2/Subnet
EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet2RouteTableDCE46591:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: EcsDefaultClusterMnL3mNNYNVpc7788A521
Tags:
- Key: Name
Value: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet2
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet2/RouteTable
EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet2RouteTableAssociation111C622F:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet2RouteTableDCE46591
SubnetId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet2SubnetE4CEDF73
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet2/RouteTableAssociation
EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet2DefaultRoute20CE2D89:
Type: AWS::EC2::Route
Properties:
RouteTableId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPrivateSubnet2RouteTableDCE46591
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId:
Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2NATGateway4C855E00
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet2/DefaultRoute
EcsDefaultClusterMnL3mNNYNVpcIGW9C2C2B8F:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc
- Key: User
Value: matsumikan
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/IGW
EcsDefaultClusterMnL3mNNYNVpcVPCGW2447264E:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId:
Ref: EcsDefaultClusterMnL3mNNYNVpc7788A521
InternetGatewayId:
Ref: EcsDefaultClusterMnL3mNNYNVpcIGW9C2C2B8F
Metadata:
aws:cdk:path: ECSWebSampleStack/EcsDefaultClusterMnL3mNNYN/Vpc/VPCGW
CDKMetadata:
Type: AWS::CDK::Metadata
Properties:
Analytics: v2:deflate64:H4sIAAAAAAAA/31RQW7CMBB8C3fjUiq1Z0opQkJtFBBX5DhL2BLsyF6DUJS/105CSGnVk2fHk+x4Zswfn/loIM52KNPDMMeElysS8sA8tS1B2m0hiMAoyydFkaMUhFottUhfRS6UhPRdmEwQrMCcUAKDXFhCmXtFUitQZacxL//+2rDp7ufc16ElUK3minv3a78ZaG60K4KkN1YMpN+5AukM0qWT/E/MMgPW/qIXquE3hQx3m2jKIpd4EyuXKKBa36FYO4K1SHK48TduYq2WWJvvxAHMFlE4PgTNfZRncWGRwVNItfvxQoUWoBM0TtppQr6z/REUhZdbXralrIU9vMEOFV5X3jNakUCfa4+7K7SOo4W58y3UfbSwYiiOvIx18976jLTPpjbYoIrlOvOeljrrcr/iqgrTp6PCEYvBamealX3sTaa1tYpFF9pr9fDEX/jjaPBlEYfGKcIj8Lg5vwEs3r5V0gIAAA==
Metadata:
aws:cdk:path: ECSWebSampleStack/CDKMetadata/Default
Condition: CDKMetadataAvailable
Outputs:
NginxLoadBalancerDNSC10D9E34:
Value:
Fn::GetAtt:
- NginxLB9C42F6B2
- DNSName
NginxServiceURLDB0E8C18:
Value:
Fn::Join:
- ""
- - http://
- Fn::GetAtt:
- NginxLB9C42F6B2
- DNSName
Conditions:
CDKMetadataAvailable:
Fn::Or:
- Fn::Or:
- Fn::Equals:
- Ref: AWS::Region
- af-south-1
- Fn::Equals:
- Ref: AWS::Region
- ap-east-1
- Fn::Equals:
- Ref: AWS::Region
- ap-northeast-1
- Fn::Equals:
- Ref: AWS::Region
- ap-northeast-2
- Fn::Equals:
- Ref: AWS::Region
- ap-south-1
- Fn::Equals:
- Ref: AWS::Region
- ap-southeast-1
- Fn::Equals:
- Ref: AWS::Region
- ap-southeast-2
- Fn::Equals:
- Ref: AWS::Region
- ca-central-1
- Fn::Equals:
- Ref: AWS::Region
- cn-north-1
- Fn::Equals:
- Ref: AWS::Region
- cn-northwest-1
- Fn::Or:
- Fn::Equals:
- Ref: AWS::Region
- eu-central-1
- Fn::Equals:
- Ref: AWS::Region
- eu-north-1
- Fn::Equals:
- Ref: AWS::Region
- eu-south-1
- Fn::Equals:
- Ref: AWS::Region
- eu-west-1
- Fn::Equals:
- Ref: AWS::Region
- eu-west-2
- Fn::Equals:
- Ref: AWS::Region
- eu-west-3
- Fn::Equals:
- Ref: AWS::Region
- me-south-1
- Fn::Equals:
- Ref: AWS::Region
- sa-east-1
- Fn::Equals:
- Ref: AWS::Region
- us-east-1
- Fn::Equals:
- Ref: AWS::Region
- us-east-2
- Fn::Or:
- Fn::Equals:
- Ref: AWS::Region
- us-west-1
- Fn::Equals:
- Ref: AWS::Region
- us-west-2
Parameters:
BootstrapVersion:
Type: AWS::SSM::Parameter::Value<String>
Default: /cdk-bootstrap/hnb659fds/version
Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]
Rules:
CheckBootstrapVersion:
Assertions:
- Assert:
Fn::Not:
- Fn::Contains:
- - "1"
- "2"
- "3"
- "4"
- "5"
- Ref: BootstrapVersion
AssertDescription: CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.
感想
公式のドキュメントやワークショップなどが潤沢なおかげで、そこまで迷うこともなく本当にさくっとALB×ECS(Fargate)を構築することができました。
ただ、手軽に構築できて嬉しい反面、AWS CDKの実用化に対する理解はあまり深まらなかったというのが現状です。
たとえば、以下のようなことはまだ試せていません。
- 他スタックと組み合わせて利用する場合はどうする?
- 例えばVPCなどを事前に作った場合に、CloudFormationだと
Outputs
でエクスポートした値をFn::ImportValue
でインポートして利用する…というようなことをしていましたが、CDKだとどうするのがよいのでしょう。
- 例えばVPCなどを事前に作った場合に、CloudFormationだと
- AWS CDKとCI/CDを組み合わせたいデプロイはどうする?
-
CodeBuild
上でcdk deploy
コマンドを実行し、環境をデプロイする?それとも、cdk synth
でテンプレートを出力してCodeDeploy
でCloudFormation
を実行するようにする?
-
自分はCloudFormationに慣れてしまっているのですが、CDKの方がコード数も少なく汎用性の高いものを作れそうなので、引き続きCDKの検証を実施して、実用化を目指したいなと思います。
それでは、またお会いしましょう。
参考URL