-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(ecs): allow load balancers to connect to any mapped port #3891
Conversation
* Proof of concept for explicit host port override when linking an elbv2.ITargetGroup to an ECS cluster/task * Added tests for new functionality, updated comments
Pull Request Checklist
|
Pull Request Checklist
|
Well, the documentation looks to be slightly off. It says the "listener"s port, but it's actually the port on which the instances in the target group are listening. I'd prefer using I've got a question for you in the issue though: can you tell me why switching the order of your containers around doesn't work for you? |
I responded to the question in the issue. I can also quickly convert this code to use If you believe the correctness of using the port as designed moving forward outweighs the impact of having backward incompatibility and breaking installs in the wild, I will consolidate. |
Updating the meaning of "port" in ITargetGroup to be the attachment to host instead of the load balancer port causes a need to rebuild load balancers due to a property change in the output json (for example group |
For sake of discussion, this is what the differences would look like if the meaning of |
public _findPortMapping(hostPort: number, protocol: Protocol): PortMapping | undefined { | ||
for (const portMapping of this.portMappings) { | ||
const p = portMapping.protocol || Protocol.TCP; | ||
const h = portMapping.hostPort || portMapping.containerPort; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this fail if there are two containers in a service and both container expose port 80 but I want the first container to be attached to a target group and the second container to be attached to another target group? Unless I change the second container port to other than 80 or assign a host port for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is reliant on having a distinct host port that can be targeted; my expectation while solving this problem was that the host must be able to respond to TCP traffic on distinct ports per requested listener.
I am not familiar enough with the fabric of ECS to know how things like dynamic port mapping are implemented, but I don't see how two incoming TCP connections to the same logical port could reach distinct containers. Specification of container names that implicitly map to host ports is another way to solve the same problem, but I didn't think that appropriate for the approach of extending ITargetGroup (the approach in the proposal doc is more loosely coupled and potentially better IMO).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ECS has three type of network mode: if it is aws_vpc or host, then the host port number should be the same with container port number. However, if it is bridge mode (default for EC2 tasks), you can specify a non-reserved host port for your container port mapping (or leave it undefined or 0), so that possibly two containers can have the same port number (mapped to different host ports). However, if this is the case and host port is not defined, it is hard to track through the host port, which will be any of the port range from 49153 through 65535.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation :)
This is probably moving off topic for the PR, but given that the host and container ports must match for aws_vpc or host mode, what happens if you attempt to use two containers with ports that collide in those modes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It fails when you do cdk deploy
.
* | ||
* @default Determined from target | ||
*/ | ||
readonly targetPort?: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this property is only used for ECS target, IMHO this might cause confusion to other non-ECS users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a fair point; as noted in the discussion above I would have greatly preferred having the port
of the target group refer to the ingress port of the connection in all cases, but I didn't want to break compatibility.
@@ -501,6 +502,13 @@ export interface AddApplicationTargetsProps extends AddRuleProps { | |||
*/ | |||
readonly port?: number; | |||
|
|||
/** | |||
* The port used to attach to targets. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, it seems like targetPort
can be hostport or container port, which might lead to confusion. It would be better to only specify container port number (since users might care more about the container port when building microservices).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TargetPort must strictly be the ingress (host) port in this design since it must be accessible from outside the container by a non-ECS resource; as you noted above the container port is potentially not unique in a multi-container task definition. The mapping to host port allows resolution of these potential collisions. If the user specified the container port here the underlying system would need to convert it to a host port anyway.
Sorry for taking so long to come back to this. After loading more context about the CloudFormation model into my brain, I'm confused why this needs to involve changes to the ELB package at all. Isn't the goal to have more control over the following property (at the CloudFormation level):
Seems like it would only necessitate changes in the ECS package, so that the correct port can be returned here:
I think a more generic design that @iamhopaul123 is proposing in #3922 will be a better way to achieve the same. |
Thanks so much for taking the time to contribute to the AWS CDK ❤️ We will shortly assign someone to review this pull request and help get it
|
If it's okay with you @karlpatr I'm going to close this PR. We'll wait for @iamhopaul123 to finish their work in #3922. Thanks a lot for raising this issue and contributing to the discussion! |
closes #3823
This patch allows explicit override of the existing default container selection when attaching a load balancer to an ecs task. By adding an optional
targetPort
to the properties ofelbv2.AddApplicationTargetsProps
andelbv2.AddNetworkTargetsProps
and then exposing that property via theelbv2. ITargetGroup
, the user can now explicitly declare that they want to connect to any mapped port inside a task.When an
ecs.BaseService
is asked to create an attachment, it now loops over allecs.PortMappings
using a pair of internal helper functions to resolve the particular container name and host port to specify for the generated load balancer declaration. If the Load Balancer has a security group, the target port is also used to override the security ingress necessary to connect it to the Service. If notargetPort
has been declared the previous functionality is used for backward compatibility. If atargetPort
that isn't used by a container has been declared the system will throw an error saying load balancers can't be connected to unmapped ports.I considered using the existing
port
on theelbv2.ITargetGroup
to perform the mapping, but the comment declares it is the load balancer's listening port and that is how it was used in unit tests. Instead of making a breaking change by adding new meaning to that field I elected to create an explicit and optional one. I chose the nametargetPort
because that is the name used in the pattern module for load balanced ECS prefabs.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license