How to configure ELB for two different ports in CDK? - amazon-web-services

I am having an elastic load balancer serving traffic to an EC2 instance. I have an application running on port 443 and it runs just fine.
Now I want to run another application on the EC2 instance on port 444. I want to be able to run the first application by hitting port 443 and the second application by hitting port 444.
Somehow I cant manage to add port 444 to the load balancer in CDK. I am doing something like this.
const appLoadbalancer = new elbv2.ApplicationLoadBalancer(this, `${props.type}AppLoadBalancer`, {
vpc: vpc,
vpcSubnets: subnets,
internetFacing: true,
});
const httpsListener = appLoadbalancer.addListener(`${props.type}HTTPSListener`, {
port: 443,
open: true,
certificates: [props.certificate]
});
httpsListener.addTargets(`${props.type}HTTPSTarget`, {
port: 443,
targets: [autoscalingGroup],
healthCheck: {
enabled: true,
healthyHttpCodes: "200,302"
}
});
const httpsListener2 = appLoadbalancer.addListener(`${props.type}HTTPSListener2`, {
port: 444,
protocol: elbv2.ApplicationProtocol.HTTPS,
open: true,
certificates: [props.certificate]
});
httpsListener2.addTargets(`${props.type}HTTPSTarget2`, {
port: 444,
protocol: elbv2.ApplicationProtocol.HTTPS,
targets: [autoscalingGroup],
healthCheck: {
enabled: true,
healthyHttpCodes: "200,302"
}
});
Everything is working just fine if it set it up only for port 443. But When I try the above i get something like:
Error: Cannot add AutoScalingGroup to 2nd Target Group
I dont know what is meant by this and how to fix it in cdk...

I ended up with a solution like this:
const appLoadbalancer = new elbv2.ApplicationLoadBalancer(this, `${props.type}AppLoadBalancer`, {
vpc: vpc,
vpcSubnets: subnet,
internetFacing: true,
});
const tg1 = new elbv2.ApplicationTargetGroup(this, "tg1", {
vpc: vpc,
protocol: elbv2.ApplicationProtocol.HTTPS})
const tg2 = new elbv2.ApplicationTargetGroup(this, "tg2", {vpc: vpc,
protocol: elbv2.ApplicationProtocol.HTTPS, port: 444})
const httpsListener = appLoadbalancer.addListener(`${props.type}HTTPSListener`, {
port: 443,
protocol: elbv2.ApplicationProtocol.HTTPS,
open: true,
certificates: [props.certificate]
});
httpsListener.addTargetGroups("RestTarget", {
targetGroups: [tg1]
});
const httpsListener2 = appLoadbalancer.addListener(`${props.type}HTTPSListener2`, {
port: 444,
protocol: elbv2.ApplicationProtocol.HTTPS,
open: true,
certificates: [props.certificate]
});
httpsListener2.addTargetGroups("RestTarget", {
targetGroups: [tg2]
});
const ServiceAsg = autoscalingGroup.node.defaultChild as autoscaling.CfnAutoScalingGroup
ServiceAsg.targetGroupArns = [tg1.targetGroupArn, tg2.targetGroupArn]

Related

Add Cloud Map service to Application Load Balancer created by ApplicationLoadBalancedFargateService

