【CDK/Python】CDKでSubnetのCidrBlockを指定する2つの方法
CDKでVPCを作ってかつ、SubnetのCIDRは指定した値にする!ということをやりたかったのですが、 想像以上にハマったので記事にしました。
何がしたいの?
CDKでVPCを作って、さらにSubnetも作ってそのCIDRを任意の値に設定したい!
何も考えずにCDKでVPCを作ると勝手にSubnetが作られる、、、このときCidrBlockが自動で発番されてしまって任意の値に設定できない!
CIDRがどうでもいいよ!というユースケースもあるので、その場合はどうでもいいんですけれどもね(笑)
対策
まずCIDRを指定する方法ですが、2つありました。
1つ目がSubnetをVPCコンストラクトに自動発番させて、cfnの値を上書きする方法です。
もう1つがVPCとSubnetを個別で作る方法です。
方法1. SubnetはVPCコンストラクトに自動発番させて、cfnの値を上書きする
具体的なコードは下記の通りです。
import pathlib
from aws_cdk import Stack, aws_ec2, aws_lambda
from constructs import Construct
VPC_CIDR = "10.11.0.0/16"
PRIVATE_SUBNET_1_CIDR = "10.11.10.0/24"
PRIVATE_SUBNET_2_CIDR = "10.11.20.0/24"
class CdkNetworkStack1(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# VPC
vpc = aws_ec2.Vpc(
self,
"vpc1",
cidr=VPC_CIDR,
max_azs=2,
subnet_configuration=[
aws_ec2.SubnetConfiguration(
name="Private",
cidr_mask=24,
subnet_type=aws_ec2.SubnetType.PRIVATE_ISOLATED,
)
],
)
# SubnetのCidrBlockを上書きする
selection = vpc.select_subnets(subnet_type=aws_ec2.SubnetType.PRIVATE_ISOLATED)
for cider, subnet in zip(
[PRIVATE_SUBNET_1_CIDR, PRIVATE_SUBNET_2_CIDR], selection.subnets
):
cfn_subnet: aws_ec2.CfnSubnet = subnet.node.default_child
cfn_subnet.cidr_block = cider
# (おまけ)VPCにLambdaを乗せる場合の設定例
aws_lambda.Function(
self,
"function1",
code=aws_lambda.Code.from_asset(
str(pathlib.Path(__file__).resolve().parent.joinpath("runtime"))
),
runtime=aws_lambda.Runtime.PYTHON_3_9,
handler="index.handler",
vpc=vpc,
vpc_subnets=aws_ec2.SubnetSelection(
subnet_type=aws_ec2.SubnetType.PRIVATE_ISOLATED
),
security_groups=[aws_ec2.SecurityGroup(self, "SG1", vpc=vpc)],
)
ポイントはcfn側のCidrBlock
を上書きしていることと、 max_azs=2
でSubnetの数を制御してること。
不安なところは、CDKにSubnetをほぼ自動で作らせて、作られるSubnetの数を予想してCIDRを割り当てているので、 何かの拍子で意図しないSubnetが作られちゃったりした場、for分の箇所が成立しなくなってCIDRがおかしくなる、、、的な可能性が0ではないので若干不安です。。
(2022/04/1) 不安のもとが、つまりselect_subnets
で色々取れちゃう、、ということがコアであることに気が付きました。そしてその原因がsubnet_type
を指定して取得しているからだったので、任意でSubnetに名前をつけて、その名前で取得すれば不安にならない、、ということに気が付きました!
つまりこんな感じ↓
...略
SUBNETS_NAME = "HogePrivate"
class CdkNetworkStack1(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
...略
# VPC
vpc = aws_ec2.Vpc(
...略
subnet_configuration=[
aws_ec2.SubnetConfiguration(
...略
name=SUBNETS_NAME,
)
],
)
# SubnetのCidrBlockを上書きする
selection = vpc.select_subnets(
subnet_group_name=SUBNETS_NAME,
)
for cider, subnet in zip( [PRIVATE_SUBNET_1_CIDR, PRIVATE_SUBNET_2_CIDR], selection.subnets):
...略
※ githubのコードに反映済みなので、詳しく見たい方はリポジトリ参照ください!
方法2. VPCとSubnetをバラで作る
こちらも体的なコードは下記の通りです。
import pathlib
from aws_cdk import Stack, aws_ec2, aws_lambda
from constructs import Construct
VPC_CIDR = "10.22.0.0/16"
PRIVATE_SUBNET_1_CIDR = "10.22.10.0/24"
PRIVATE_SUBNET_2_CIDR = "10.22.20.0/24"
PRIVATE_SUBNET_1_AVAILABILITY_ZONE = "ap-northeast-1a"
PRIVATE_SUBNET_2_AVAILABILITY_ZONE = "ap-northeast-1c"
class CdkNetworkStack2(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# VPC
vpc = aws_ec2.Vpc(
self,
"vpc2",
cidr=VPC_CIDR,
subnet_configuration=[], # 明示的に空にしないとSubnetが自動で作られる
)
# Subnet
vpc_subnets = [
aws_ec2.Subnet(
self,
"Private1",
availability_zone=PRIVATE_SUBNET_1_AVAILABILITY_ZONE,
cidr_block=PRIVATE_SUBNET_1_CIDR,
vpc_id=vpc.vpc_id,
),
aws_ec2.Subnet(
self,
"Private2",
availability_zone=PRIVATE_SUBNET_2_AVAILABILITY_ZONE,
cidr_block=PRIVATE_SUBNET_2_CIDR,
vpc_id=vpc.vpc_id,
),
]
# (おまけ)VPCにLambdaを乗せる場合の設定例
aws_lambda.Function(
self,
"function2",
code=aws_lambda.Code.from_asset(
str(pathlib.Path(__file__).resolve().parent.joinpath("runtime"))
),
runtime=aws_lambda.Runtime.PYTHON_3_9,
handler="index.handler",
vpc=vpc,
vpc_subnets=aws_ec2.SubnetSelection(
subnets=vpc_subnets,
),
security_groups=[aws_ec2.SecurityGroup(self, "SG2", vpc=vpc)],
)
ポイント、、、というか注意事項はAZがハードコーディングなので、別のリージョンにデプロイできません!(このコードは東京リージョンでしか使えない)
ただそれ以外は普通に1つ1つ作っているので、何ができるかわかりやすいです。
まとめ
CDKでCIDRを任意の値で作りたい場合は、2つの方法がありました。
cfnの値を上書きする方法
はリージョンに関わらず動作するがSubnetがどう作られるか不透明で不安。
VPCとSubnetをバラで作る方法
はSubnetを細かく設定できるので安心できるが、AZをリージョン毎に手動で変える必要がある。
感想
使い分けですが、個人的には方法2
だけを使っていきたいなと思いました、、、方法1
は実環境で使う勇気がないです。。。
(2022/04/01)どっちも若干気持ち悪いのですが、方法1
の方が良いかなと思います。理由は方法1
の方はSelectedSubnets
型でSubnetを取得できるのですが、他のCDKコンストラクタで使い回す場合に、CfnSubnet
で持っているよりもSelectedSubnets
で持っていた方が使い勝手よいことが多いからです。
余談
cdk-diaを使って今回の2つの方法を図にしてみました!
Stack1が方法1
で、Stack2が方法2
です!
結構違いますね。そしてやっぱり方法1
はSubnetがいくつ作られるか分かりづらくて不安、、、