0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

CDK で 列挙型 SubnetType を利用する際に注意する点

Posted at

これは何?

AWS CDKのL3コンストラクトであるaws_ecs_patternsを試してみて、RDSも必要だなーと思ってRDSを作成した際、サブネット周りのエラー内容になります。
稼働プロジェクト上でのサブネットの追加や参照の仕方についても考察したいと思います。

事前準備

CDKプロジェクト上のlib/test.tsで、以下のようにApplicationLoadBalancedFargateServiceによってALB + ECS on Fargate環境を構築しました。
ECSクラスターをProtectedサブネット上に置き、Nat Gateway経由でインターネットにアウトバウンドします。

lib/test.ts
const vpc = new ec2.Vpc(this, `${props.resourceName}-vpc`, {
    vpcName: `${props.resourceName}-vpc-01`,
    ipAddresses: ec2.IpAddresses.cidr(props.vpcCidrValue),
    natGateways: 1,
});

vpc.addInterfaceEndpoint(`${props.resourceName}-edpif-dkr`, { service: ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER, });
vpc.addInterfaceEndpoint(`${props.resourceName}-edpif-ecr`, { service: ec2.InterfaceVpcEndpointAwsService.ECR, });
vpc.addGatewayEndpoint(`${props.resourceName}-edpgw-s3`, { service: ec2.GatewayVpcEndpointAwsService.S3, });

/* ---------- ECS Cluster ---------- */
const cluster = new ecs.Cluster(this, `${props.resourceName}-ecs-cluster`, {
    vpc: vpc,
});

/* ---------- ECR ---------- */
const ecrNginxRepo = ecr.Repository.fromRepositoryName(this, `${props.resourceName}-ecr-nginx`, `${props.resourceName}/nginx`);

/* ---------- ECS Patterns ---------- */
const ecsServiceAlbFargate = new ecs_patterns.ApplicationLoadBalancedFargateService(this, `${props.resourceName}-ecspatterns`, {
    cluster: cluster,
    taskImageOptions: {
        image: ecs.ContainerImage.fromEcrRepository(ecrNginxRepo),
    },
    taskSubnets: {
        subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
    },
    assignPublicIp: false,
    deploymentController: {
        type: ecs.DeploymentControllerType.ECS,
    }
});

上記をcdk deployコマンドによって環境構築した後、RDSへの接続も検証するため、以下のようにRDSを追加しました。

サブネットタイプはPRIVATE_ISOLATEDと指定しました。

lib/test.ts
// 追加
/* ---------- RDS ---------- */
const rdsInstance = new rds.DatabaseInstance(this, `${props.resourceName}-rds-mysql`, {
    vpc: vpc,
    engine: rds.DatabaseInstanceEngine.MARIADB,
    instanceType: new ec2.InstanceType("t3.micro"),           // db.t3.micro
    vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
    storageType: rds.StorageType.GP3,
    multiAz: true,
    allocatedStorage: 20,
    credentials: rds.Credentials.fromGeneratedSecret(
        'mariadb',
        { secretName: `${props.resourceName}/mariadb/pw`, }
    ),
    securityGroups: [sgRds],
});

しかし、cdk synthにて、以下のようなエラーに遭遇しました。

Error: There are no 'Isolated' subnet groups in this VPC. Available types: Deprecated_Private_NAT,Private,Deprecated_Private,Public
    :
    :

エラーの原因

RDS上インスタンスの作成場所を SubnetType.PRIVATE_ISOLATED として定義してPRIVATE_ISOLATEDの箇所にインスタンスを作成しようとしたが、そもそもSubnetType上でPRIVATE_ISOLATEDのサブネットが存在せず、エラーになったとのことです。

上記リンクの通り、NatGatewayを作成したことでサブネットタイプがPRIVATE_WITH_EGRESSと定義されていること & PRIVATE_ISOLATEDに該当するサブネットが環境上で存在しないことでこのようなエラーメッセージが発生しました。。。

確認方法

すでに作成してしまっている場合、VPCのPropertiesであるpublicSubnetsなどで、直接出力することもできます。

lib/test.js
console.log(`publicSubnets = ${vpc.publicSubnets}`);
console.log(`privateSubnets = ${vpc.privateSubnets}`);
console.log(`isolatedSubnets = ${vpc.isolatedSubnets}`);

上記を追加してcdk synthを実行することで, どのタイプのサブネットがあるのかを把握できます。
以下の実行結果は、IsolatedSubnetがない出力結果となります。

