AWS CDKで、NATゲートウェイの数を抑制する

AWS CDKでVPCを作成する際にsubnetTypeをPRIVATEにしてしまうとNAT ゲートウェイが作成されますよね。
NAT ゲートウェイは必要だけどサブネット分だけ作成されると、コストが増えたりするので数を指定したい時ありますよね。
この記事では、AWS CDKで作成されるNAT ゲートウェイの数を抑制する方法を記載します。

設定すること

Vpcインスタンスの定義時に、プロパティにnatGatewaysを与えるだけです。
具体的にはこのように記載するだけです。

import cdk = require('@aws-cdk/core')
import { Vpc, SubnetType } from '@aws-cdk/aws-ec2'

export class AwesomeStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props)
    const vpc = new Vpc(this, 'awesome-vpc', {
      cidr: '192.168.0.0/24',
      natGateways: 1,
      subnetConfiguration: [
        {
          cidrMask: 28,
          name: 'public',
          subnetType: SubnetType.PUBLIC,
        },
        {
          cidrMask: 28,
          name: 'private',
          subnetType: SubnetType.PRIVATE,
        },
      ]
    })
  }
}
const app = new cdk.App()
new AwesomeStack(app, 'AwesomeStack')
app.synth()

natGatewaysを指定しないとsubnetTypeがPUBLICなサブネットと、PRIVATEなサブネットがそれぞれ2つ作成されます。
そして、NAT ゲートウェイが2つ作成されます。

natGatewaysを指定して必要個数を指定することで、不要なNAT ゲートウェイの作成を防ぐことができます。

エラーが出るパターン

NAT ゲートウェイが0個の状態で、subnetTypeがPRIVATEなものがあると作成に失敗します。
具体的には下記の様に設定した場合です。

import cdk = require('@aws-cdk/core')
import { Vpc, SubnetType } from '@aws-cdk/aws-ec2'

export class AwesomeStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props)
    const vpc = new Vpc(this, 'awesome-vpc', {
      cidr: '192.168.0.0/24',
      natGateways: 0,
      subnetConfiguration: [
        {
          cidrMask: 28,
          name: 'public',
          subnetType: SubnetType.PUBLIC,
        },
        {
          cidrMask: 28,
          name: 'private',
          subnetType: SubnetType.PRIVATE,
        },
      ]
    })
  }
}
const app = new cdk.App()
new AwesomeStack(app, 'AwesomeStack')
app.synth()

実際にこのコードでデプロイを実行してみましょう。

$ cdk deploy

AwesomeStack: deploying...
AwesomeStack: creating CloudFormation changeset...
  0/21 | 6:00:52 PM | CREATE_IN_PROGRESS   | AWS::CDK::Metadata                    | CDKMetadata 
  8/21 | 6:01:15 PM | CREATE_FAILED        | AWS::EC2::Route                       | awesome-vpc/privateSubnet1/DefaultRoute (awesomevpcprivateSubnet1DefaultRouteAB8AC2F7) Exactly one of [EgressOnlyInternetGatewayId, GatewayId, InstanceId, NetworkInterfaceId, TransitGatewayId, NatGatewayId, VpcPeeringConnectionId] must be specified and not empty
  
❌  AwesomeStack failed: Error: The stack named AwesomeStack failed creation, it may need to be manually deleted from the AWS console: ROLLBACK_COMPLETE
The stack named AwesomeStack failed creation, it may need to be manually deleted from the AWS console: ROLLBACK_COMPLETE

なぜデプロイに失敗するのでしょうか。
AWS CDKでsubnetTypeがPRIVATEの場合、作成されるサブネットはNATを通じて外部と通信できる必要があります。
なので、サブネットからNAT ゲートウェイにルーティングをしようとした結果、存在しないためエラーが出てしまいます。
外部との通信が一切必要ない場合はISOLATEDを使用する必要があります。
それではNAT ゲートウェイなしでVPCをAWS CDKで作成しましょう。 具体的には下記のようにコードを書きます。

import cdk = require('@aws-cdk/core')
import { Vpc, SubnetType } from '@aws-cdk/aws-ec2'

export class AwesomeStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props)
    const vpc = new Vpc(this, 'awesome-vpc', {
      cidr: '192.168.0.0/24',
      subnetConfiguration: [
        {
          cidrMask: 28,
          name: 'public',
          subnetType: SubnetType.PUBLIC,
        },
        {
          cidrMask: 28,
          name: 'isolated',
          subnetType: SubnetType.ISOLATED,
        },
      ]
    })
  }
}
const app = new cdk.App()
new AwesomeStack(app, 'AwesomeStack')
app.synth()

subnetTypeをISOLATEDに変更しています。
NAT ゲートウェイが不要な場合(PRIVATEがない場合)にはそもそも作成しないのでnatGatewaysを指定する必要はありません。

さいごに

AWS CDKは便利でよしなにやってくれる分、しっかり仕様を読まないと動作がわからなくなることがあります。
しっかりとドキュメントを読んでより便利に使えればと思っています。