I've got the following Fargate service created by an ecs pattern. The CloudMap I create here only points to the underlying task which is a private IP and runs on port 8080 (Tomcat). The ALB forwards properly from 80->8080. How can I get the DNS to properly route to the task? Can I get the DNS service to route directly to the ALB?
const service = new ecs_patterns.ApplicationLoadBalancedFargateService(this, 'MyAppWebstartFargateService', {
serviceName: "myapp-service",
cluster: cluster,
cpu: 512,
memoryLimitMiB: 2048,
cloudMapOptions: {
name: "myapp",
containerPort: 8080,
cloudMapNamespace: namespace,
dnsRecordType: svc_dsc.DnsRecordType.A,
dnsTtl: Duration.seconds(300),
},
desiredCount: 1,
publicLoadBalancer: false,
securityGroups: [sg],
listenerPort: 80,
openListener: true,
healthCheckGracePeriod: Duration.seconds(300),
targetProtocol: elbv2.ApplicationProtocol.HTTP,
protocol: elbv2.ApplicationProtocol.HTTP,
enableExecuteCommand: true,
taskImageOptions: {
containerName: "myapp-container",
containerPort: 8080,
enableLogging: true,
image: ecs.ContainerImage.fromEcrRepository(repository, "latest"),
},
});
I figured it out! I needed to call registerLoadBalancer on the Cloud Map service and give it the resulting LB from the Fargate pattern. Hope this helps someone down the road b/c I could not find any solution to this exact use case.
const namespace = svc_dsc.PrivateDnsNamespace.fromPrivateDnsNamespaceAttributes(this, "MyAppCloudMapNamespace", {
namespaceArn: "*****************",
namespaceId: "999999999999999",
namespaceName: "mydomain.com"
});
const mapService = new svc_dsc.Service(this, 'MyAppCloudMapService', {
namespace: namespace,
dnsRecordType: svc_dsc.DnsRecordType.A,
dnsTtl: Duration.seconds(300),
name: "myapp",
routingPolicy: svc_dsc.RoutingPolicy.WEIGHTED,
loadBalancer: true // Important! If you choose WEIGHTED but don't set this, the routing policy will default to MULTIVALUE instead
});
const service = new ecs_patterns.ApplicationLoadBalancedFargateService(this, 'MyAppWebstartFargateService', {
serviceName: "myapp-service",
cluster: cluster,
cpu: 512,
memoryLimitMiB: 2048,
desiredCount: 1,
publicLoadBalancer: false,
securityGroups: [sg],
listenerPort: 80,
openListener: true,
healthCheckGracePeriod: Duration.seconds(300),
targetProtocol: elbv2.ApplicationProtocol.HTTP,
protocol: elbv2.ApplicationProtocol.HTTP,
enableExecuteCommand: true,
taskImageOptions: {
containerName: "myapp-container",
containerPort: 8080,
enableLogging: true,
image: ecs.ContainerImage.fromEcrRepository(repository, "latest"),
},
});
mapService.registerLoadBalancer("MyAppLoadBalancer", service.loadBalancer);

Adding an ApplicationListenerRule to an existing ApplicationListener gives error 'A listener already exists on this port for this load balancer'

I want to add a rule to an existing load balancer listener which is listening on PORT:80. I'm also creating a new target group to attach to the listener rule action.
in CDK i used fromLookup and grabbed the listener from ARN
const appListener = elbv2.ApplicationListener.fromLookup(this, `ALBListener-${props.stage}`, { listenerArn });
const applicationListenerRule = new elbv2.ApplicationListenerRule(this, `BlablaSyncRule-${props.stage}`, {
listener: appListener,
priority: 1000, //do not hardcode
conditions: [elbv2.ListenerCondition.pathPatterns(['/socket.io*'])],
action: elbv2.ListenerAction.forward([targetGroup])
});
when i do cdk synth i can see this included in the generated Cloudformation
ALBPublicListener9C613A95:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
Properties:
DefaultActions:
- TargetGroupArn:
Ref: ALBPublicListenerECSGroup7E4FFE32
Type: forward
LoadBalancerArn: >-
arn:aws:elasticloadbalancing:eu-central-1....
Port: 80
Protocol: HTTP
Metadata:
'aws:cdk:path': SocketFargateStack/ALB/PublicListener/Resource
When I try to deploy I get the error A listener already exists on this port for this load balancer so is it trying to create a new listener on PORT 80. If so why. Is there a way to add a rule to an existing listener using CDK

Using existing loadbalancer for adding targetgroup