出力結果
publicSubnets = TestStack/test-vpc/PublicSubnet1,TestStack/test-vpc/PublicSubnet2,TestStack/test-vpc/PublicSubnet3
privateSubnets = TestStack/test-vpc/PrivateSubnet1,TestStack/test-vpc/PrivateSubnet2,TestStack/test-vpc/PrivateSubnet3
isolatedSubnets = 

また、新規でVPCを作成する場合は、subnetConfiguration を明示的に定義し、PRIVATE_WITH_EGRESS と PRIVATE_ISOLATEDの役割を持つサブネットを2つ作成するようにすればいいかと思います。

lib/test.ts
const vpc = new ec2.Vpc(this, `test-vpc`, {
    vpcName: `test-vpc-01`,
    ipAddresses: ec2.IpAddresses.cidr("10.0.0.0/16"),
    subnetConfiguration: [
        {
            cidrMask: 24,
            name: 'Public',
            subnetType: ec2.SubnetType.PUBLIC,
        },
        {
            cidrMask: 24,
            name: 'Protected',
            subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
        },
        {
            cidrMask: 24,
            name: 'Private',
            subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
        },
    ],
    natGateways: 2,
});

既存VPC上でのサブネットの追加

上記のやり方での欠点としては、既存のVPCでsubnetConfigurationを定義しようとすると場合によってはReplaceの対象になるため、プロジェクトで稼働中のリソースでのケースでは避けたいアプローチです。

VpcコンストラクトでてっきりaddSubnetのようなメソッドがあるかと思ったらありませんでした。

仕方がないため、以下のようにPrivateSubnetコンストラクトを追記して、IsolatedSubnetを作成します。

lib/test.ts
    :
const isolatedSubneta = new ec2.PrivateSubnet(this, `${props.resourceName}-subnet-isolated-a`, {
    vpcId: vpc.vpcId,
    availabilityZone: "ap-northeast-1a",
    cidrBlock: "10.0.200.0/24",
});
const isolatedSubnetc = new ec2.PrivateSubnet(this, `${props.resourceName}-subnet-isolated-c`, {
    vpcId: vpc.vpcId,
    availabilityZone: "ap-northeast-1c",
    cidrBlock: "10.0.210.0/24",
});
const isolatedSubnetd = new ec2.PrivateSubnet(this, `${props.resourceName}-subnet-isolated-d`, {
    vpcId: vpc.vpcId,
    availabilityZone: "ap-northeast-1d",
    cidrBlock: "10.0.220.0/24",
});

    :
    :

console.log(`publicSubnets = ${vpc.publicSubnets}`);
console.log(`privateSubnets = ${vpc.privateSubnets}`);
console.log(`isolatedSubnets = ${vpc.isolatedSubnets}`);

上記をcdk deployを実行した後、console.log部分の出力結果を見ましたが以下のように、IsolatedSubnetに該当するSubnetTypeはみあたりませんでした。。(何でや。。)

出力結果
publicSubnets = TestStack/test-vpc/PublicSubnet1,TestStack/test-vpc/PublicSubnet2,TestStack/test-vpc/PublicSubnet3
privateSubnets = TestStack/test-vpc/PrivateSubnet1,TestStack/test-vpc/PrivateSubnet2,TestStack/test-vpc/PrivateSubnet3
isolatedSubnets = 

仕方がないため、subnetTypeで指定せず、subnetsで直接サブネットIDを参照するように変更するとうまくRDSの作成をすることができました。

lib/test.ts
// 追加
/* ---------- RDS ---------- */
const rdsInstance = new rds.DatabaseInstance(this, `${props.resourceName}-rds-mysql`, {
    vpc: vpc,
    engine: rds.DatabaseInstanceEngine.MARIADB,
    instanceType: new ec2.InstanceType("t3.micro"),           // db.t3.micro
    // vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED }, // コメントアウト
    vpcSubnets: { subnets: [isolatedSubneta, isolatedSubnetc, isolatedSubnetd] },
    storageType: rds.StorageType.GP3,
    multiAz: true,
    allocatedStorage: 20,
    credentials: rds.Credentials.fromGeneratedSecret(
        'mariadb',
        { secretName: `${props.resourceName}/mariadb/pw`, }
    ),
    securityGroups: [sgRds],
});

まとめ

列挙型便利だなーと思ったのですが今回のような追加/削除に伴ったケースだと、参照時には列挙型より直接リソースを指定した方が、今回のような事象は回避しやすいのかなとおもいました。

(時間があったら列挙型で途中で追加したサブネットに対しても、参照できるようにしてみたい。。)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?