skip to Main Content

How to create a Network Load Balancer with one or more Elastic IP addresses in front of the Application Load Balancer using AWS CDK?

This should allow having fixed IP addresses for the load balancer. The article I need a static IP address for my Application Load Balancer. How can I register an Application Load Balancer behind a Network Load Balancer? recommends this approach.

The CDK API manual does not cover this use case. The class NetworkLoadBalancer (construct) lacks a definition of the SubnetMappings property. This looks like an issue in the documentation or the library.

The code should be preferable in TypeScript.

2

Answers


  1. Before answering the question about the static IP address for the load balancer, I want to suggest an alternative solution.

    Alternative solution – register ALB with Route53

    We can create a DNS record for the ALB in Route53 using CDK. The code is straightforward. Unfortunately, this solution does not work nicely if the DNS zone is not hosted by the AWS Route53.

    
      // Create or import your hosted zone.
      const hostedZone = new PublicHostedZone(stack, 'HostedZone', {
        zoneName: 'your.domain.name'
      })
    
      const loadBalancer = ... // Define your ALB.
    
      // Create a DNS record for your ALB instead of the static IP address.
      // eslint-disable-next-line no-new
      new ARecord(stack, 'WebServerARecord', {
        recordName: 'www',
        target: RecordTarget.fromAlias(new LoadBalancerTarget(loadBalancer)),
        zone: hostedZone
      })
    

    Issues with fixed address for the Network Load Balancer

    At the moment of writing, CDK constructs do not have the option to assign a static IP address to the Network Load Balancer. There are several open issues on GitHub: SubnetMappings support for LoadBalancer #7424, Add support for SubnetMapping to Network Load Balancer #9696.

    In a nutshell, using the SubnetMappings allows the load balancer to specify one or more Elastic IP addresses, but the NetworkLoadBalancer class does not have the SubnetMappings property.

    [Network Load Balancers] You can specify subnets from one or more
    Availability Zones. You can specify one Elastic IP address per subnet
    if you need static IP addresses for your internet-facing load
    balancer.

    Issue 7424 is more than two years old. Instead of waiting, we might want to go for a workaround if we must.

    Workaround to assign Elastic IP address to the NLB

    We will register an Elastic IP address, create a Network Load Balancer and assign it the IP address. I also add code to import VPC and create a simple Application Load Balancer for completeness. Please, check the code below and comments on the code. Note that the network load balancer and the Elastic IP start responding with some delay after the stack creation.

    import { App, Stack } from 'aws-cdk-lib'
    import { CfnEIP, Port, Vpc } from 'aws-cdk-lib/aws-ec2'
    import { ApplicationLoadBalancer, ListenerAction, NetworkLoadBalancer } from 'aws-cdk-lib/aws-elasticloadbalancingv2'
    import { AlbTarget } from 'aws-cdk-lib/aws-elasticloadbalancingv2-targets'
    import { env } from 'process'
    
    function createStack (scope, id, props) {
      const stack = new Stack(scope, id, props)
    
      // 1. Prepare required resources: import VPC and create simple ALB.
      const vpc = Vpc.fromLookup(stack, 'Vpc', { vpcName: 'BlogVpc' })
      const alb = new ApplicationLoadBalancer(stack, 'ALB', {
        internetFacing: false,
        port: 80,
        vpc
      })
      const albListener = alb.addListener('HttpListener', {
        defaultAction: ListenerAction.fixedResponse(200, {
          contentType: 'text/plain', messageBody: 'Hello World!'
        }),
        open: true,
        port: 80,
      })
    
      // 2. Create an Elastic IP address.
      const elasticIp = new CfnEIP(stack, 'ElasticIp', {domain: 'vpc'})
    
      // 3.1. Create a network load balancer.
      const nlb = new NetworkLoadBalancer(stack, 'NLB', {
        crossZoneEnabled: true,
        internetFacing: true,
        vpc
      })
    
      // 3.2. Add listener and target group to forward traffic to ALB.
      const nlbTargetGroup = nlb
        .addListener('AlbListener', {port: 80})
        .addTargets('AlbTargets', {targets: [new AlbTarget(alb, 80)], port: 80});
    
      // 3.3. We should create an ALB listener before creating the target group. This dependency is not added automatically.
      // https://github.com/aws/aws-cdk/issues/17208
      nlbTargetGroup.node.addDependency(albListener);
    
      // 3.4. Replace Subnets with SubnetMappings.
      // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticloadbalancingv2-loadbalancer-subnetmapping.html
      // We can have mappings for all public subnets. I did one for simplicity.
      const cfnNlb = nlb.node.defaultChild
      cfnNlb.addDeletionOverride('Properties.Subnets')
      cfnNlb.subnetMappings = [{
        allocationId: elasticIp.attrAllocationId,
        subnetId: vpc.publicSubnets[0].subnetId
      }]
    
      return stack
    }
    
    const app = new App()
    createStack(app, 'StaticIpStack', {
      env: { account: env.CDK_DEFAULT_ACCOUNT, region: env.CDK_DEFAULT_REGION }
    })
    

    The screenshot below shows the IP address association in the Console. You can add more associations to the cfnNlb.subnetMappings list. If you want to remove some, you must recreate the load balancer.

    enter image description here

    Login or Signup to reply.
  2. Instead of assigning a static IP directly to your ALB, you can link a AWS Global Accelerator to your ALB and it in turn will give you two static ip addresses.

    From this article:
    The global static IP addresses provisioned by Global Accelerator remain assigned to you for as long as your accelerator exists, even if you disable the accelerator and it no longer accepts or routes traffic

    Some code directly from the docs.

    declare const alb: elbv2.ApplicationLoadBalancer;
    declare const listener: globalaccelerator.Listener;
    
    listener.addEndpointGroup('Group', {
      endpoints: [
        new ga_endpoints.ApplicationLoadBalancerEndpoint(alb, {
          weight: 128,
          preserveClientIp: true,
        }),
      ],
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search