この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWS Cloud Development Kit (AWS CDK) 、乗り遅れた感がありますが私も始めました。
入門としてVPCなどのネットワークリソースを作成してみます。
目次
環境
今回は Pythonを利用します。
インストールとセットアップ詳細は割愛します (以下の AWS Workshop, 弊社ブログを参照ください)
環境は以下のとおり
- cdk: 1.18.0
- Python: 3.7.3
セットアップ
初期セットアップしてきます。
practice-nw
ディレクトリ配下にプロジェクトを作成cdk init
で空のCDKプロジェクトを作成- Python の仮想環境を有効化
mkdir practice-nw
cd practice-nw
cdk init app --language=python
source .env/bin/activate
今回は VPCやサブネットなどを作成するために awscdk.awsec2 を利用します。
setup.py
にその旨を書きます(以下抜粋)。
./setup.py
install_requires=[
"aws-cdk.core",
"aws-cdk.aws-ec2"
],
pip install
で必要なパッケージをインストールします。
pip install -r requirements.txt
(必要な方のみ) 該当リージョン上で初めてCDKを実行する場合は cdk bootstrap
を一度実行する必要があります。
確認
cdk init
で出来たものを確認していきます。
cdk list
(もしくは cdk ls
)でこのプロジェクトで作成されるスタック一覧を表示します。
$ cdk ls
practice-nw
"practice-nw" というスタックは次の app.py
から生成されています。
./app.py
#!/usr/bin/env python3
from aws_cdk import core
from practice_nw.practice_nw_stack import PracticeNwStack
app = core.App()
PracticeNwStack(app, "practice-nw")
app.synth()
7行目ハイライト部分 PracticeNwStack
のコンストラクタで スタックが生成されます。
さらに、この "PracticeNwStack" クラス は 3行目ハイライト部分の
from practice_nw.practice_nw_stack ...
によってインポートされています。
./practice_nw/practice_nw_stack.py
from aws_cdk import core
class PracticeNwStack(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
それでは、この practice_nw_stack.py
にスタックのリソースを書いていきましょう。
Vpc()
を使ってリソースを作成 (2行追記)
practice_nw_stack.py
に (わずか) 2行追記しました。
./practice_nw/practice_nw_stack.py
from aws_cdk import core
from aws_cdk import aws_ec2 as ec2
class PracticeNwStack(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
ec2.Vpc(self, "vpc")
デプロイする前に cdk diff
で作られる予定のリソースを確認してみましょう。
cdk deploy
でデプロイします。
↓のように作成中/作成したリソースを分かりやすく表示してくれます。
マネジメントコンソールで
- CloudFormation スタックが作成されていること
- 各種リソースが作成されていること
を確認しましょう。
Vpc()
について
作成したリソースを図で表すと以下になります。
ec2.Vpc(self, "vpc")
でこれだけのリソースをよしなに作成してくれるのは、
コンストラクタのパラメータにそれぞれデフォルトの値が定義されているからです。
Vpc()
コンストラクタのパラメータを列挙してみます。
- cidr
- defaultInstanceTenancy
- enableDnsHostnames
- enableDnsSupport
- gatewayEndpoints
- maxAzs
- natGatewayProvider
- natGatewaySubnets
- natGateways
- subnetConfiguration
- vpnConnections
- vpnGateway
- vpnGatewayAsn
- vpnRoutePropagationcidr
- defaultInstanceTenancy
- enableDnsHostnames
- enableDnsSupport
- gatewayEndpoints
これらパラメータを指定することで、要件に合わせた VPCネットワークを作成することが出来ます。 ※それぞれの詳細は class Vpc (Python版) を参照ください
CfnXXX()
を使ってCloudFormationらしく作成 (184行追記)
CfnVPC() のように CloudFormationのリソースと 1対1 で対応 するクラスがあります。
今回は Vpc()
で出来る内容を (わざわざ) CfnXXX()
でも作成してみます。
まず cdk destroy
で前に作成したスタックを削除します。
practice_nw_cfn_stack.py
を作成して、
そこにゴリゴリ書いていきます。
./practice_nw/practice_nw_cfn_stack.py
from aws_cdk import core
from aws_cdk import aws_ec2 as ec2
class PracticeNwCfnStack(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
prefix = "PracticeNw"
def name(s): return "{0}/{1}".format(prefix, s)
# VPC
vpc = ec2.CfnVPC(
self, "vpc",
cidr_block="192.168.0.0/16",
enable_dns_hostnames=True,
enable_dns_support=True,
tags=[
core.CfnTag(key="Name", value=name("vpc"))
]
)
# InternetGateway
igw = ec2.CfnInternetGateway(
self, "igw",
tags=[
core.CfnTag(key="Name", value=name("igw"))
]
)
igw_attachment = ec2.CfnVPCGatewayAttachment(
self, "igw_attachment",
vpc_id=vpc.ref,
internet_gateway_id=igw.ref
)
# PrivateSubnetA
private_subnet_a = ec2.CfnSubnet(
self, "private_a",
vpc_id=vpc.ref,
cidr_block="192.168.0.0/24",
availability_zone="ap-northeast-1a",
tags=[
core.CfnTag(key="Name", value=name("private_a"))
]
)
# PrivateSubnetC
private_subnet_c = ec2.CfnSubnet(
self, "private_c",
vpc_id=vpc.ref,
cidr_block="192.168.1.0/24",
availability_zone="ap-northeast-1c",
tags=[
core.CfnTag(key="Name", value=name("private_c"))
]
)
# PublicSubnetA
public_subnet_a = ec2.CfnSubnet(
self, "public_a",
vpc_id=vpc.ref,
cidr_block="192.168.10.0/24",
availability_zone="ap-northeast-1a",
tags=[
core.CfnTag(key="Name", value=name("public_a"))
]
)
# PublicSubnetC
public_subnet_c = ec2.CfnSubnet(
self, "public_c",
vpc_id=vpc.ref,
cidr_block="192.168.11.0/24",
availability_zone="ap-northeast-1c",
tags=[
core.CfnTag(key="Name", value=name("public_c"))
]
)
# EIP1 (for NATGW)
eip1 = ec2.CfnEIP(
self, "eip1",
domain="vpc",
)
eip1.add_depends_on(igw_attachment)
# EIP2 (for NATGW)
eip2 = ec2.CfnEIP(
self, "eip2",
domain="vpc",
)
eip2.add_depends_on(igw_attachment)
# NatGatewayA
natgw_a = ec2.CfnNatGateway(
self, "natgw_a",
allocation_id=eip1.attr_allocation_id,
subnet_id=public_subnet_a.ref,
tags=[
core.CfnTag(key="Name", value=name("natgw_a"))
]
)
# NatGatewayC
natgw_c = ec2.CfnNatGateway(
self, "natgw_c",
allocation_id=eip2.attr_allocation_id,
subnet_id=public_subnet_c.ref,
tags=[
core.CfnTag(key="Name", value=name("natgw_c"))
]
)
# RouteTable of PrivateSubnetA
rtb_private_a = ec2.CfnRouteTable(
self, "rtb_private_a",
vpc_id=vpc.ref,
tags=[
core.CfnTag(key="Name", value=name("rtb_private_a"))
]
)
ec2.CfnSubnetRouteTableAssociation(
self, "rtb_private_a_association",
route_table_id=rtb_private_a.ref,
subnet_id=private_subnet_a.ref
)
ec2.CfnRoute(
self, "route_private_a",
route_table_id=rtb_private_a.ref,
destination_cidr_block="0.0.0.0/0",
nat_gateway_id=natgw_a.ref
)
# RouteTable of PrivateSubnetC
rtb_private_c = ec2.CfnRouteTable(
self, "rtb_private_c",
vpc_id=vpc.ref,
tags=[
core.CfnTag(key="Name", value=name("rtb_private_c"))
]
)
ec2.CfnSubnetRouteTableAssociation(
self, "rtb_private_c_association",
route_table_id=rtb_private_c.ref,
subnet_id=private_subnet_c.ref
)
ec2.CfnRoute(
self, "route_private_c",
route_table_id=rtb_private_c.ref,
destination_cidr_block="0.0.0.0/0",
nat_gateway_id=natgw_c.ref
)
# RouteTable of PublicSubnetA
rtb_public_a = ec2.CfnRouteTable(
self, "rtb_public_a",
vpc_id=vpc.ref,
tags=[
core.CfnTag(key="Name", value=name("rtb_public_a"))
]
)
ec2.CfnSubnetRouteTableAssociation(
self, "rtb_public_a_association",
route_table_id=rtb_public_a.ref,
subnet_id=public_subnet_a.ref
)
ec2.CfnRoute(
self, "route_public_a",
route_table_id=rtb_public_a.ref,
destination_cidr_block="0.0.0.0/0",
gateway_id=igw.ref
)
# RouteTable of PublicSubnetC
rtb_public_c = ec2.CfnRouteTable(
self, "rtb_public_c",
vpc_id=vpc.ref,
tags=[
core.CfnTag(key="Name", value=name("rtb_public_c"))
]
)
ec2.CfnSubnetRouteTableAssociation(
self, "rtb_public_c_association",
route_table_id=rtb_public_c.ref,
subnet_id=public_subnet_c.ref
)
ec2.CfnRoute(
self, "route_public_c",
route_table_id=rtb_public_c.ref,
destination_cidr_block="0.0.0.0/0",
gateway_id=igw.ref
)
app.py
を書き換えます。
./app.py
#!/usr/bin/env python3
from aws_cdk import core
# from practice_nw.practice_nw_stack import PracticeNwStack
from practice_nw.practice_nw_cfn_stack import PracticeNwCfnStack
app = core.App()
# PracticeNwStack(app, "practice-nw")
PracticeNwCfnStack(app, "practice-nw-cfn")
app.synth()
cdk deploy
でデプロイします。
184行書いてできたものは 残念ながら 細かいパラメータに違いはあれど 2行追記した Vpc()
と同じです。
まとめ
今回は 2パターンでネットワークリソースを作成してみました。 AWS CDK 便利すぎますね 。
AWS CDKの第1印象としては ネットワークリソースをよしなに作成してくるけど、細かい制御はできないのか〜 と、誤った印象を持っていました。
しかし Vpc()
の パラメータ を設定したり、
そもそも CloudFormationと 1対1で対応しているクラス を使用したりすることで、
CloudFormationテンプレートと同じレベルで制御ができることが分かりました。
(私はVSCodeで) 書いていて思ったことですが、プログラムで書いていることもあり、補完機能が素晴らしいです。
CloudFormationらしく 184行も追記しましたが、ほとんどストレスなく書けました。 「リソースのパラメータに何を書けば良いのか」 といった部分はポップアップ画面に出してくれるため、わざわざドキュメントを見に行く手間が省けます。
はじめのうちは、CDKになれるためにも CloudFormationらしく書いても良いかもしれません。
ただ、CDKらしく書くには、もちろん Vpc()
のような より上位のコンストラクタ を使った方が便利・楽です。
その際は ドキュメントを見て
- コンストラクタのパラメータが何なのか
- それぞれのデフォルト値が何なのか
といったところをよく見る必要があるかと思います。