AWS CDK入門としてネットワークリソースを作ってみた
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
にその旨を書きます(以下抜粋)。
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
から生成されています。
#!/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 ...
によってインポートされています。
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行追記しました。
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
を作成して、
そこにゴリゴリ書いていきます。
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
を書き換えます。
#!/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()
のような より上位のコンストラクタ を使った方が便利・楽です。
その際は ドキュメントを見て
- コンストラクタのパラメータが何なのか
- それぞれのデフォルト値が何なのか
といったところをよく見る必要があるかと思います。