I want to make the targetGroup by cdk and use the ALB already existed.
This error happens.
Error: .listeners can only be accessed if the class was constructed as an owned, not looked up, load balancer
at LookedUpApplicationLoadBalancer.get listeners [as listeners]
I can't access the listeners of ALB
What I want to do is use one load balancer for two ECS and two domain.
www.exampleA.com -> port 80 -> ALB -> fargate A
www.exampleB.com -> port 80 -> ALB -> fargate B
These are my code below.
const lb = elb.ApplicationLoadBalancer.fromLookup(this, 'ALB', {
loadBalancerArn: 'arn:aws:elasticloadbalancing:ap-northeast-1:678100228232:loadbalancer/app/app-load-balancer/1a97159fcaf4d6c0',
});
const listener = lb.listeners[0];
const targetGroup = listener.addTargets("ECS", {
protocol: elb.ApplicationProtocol.HTTP,
port: 80,
targets: [ecsAdminService]
});
targetGroup.configureHealthCheck({
path: "/",
port: "8080"
})
Or if it is impossible, I want to make targetgroup without ALB
(then I can attach targetgroup to ALB manually)
So, I tried this
const targetGroup = new elb.ApplicationTargetGroup(this,"ECS", {
protocol: elb.ApplicationProtocol.HTTP,
port: 80,
targets: [ecsAdminService],
vpc: cluster.vpc,
});
targetGroup.configureHealthCheck({
path: "/",
port: "8080"
})
However this error comes
"Invalid request provided: UpdateService error: The target group with targetGroupArn arn:aws:elasticloadbalancing:ap-northeast-1:678100228133:targetgroup/CdkTr-ECSD2-S1ROICFY9661/f1f3e3b280c2a008 does not have an associated load balancer
I can use existing ALB like this below
Actually, what I need is only securityGroup of ALB and listener.
const securityGroup = ec2.SecurityGroup.fromSecurityGroupId(this, "MyAlbSecGroup", "sg-0ea7a62badcc673a3")
const listenerArn = "arn:aws:elasticloadbalancing:ap-northeast-1:67810022242:listener/app/my-alb-id-listener/1a97159fcaf4d6c0/09a32815415beae6";
const existingListener = elb.ApplicationListener.fromApplicationListenerAttributes(this, "SharedListener", {
listenerArn,
securityGroup
});
const targetGroup = new elb.ApplicationTargetGroup(this,"ECS", {
port: 80,
targets: [ecsAdminService],
vpc: cluster.vpc,
});
existingListener.addTargetGroups("tg",{
priority:1,
conditions:[
elb.ListenerCondition.hostHeaders(['my.example.com'])
],
targetGroups:[targetGroup]
})

AWS-CDK ECS Fargate LoadBalancer listening on port 80 with target group mapping to container port

I've been trying to build a simple ECS Fargate infrastructure using the CDK to learn ECS and its components. The web server exposes port 8081, service SG ingress rule allows all TCP from ALB SG, ALB SG allows connection on PORT 80 - later changed to all TCP for testing. ALB has a listener on port 80 – later also on port 8081 – which forwards traffic to the Target Group on HTTP:8081, fargate tasks are automatically registered, health checks are passing.
Everything seems to be set up the right way, however, when going to [alb-dns].com I get nothing – not even a 504 – DNS just cannot be found. But when I go with [alb-dns].com:8081 it serves me the "hello world" from the webserver. This works regardless of whether my Target Group is on HTTP:8081 or HTTP:80.
I tried an old github/stackoverflow solution of passing a listener as port mapping to a container, but that doesn't work anymore – type mismatch.
What am I missing here?
Code:
this.cluster = new Cluster(this, 'exanubes-cluster', {
vpc: props.vpc,
clusterName: 'exanubes-cluster',
containerInsights: true,
enableFargateCapacityProviders: true,
})
const albSg = new SecurityGroup(this, 'SecurityGroupLoadBalancer', {
vpc: props.vpc,
allowAllOutbound: true
})
albSg.addIngressRule(Peer.anyIpv4(), Port.allTcp())
const alb = new ApplicationLoadBalancer(this, 'alb', {
vpc: props.vpc,
loadBalancerName: 'exanubes-ecs-application-LB',
internetFacing: true,
securityGroup: albSg,
http2Enabled: false,
deletionProtection: false
})
const listener = alb.addListener('http listener', {
port: 80,
open: true
})
const targetGroup = listener.addTargets('tcp-listener-target', {
targetGroupName: 'tcp-target-ecs-service',
protocol: ApplicationProtocol.HTTP,
protocolVersion: ApplicationProtocolVersion.HTTP1,
port: CONTAINER_PORT
})
const taskDefinition = new FargateTaskDefinition(this, 'fargate-task-definition');
taskDefinition.addContainer('web-server', {
image: EcrImage.fromEcrRepository(props.repository),
}).addPortMappings({
containerPort: CONTAINER_PORT
})
const securityGroup = new SecurityGroup(this, 'http-sg', {
vpc: props.vpc,
})
securityGroup.addIngressRule(Peer.securityGroupId(albSg.securityGroupId), Port.allTcp(), 'Allow inbound connections from ALB')
const fargateService = new FargateService(this, 'fargate-service', {
cluster: this.cluster,
assignPublicIp: true,
taskDefinition,
capacityProviderStrategies: [
{
capacityProvider: "FARGATE_SPOT",
weight: 0,
},
{
capacityProvider: "FARGATE",
weight: 1
}
],
securityGroups: [securityGroup],
})
targetGroup.addTarget(fargateService)
PS: I know of ApplicationLoadBalancedFargateService but I wanted to build it myself.
I think this example in the source code .../aws_cdk/aws_ecs/__init__.py should help
Example::
# cluster: ecs.Cluster
# task_definition: ecs.TaskDefinition
# vpc: ec2.Vpc
service = ecs.FargateService(self, "Service", cluster=cluster, task_definition=task_definition)
lb = elbv2.ApplicationLoadBalancer(self, "LB", vpc=vpc, internet_facing=True)
listener = lb.add_listener("Listener", port=80)
service.register_load_balancer_targets(
container_name="web",
container_port=80,
new_target_group_id="ECS",
listener=ecs.ListenerConfig.application_listener(listener,
protocol=elbv2.ApplicationProtocol.HTTPS
)
)
Edit: the above did not work for me, but this did
listener.add_targets('tcp-target-group',
protocol=ApplicationProtocol.HTTP,
target_group_name="my-target",
targets=[service.load_balancer_target(
container_name=container.container_name,
container_port=CONTAINER_PORT
)],
)

How to set "targets" in creating ALB in CDK?

Current Code
import * as elasticloadbalancingv2 from "#aws-cdk/aws-elasticloadbalancingv2";
.
.
.
target: ec2.Instance
const targetGroups = new elasticloadbalancingv2.ApplicationTargetGroup(this, "TargetGroup", {
healthCheck: {
path: "/",
port: "80",
protocol: elasticloadbalancingv2.Protocol.HTTP
},
port: 80,
protocol: elasticloadbalancingv2.ApplicationProtocol.HTTP,
targets: [new elasticloadbalancingv2.IpTarget(target.instancePrivateIp)],
targetType: elasticloadbalancingv2.TargetType.IP,
vpc,
})
Problem
This code is working but IpTarget is deprecated.
I cannot understand how to replace it.
How do you make it work without using any deprecated class?
Fixed Code
import * as elasticloadbalancingv2 from "#aws-cdk/aws-elasticloadbalancingv2";
import * as elasticloadbalancingv2targets from "#aws-cdk/aws-elasticloadbalancingv2-targets";
.
.
.
const pgAdminTarget: elasticloadbalancingv2targets.InstanceIdTarget[] = [];
pgAdminTarget.push(new elasticloadbalancingv2targets.InstanceIdTarget(props.instance.instanceId, 80));
const pgAdminTg = new elasticloadbalancingv2.ApplicationTargetGroup(this, "TargetGroup", {
healthCheck: {
path: "/health.html",
port: "80",
protocol: elasticloadbalancingv2.Protocol.HTTP
},
port: 80,
protocol: elasticloadbalancingv2.ApplicationProtocol.HTTP,
targetType: elasticloadbalancingv2.TargetType.INSTANCE,
targets: [pgAdminTarget],
vpc,
})
const alb = new elasticloadbalancingv2.ApplicationLoadBalancer(this, "ALB", {
vpc,
internetFacing: true,
loadBalancerName: "ec2-alb",
vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
});
alb.addListener("lister", {
certificates: [certificate],
defaultTargetGroups: [pgAdminTg],
port: 443,
protocol: elasticloadbalancingv2.ApplicationProtocol.HTTPS,
});
New Error
Property 'attachToApplicationTargetGroup' is missing in type 'InstanceIdTarget[]' but required in type 'IApplicationLoadBalancerTarget'.
targets: [pgAdminTarget],
node_modules/#aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.d.ts:291:5
291 attachToApplicationTargetGroup(targetGroup: IApplicationTargetGroup): LoadBalancerTargetProps;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'attachToApplicationTargetGroup' is declared here.
According to the document, attachToApplicationTargetGroup is called automatically when you add the target to a load balancer.
I guess I don’t need to call this but error says that attachToApplicationTargetGroup is missing in type InstanceIdTarget[].
What is the problem?
You need to use the aws-cdk.aws-elasticloadbalancingv2-targets package.
It also has an IpTarget construct.
You can also just use an instance target and point it to your instance:
new InstanceTarget(instance: Instance, port?: number)
https://docs.aws.amazon.com/cdk/api/latest/docs/#aws-cdk_aws-elasticloadbalancingv2-targets.IpTarget.html
https://docs.aws.amazon.com/cdk/api/latest/docs/#aws-cdk_aws-elasticloadbalancingv2-targets.InstanceTarget.html
EDIT: Regarding your edit, pass the array to targets and not an array with an array inside:
targets: [pgAdminTarget], should be targets: pgAdminTarget