简体   繁体   中英

Using AWS CDK, How to connect an AWS Load balancer and an AWS Interface VPC Endpoint together

Background: We're using AWS Cloud Development Kit (CDK) 2.5.0.

Manually using the AWS Console and hard-coded IP addresses, Route 53 to an ALB (Application Load Balancer) to a private Interface VPC Endpoint to a private REST API-Gateway (and so on..) works. See image below.

Code: We're trying to code this manual solution via CDK, but are stuck on how to get and use the IP addresses or in some way hook up the load balancer to the Interface VPC Endpoint. (Endpoint has 3 IP addresses, one per availability zone in the region.)

The ALB needs a Target Group which targets the IP addresses of the Interface VPC Endpoint. (Using an "instance" approach instead of IP addresses, we tried using InstanceIdTarget with the endpoint's vpcEndpointId , but that failed. We got the error Instance ID 'vpce-WITHWHATEVERWASHERE' is not valid )

Using CDK, we created the following (among other things) using the aws_elasticloadbalancingv2 module:

  • ApplicationLoadBalancer (ALB)
  • ApplicationTargetGroup (ATG) aka Target Group

We were hopeful about aws_elasticloadbalancingv2_targets similar to aws_route53_targets , but no luck. We know the targets property of the ApplicationTargetGroup takes an array of IApplicationLoadBalancerTarget objects, but that's it.

  :
import { aws_ec2 as ec2 } from 'aws-cdk-lib';
  :
import { aws_elasticloadbalancingv2 as loadbalancing } from 'aws-cdk-lib';

// endpointSG, loadBalancerSG, vpc, ... are defined up higher
        const endpoint = new ec2.InterfaceVpcEndpoint(this, `ABCEndpoint`, {
            service: {
                name: `com.amazonaws.us-east-1.execute-api`,
                port: 443
            },
            vpc,
            securityGroups: [endpointSG],
            privateDnsEnabled: false,
            subnets: { subnetGroupName: "Private" }
        });


        const loadBalancer = new loadbalancing.ApplicationLoadBalancer(this, 'abc-${config.LEVEL}-load-balancer', {
            vpc: vpc,
            vpcSubnets: { subnetGroupName: "Private" },
            internetFacing: false,
            securityGroup: loadBalancerSG
        });

        const listenerCertificate = loadbalancing.ListenerCertificate.fromArn(config.ARNS.CERTIFICATE)
        const listener = loadBalancer.addListener('listener', {
            port: 443,
            certificates: [ listenerCertificate ]
        });

        let applicationTargetGroup = new loadbalancing.ApplicationTargetGroup(this, 'abc-${config.LEVEL}-target-group', {
            port: 443,
            vpc: vpc,
            // targets: [ HELP ], - how to get the IApplicationLoadBalancerTarget objects?  
        })
        listener.addTargetGroups( 'abc-listener-forward-to-target-groups', { 
            targetGroups: [applicationTargetGroup]
        } );

As you can see above, we added a listener to the ALB. We added the Target Group to the listener.

Some of the resources we used:

In case visualizing the set up via an image helps, here's a close approximation of what we're going for. 在此处输入图像描述

Any help populating that targets property of the ApplicationTargetGroup with IApplicationLoadBalancerTarget objects is appreciated. Thanks!

https://aws.amazon.com/blogs/networking-and-content-delivery/accessing-an-aws-api-gateway-via-static-ip-addresses-provided-by-aws-global-accelerator/

This blog shows how to configure the architecture given in the question using AWS console (just disable the global accelerator option). The key takeaway is that the application load balancer uses target type IP and resolves the VPC endpoint domain name manually in step 2. The other two options, instance (target is an EC2 instances) and lambda (target is an AWS Lambda function) cannot be used.

The ec2.InterfaceVpcEndpoint construct has no output which directly gives an IP address. The underlying CloudFormation resource also does not support it. Instead, you will have to use the vpcEndpointDnsEntries property of ec2.InterfaceVpcEndpoint and resolve the domain names to IP addresses in your code (the console configuration also required the same domain name resolution). You can use an IpTarget object in your ApplicationTargetGroup .

At this point, you will run into one final roadblock due to how CDK works under the hood. 文档中的 cdk 结构 If you have all your resources defined in one CDK application, the value for each parameter (or a reference to the value using an underlying CloudFormation functions like Ref, GetAtt, etc.) needs to be available before the synthesize step, since that's when all templates are generated. AWS CDK uses tokens for this purpose, which during synthesis resolve to values such as {'Fn::GetAtt': ['EndpointResourceLogicalName', 'DnsEntries'] . However since we need the actual value of the DNS entry to be able to resolve it, the token value won't be useful.

One way to fix this issue is to have two completely independent CDK applications structured this way:

  • Application A with VPC and interface endpoint. Define the vpcEndpointDnsEntries and VPC-ID as outputs using CfnOutput .
  • Application B with the rest of the resources. You will have to write code to read outputs of the CloudFormation stack created by Application A. You can use Fn.importValue for VPC ID, but you cannot use it for the DnsEntries output since it would again just resolve to a Fn::ImportValue based token. You need to read the actual value of the stack output, using the AWS SDK or some other option. Once you have the domain name, you can resolve it in your typescript code (I am not very familiar with typescript, this might require a third party library).

Image credits:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM