この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
どーもsutoです。
私は業務のなかでCloudFormationを使ってインフラ構築を自動化しているのですが、今回はAWS Cloud Development Kit(CDK)を使ってPythonコードでインフラ構築自動化をやってみたいと思います。
AWS CDKは、CloudFormationのテンプレートをTypescriptやPythonのようなプログラミング言語で記述でき、コマンド1つでデプロイ、破棄、スタック更新のDiffができるツールです。
はじめに
今回、自分のアカウントで機械学習検証用環境のインフラを構築し、いつでも環境のデプロイ/破棄ができるように自動化&コード管理したい、というモチベからAWS CDKを本格的に触ってみることにしました。
本検証の目標
- AWS CDKのインストールと初期設定、VPC構築(←本記事)
- Sagemakerのノートブックインスタンスを自動構築、VPCとの関連付け
- ノートブックインスタンスのスケジュールによる自動停止を行うLambdaの追加設定
でやっていきます。
2、3の内容は後ほど別の記事でまとめていきます。
AWS CDKのインストール
ローカル作業環境はMacでPython3インストール済みです。
まずnpmでインストールします
npm install -g aws-cdk
cdk --version
ローカル環境での下準備
CDKの新しいプロジェクト用のフォルダを作成し、移動します。
mkdir sage-nw && cd sage-nw
使用言語を指定してプロジェクトの雛形を作成します。
~ sage-nw % cdk init --language python
~ sage-nw % ls
README.md app.py cdk.json requirements.txt sage_nw setup.py source.bat
ディレクトリ内の仮想環境に入ります。
~ sage-nw % source .env/bin/activate
(.env) ~ sage-nw %
setup.py
の「install_requires」部分を編集(aws-cdk.aws-ec2追記)して必要なリソースのモジュールもインストールします。
install_requires=[
"aws-cdk.core",
"aws-cdk.aws-ec2",
],
pip install -r requirements.txt
app.py
を編集してデプロイするリージョンを指定しておくと便利です。
SageNwStack(app, "sage-nw", env={'region': 'ap-northeast-1'})
コードを記述
ここから「sage_nw」フォルダ内のsage_nw_stack.py
にAWSリソースの構築内容を記述して保存します。
今回のVPC構築内容は以下のとおりです。
- CIDRが/16のVPCを1つ新規作成
- CIDRが/24のパブリックサブネット2つ、プライベートサブネット2つを各AZに1つずつ配置されるように新規作成
- セキュリティグループを1つ新規作成(sagemaker ノートブックにアタッチする用に作っておきます)
from aws_cdk import core
from aws_cdk import aws_ec2 as ec2
class SageNwStack(core.Stack):
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
# The code that defines your stack goes here
# 変数の宣言
vpc_cidr = '10.1.0.0/16'
subnet_mask = 24
# 新規VPC作成
vpc = ec2.Vpc(
self,
id="Sage-vpc",
cidr=vpc_cidr,
nat_gateways=0,
subnet_configuration=[
ec2.SubnetConfiguration(
cidr_mask=subnet_mask, name='public', subnet_type=ec2.SubnetType.PUBLIC,
),
ec2.SubnetConfiguration(
cidr_mask=subnet_mask, name='private', subnet_type=ec2.SubnetType.ISOLATED,
),
],
)
security_group = ec2.SecurityGroup(
self,
id='Sage-sg',
vpc=vpc,
security_group_name='test-sg'
)
security_group.add_ingress_rule(
peer=ec2.Peer.ipv4(vpc_cidr),
connection=ec2.Port.all_traffic(),
)
デフォルト設定でVPCを構築すると、サブネットのCIDRが勝手に決められる、NATゲートウェイが作成されるなど少々都合が悪かったのでいろいろオプションを指定しています。
参考
スタックの確認とデプロイ
lsコマンドで本プロジェクトで作成されるスタック一覧を表示できます。
cdk ls
cdk synth
コマンドでCloudFormationテンプレで記述した場合のコードを見ることができます。
Resources:
SagevpcFE72F9E6:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.1.0.0/16
EnableDnsHostnames: true
EnableDnsSupport: true
InstanceTenancy: default
Tags:
- Key: Name
Value: sage-nw/Sage-vpc
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/Resource
SagevpcpublicSubnet1Subnet94477DD3:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.1.0.0/24
VpcId:
Ref: SagevpcFE72F9E6
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: sage-nw/Sage-vpc/publicSubnet1
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/publicSubnet1/Subnet
SagevpcpublicSubnet1RouteTableC299A029:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: SagevpcFE72F9E6
Tags:
- Key: Name
Value: sage-nw/Sage-vpc/publicSubnet1
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/publicSubnet1/RouteTable
SagevpcpublicSubnet1RouteTableAssociation6D1A442B:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId:
Ref: SagevpcpublicSubnet1RouteTableC299A029
SubnetId:
Ref: SagevpcpublicSubnet1Subnet94477DD3
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/publicSubnet1/RouteTableAssociation
SagevpcpublicSubnet1DefaultRoute07A07172:
Type: AWS::EC2::Route
Properties:
RouteTableId:
Ref: SagevpcpublicSubnet1RouteTableC299A029
DestinationCidrBlock: 0.0.0.0/0
GatewayId:
Ref: SagevpcIGW9475FA95
DependsOn:
- SagevpcVPCGW7B030D8E
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/publicSubnet1/DefaultRoute
SagevpcpublicSubnet2Subnet2C67E71A:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.1.1.0/24
VpcId:
Ref: SagevpcFE72F9E6
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: sage-nw/Sage-vpc/publicSubnet2
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/publicSubnet2/Subnet
SagevpcpublicSubnet2RouteTable8690CC41:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: SagevpcFE72F9E6
Tags:
- Key: Name
Value: sage-nw/Sage-vpc/publicSubnet2
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/publicSubnet2/RouteTable
SagevpcpublicSubnet2RouteTableAssociation3C69FE0F:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId:
Ref: SagevpcpublicSubnet2RouteTable8690CC41
SubnetId:
Ref: SagevpcpublicSubnet2Subnet2C67E71A
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/publicSubnet2/RouteTableAssociation
SagevpcpublicSubnet2DefaultRouteE00663C9:
Type: AWS::EC2::Route
Properties:
RouteTableId:
Ref: SagevpcpublicSubnet2RouteTable8690CC41
DestinationCidrBlock: 0.0.0.0/0
GatewayId:
Ref: SagevpcIGW9475FA95
DependsOn:
- SagevpcVPCGW7B030D8E
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/publicSubnet2/DefaultRoute
SagevpcprivateSubnet1SubnetB83A6331:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.1.2.0/24
VpcId:
Ref: SagevpcFE72F9E6
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs: ""
MapPublicIpOnLaunch: false
Tags:
- Key: aws-cdk:subnet-name
Value: private
- Key: aws-cdk:subnet-type
Value: Isolated
- Key: Name
Value: sage-nw/Sage-vpc/privateSubnet1
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/privateSubnet1/Subnet
SagevpcprivateSubnet1RouteTable1F39D14D:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: SagevpcFE72F9E6
Tags:
- Key: Name
Value: sage-nw/Sage-vpc/privateSubnet1
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/privateSubnet1/RouteTable
SagevpcprivateSubnet1RouteTableAssociation385BE067:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId:
Ref: SagevpcprivateSubnet1RouteTable1F39D14D
SubnetId:
Ref: SagevpcprivateSubnet1SubnetB83A6331
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/privateSubnet1/RouteTableAssociation
SagevpcprivateSubnet2Subnet6652904B:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.1.3.0/24
VpcId:
Ref: SagevpcFE72F9E6
AvailabilityZone:
Fn::Select:
- 1
- Fn::GetAZs: ""
MapPublicIpOnLaunch: false
Tags:
- Key: aws-cdk:subnet-name
Value: private
- Key: aws-cdk:subnet-type
Value: Isolated
- Key: Name
Value: sage-nw/Sage-vpc/privateSubnet2
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/privateSubnet2/Subnet
SagevpcprivateSubnet2RouteTableB1CE60A7:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: SagevpcFE72F9E6
Tags:
- Key: Name
Value: sage-nw/Sage-vpc/privateSubnet2
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/privateSubnet2/RouteTable
SagevpcprivateSubnet2RouteTableAssociationD4FFD8F0:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId:
Ref: SagevpcprivateSubnet2RouteTableB1CE60A7
SubnetId:
Ref: SagevpcprivateSubnet2Subnet6652904B
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/privateSubnet2/RouteTableAssociation
SagevpcIGW9475FA95:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: sage-nw/Sage-vpc
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/IGW
SagevpcVPCGW7B030D8E:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId:
Ref: SagevpcFE72F9E6
InternetGatewayId:
Ref: SagevpcIGW9475FA95
Metadata:
aws:cdk:path: sage-nw/Sage-vpc/VPCGW
SagesgCC54FEB9:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: sage-nw/Sage-sg
GroupName: test-sg
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
Description: Allow all outbound traffic by default
IpProtocol: "-1"
SecurityGroupIngress:
- CidrIp: 10.1.0.0/16
Description: from 10.1.0.0/16:ALL TRAFFIC
IpProtocol: "-1"
VpcId:
Ref: SagevpcFE72F9E6
Metadata:
aws:cdk:path: sage-nw/Sage-sg/Resource
CDKMetadata:
Type: AWS::CDK::Metadata
Properties:
Modules: aws-cdk=1.54.0,@aws-cdk/assets=1.59.0,@aws-cdk/aws-cloudwatch=1.59.0,@aws-cdk/aws-ec2=1.59.0,@aws-cdk/aws-events=1.59.0,@aws-cdk/aws-iam=1.59.0,@aws-cdk/aws-kms=1.59.0,@aws-cdk/aws-logs=1.59.0,@aws-cdk/aws-s3=1.59.0,@aws-cdk/aws-s3-assets=1.59.0,@aws-cdk/aws-ssm=1.59.0,@aws-cdk/cloud-assembly-schema=1.54.0,@aws-cdk/core=1.54.0,@aws-cdk/cx-api=1.54.0,@aws-cdk/region-info=1.59.0,jsii-runtime=Python/3.8.2
CloudFormationでCDK実行環境を作成します。(AWS アカウント、リージョン単位で一度だけ実行するコマンドです。(リージョンで1度実行したことがあれば、2回目以降は実行する必要はありません)以下の例では--profileでスイッチロール先である自分のアカウントを指定しています。
cdk bootstrap --profile suto
確認が終わったら以下コマンドでスタック作成してみます。
(.env) ~ sage-nw % cdk deploy sage-nw --profile suto
無事スタックCOMPLETEしました。
まとめ
AWSCDKとCloudFormationのコードの長さを比較してみると、記述の短さは一目瞭然でCDKでスタックを作成した方が簡単であることがわかります。
今回は入門編という内容でVPC構築のみでした。次回の記事でSagemakerのノートブックインスタンスを構築するコードを追加してみようと思います。
<次回記事>