Skip to content
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 balancing to any container and port of service #4107

Merged
merged 3 commits into from
Sep 19, 2019

Conversation

iamhopaul123
Copy link
Contributor

@iamhopaul123 iamhopaul123 commented Sep 16, 2019

Add a loadBalancerTarget() method to ECS's Service, which allows load balancing
to containers other than the default container and to ports beside the first mapped port.

Services can be registered to multiple ELBv2 target groups at the same time if necessary.


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

@mergify
Copy link
Contributor

mergify bot commented Sep 16, 2019

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
merged. In the meantime, please take a minute to make sure you follow this
checklist
:

  • PR title type(scope): text
    • type: fix, feat, refactor go into CHANGELOG, chore is hidden
    • scope: name of module without aws- or cdk- prefix or postfix (e.g. s3 instead of aws-s3-deployment)
    • text: use all lower-case, do not end with a period, do not include issue refs
  • PR Description
    • Rationale: describe rationale of change and approach taken
    • Issues: indicate issues fixed via: fixes #xxx or closes #xxx
    • Breaking?: last paragraph: BREAKING CHANGE: <describe what changed + link for details>
  • Testing
    • Unit test added. Prefer to add a new test rather than modify existing tests
    • CLI or init templates change? Re-run/add CLI integration tests
  • Documentation
    • README: update module README to describe new features
    • API docs: public APIs must be documented. Copy from official AWS docs when possible
    • Design: for significant features, follow design process

@iamhopaul123
Copy link
Contributor Author

@karlpatr please review.

@iamhopaul123 iamhopaul123 added the pr/do-not-merge This PR should not be merged at this time. label Sep 16, 2019
@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

Copy link

@karlpatr karlpatr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is much cleaner than my own first attempt, which I'll be closing in favor of this code. I look forward to seeing it published so I can adopt my project code.

packages/@aws-cdk/aws-ecs/lib/base/base-service.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/aws-ecs/lib/base/base-service.ts Outdated Show resolved Hide resolved
@@ -232,6 +295,66 @@ export abstract class BaseService extends Resource
return ret;
}

public loadBalancerTarget(options: LoadBalancerTargetOptions): IEcsLoadBalancerTarget {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming doc comments are a next step after these reviews, but it will be important to highlight how this factory function is used since it's not a common CDK pattern (from what I can tell)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think this docstring needs a rewrite. Some things I want to highlight:

This method is called to register the

It doesn't have a side effect, so I wouldn't phrase it like this.

Don't call this function alone.

Avoid using negatives. Tell me what I should be doing instead!

Suggested phrasing:

Return a load balancing target for a specific container and port

Use this function to create a load balancer target if you want to load balance to
another container than the first essential container or the first mapped port on
the container.

Use the return value of this function where you would normally use a load balancer
target, instead of the `Service` object itself.

@example

listener.addTarget(service.loadBalancerTarget({
  containerName: 'MyContainer',
  containerPort: 1234
}));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very helpful feedback. Thanks!

};
}

public registerContainerTargets(containers: TargetProps[]) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is implemented in terms loadBalancerTarget; that's a good thing for maintenance and answers my earlier concerns about feature parity, but again leaves me wondering if 2 different interfaces is a boon in practice if I'm trying to discover how to use the system.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'm not sure why we need to invert the call pattern here. Why is the current mechanism of targetGroup.addTarget() not good enough?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using registerContainerTargets we could potentially make some setting (e.g., target group port) opinionated so that users don't need to set up some properties. Also this is a team decision and we feel like it would make more senses for some users to start from service instead of listener or target group. However, we might be wrong since we aren't real users. I agree that this implementation needs some tweaks, if we decide to keep it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you be open to separating the concerns?

As in, add the fine-grained target control in this PR, and add the convenience function in a future PR? I would like to separate them out very cleanly.

If you insist on adding it, what I would like to do is mark the function and every new interface that is used to support it @experimental.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am more inclined to put it in another PR so that I can invite my teammate to review and help refine the implementation, because we feel like this is a behavior we should support.

packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts Outdated Show resolved Hide resolved
@rix0rrr rix0rrr changed the title feat(ecs): multiple target group support feat(ecs): more control over load balancer attachments Sep 17, 2019
@rix0rrr rix0rrr changed the title feat(ecs): more control over load balancer attachments feat(ecs): more control over load balancer targeting Sep 17, 2019
Copy link
Contributor

