固定IPなNLBをAWS CDKで構築する

AWS CDKでElastic IPを使った固定IPなNLB環境を作ろうとしたところ、簡単には作れなかったのでTypeScriptのサンプルと解説を共有します。
2022.03.30

AWS CDKでElastic IPを使った固定IPなNLB環境を作る機会があったので、TypeScriptのサンプルプログラムを共有します。

簡単にできるかなー?と、思っていたんですが、現状(2022/03/30)、cdkでNLBを作成するNetworkLoadBalancerクラスに、 Elastic IPを付与するパラメーターがありません。 正攻法では、NLBにEIPを設定することができないです。

CloudFormationであれば、以下ブログのようにEIPを使った固定IPなNLBを構築できます。

CloudFormationでできることは、ローレベルConstructを使えばだいたいcdkでもできるので、それを試します。

前提条件

AWS CDKはv2を使用しています

$ cdk --version
2.18.0 (build 75c90fa)

構成図

ざっくりこんな感じの環境を作ります。

AWS CDKのサンプルプログラム

TypeScriptのサンプルプログラムは次の通りです。

#!/usr/bin/env node
import "source-map-support/register"
import { Construct } from 'constructs'
import { App, Stack, StackProps, Token } from 'aws-cdk-lib'
import * as ec2 from 'aws-cdk-lib/aws-ec2'
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2'
import * as elbv2Targtes from 'aws-cdk-lib/aws-elasticloadbalancingv2-targets'

class TestStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const vpc = new ec2.Vpc(this, 'VPC', {
      maxAzs: 2,
      natGateways: 0,
      subnetConfiguration: [{
        name: 'PublicSubnet',
        subnetType: ec2.SubnetType.PUBLIC,
      }],
    })

    // EC2作成
    const securityGroup = new ec2.SecurityGroup(this, 'SecurityGroup', {
      vpc,
      allowAllOutbound: true,
    })
    securityGroup.addIngressRule(
        ec2.Peer.anyIpv4(),
        ec2.Port.tcp(80),
    )
    const bastion = new ec2.BastionHostLinux(this, 'EC2', {
      vpc,
      securityGroup,
      instanceType: ec2.InstanceType.of(
          ec2.InstanceClass.BURSTABLE4_GRAVITON,
          ec2.InstanceSize.MICRO,
      ),
    })

    // NLB作成
    const nlb = new elbv2.NetworkLoadBalancer(this, 'NLB', {
      vpc,
      internetFacing: true,
      vpcSubnets: {
        subnetType: ec2.SubnetType.PUBLIC,
      },
    })
    const nlbListener = nlb.addListener(
        'NLBListener',
        {
          port: 80
        },
    )
    nlbListener.addTargets('NLBTargets', {
      protocol: elbv2.Protocol.TCP,
      port: 80,
      targets: [
          new elbv2Targtes.InstanceTarget(
              bastion.instance,
              80
          )
      ]
    })

    // NLBにEIPを付与する
    const cfnLoadBalancer = nlb.node.defaultChild as elbv2.CfnLoadBalancer
    cfnLoadBalancer.subnetMappings = vpc.publicSubnets.map((subnet, i) => {
      // パブリックサブネットのAZの数だけ、EIPを構築する
      const eip = new ec2.CfnEIP(this, `EIP${i}`,{
        domain: 'vpc',
      })
      nlb.node.addDependency(eip)

      // EIPを設定したNLBのSubnetMappingPropertyを作成する
      return {
        subnetId: subnet.subnetId,
        allocationId: Token.asString(eip.getAtt('AllocationId')),
      } as elbv2.CfnLoadBalancer.SubnetMappingProperty
    })
    // NLBのSubnet設定を削除する
    if (cfnLoadBalancer.subnetMappings !== undefined) {
      cfnLoadBalancer.subnets = undefined
    }
  }
}

const app = new App();
new TestStack(app, 'TestStack');

解説

CloudFormationでNLBにEIPを設定して構築する場合は SubnetMappings パラメーターを使って、EIPを設定します。

なので、cdkでも同じようにCloudFormationの SubnetMappings パラメーターを設定してやります。

66行目で、NLBのdefaultChildから、CloudFormationと1:1になるL1コンストラクタのCfnLoadBalancerを取り出しています。

67〜79行目で、VPCのパブリックサブネットのAZの数だけSubnetMappingPropertyを作って、subnetMappingsに設定しています。

81〜83行目で、subnetMappingsを設定したので、重複しているとエラーとなるsubnetsのパラメーターを undefined にして設定を削除しています。

この様に、subnetMappingsにElastic IPを設定して、デフォルトで設定されるsubnetsを無効にすることで、固定IPなNLBをAWS CDKで構築することができます。

参考URL