@rix0rrr rix0rrr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think maybe it's also worthwhile to reimplement the default attachToApplicationLoadBalancer() as such to make sure we converge to a single code path as quickly as possible:

class BaseService {
  public attachToApplictionLoadBalancer(args: Args) {
    return this.defaultLoadBalancerTarget.attachToApplicationLoadBalancer(args);
  }

  private get defaultLoadBalancerTarget() {
    return this.loadBalancerTarget({
      containerName: this.taskDefinition.defaultContainer!.containerName,
      containerPort: this.taskDefinition.defaultContainer!.containerPort,
    });
  }
}

Put into the docstring of XxxService that if it is used as a load balancer target, it will route to the default port of the default container, and that people should use loadBalancerTarget() to route traffic somewhere else.

Don't forget to describe this in README.md as well, and give an example of the new usage.

packages/@aws-cdk/aws-ecs/lib/base/base-service.ts Outdated Show resolved Hide resolved
};
}

public registerContainerTargets(containers: TargetProps[]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'm not sure why we need to invert the call pattern here. Why is the current mechanism of targetGroup.addTarget() not good enough?

packages/@aws-cdk/aws-ecs/lib/base/base-service.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/aws-ecs/lib/base/base-service.ts Outdated Show resolved Hide resolved
};
}

if (this.networkMode === NetworkMode.BRIDGE) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the order of ifs here correct? Do we really check port mappings before checking network mode?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I fully understand what you mean. Is there any problem with the logic: check if port mapping exists then determine the security group port to open?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user somehow specifies a host port in bridge mode (and this is a public function so they could call it directly with too much information) I believe the early return would cause an incorrect binding to that port instead of the ephemeral range. Moving this above the port check would fix that edge case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the user specifies a host port in bridge mode, it should return the host port right? I think it returns the ephemeral range only if the network mode is bridge and the host port is 0 (or undefined). See hostPort.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, disregard this one :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just trying to be sure. This looks and feels like hairy logic.

packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/aws-ecs/lib/base/base-service.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts Outdated Show resolved Hide resolved
@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

Copy link
Contributor

@rix0rrr rix0rrr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it. Almost there, just some small niggles.

packages/@aws-cdk/aws-ecs/README.md Outdated Show resolved Hide resolved
packages/@aws-cdk/aws-ecs/README.md Outdated Show resolved Hide resolved
@@ -232,6 +295,66 @@ export abstract class BaseService extends Resource
return ret;
}

public loadBalancerTarget(options: LoadBalancerTargetOptions): IEcsLoadBalancerTarget {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think this docstring needs a rewrite. Some things I want to highlight:

This method is called to register the

It doesn't have a side effect, so I wouldn't phrase it like this.

Don't call this function alone.

Avoid using negatives. Tell me what I should be doing instead!

Suggested phrasing:

Return a load balancing target for a specific container and port

Use this function to create a load balancer target if you want to load balance to
another container than the first essential container or the first mapped port on
the container.

Use the return value of this function where you would normally use a load balancer
target, instead of the `Service` object itself.

@example

listener.addTarget(service.loadBalancerTarget({
  containerName: 'MyContainer',
  containerPort: 1234
}));

};
}

public registerContainerTargets(containers: TargetProps[]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you be open to separating the concerns?

As in, add the fine-grained target control in this PR, and add the convenience function in a future PR? I would like to separate them out very cleanly.

If you insist on adding it, what I would like to do is mark the function and every new interface that is used to support it @experimental.

packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/aws-ecs/lib/ec2/ec2-service.ts Outdated Show resolved Hide resolved
@iamhopaul123 iamhopaul123 force-pushed the multiple-target-groups branch 2 times, most recently from 7e108fd to e6c97ad Compare September 18, 2019 19:19
@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@rix0rrr rix0rrr changed the title feat(ecs): more control over load balancer targeting feat(ecs): allow load balancing to any container and port of service Sep 19, 2019
@rix0rrr rix0rrr merged commit c3b3c93 into aws:master Sep 19, 2019
eladb pushed a commit that referenced this pull request Sep 23, 2019
…4107)

Add a `loadBalancerTarget()` method to ECS's Service, which allows load balancing
to containers other than the default container and to ports beside the first mapped port.

Services can be registered to multiple ELBv2 target groups at the same time if necessary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr/do-not-merge This PR should not be merged at this time.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants