Skip to content

Enable frontend NLB #4126

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

wweiwei-li
Copy link
Collaborator

@wweiwei-li wweiwei-li commented Apr 4, 2025

Issue

#2297

Description

With this feature , the controller will automatically provision a Network Load Balancer and register the Application Load Balancer as its target. Additional annotations are available to customize the NLB configurations, including options for scheme, security groups, subnets, and health check. The ingress will expose both the ALB DNS name and NLB DNS name. This allows users to combine the benefits of NLB and ALB into a single solution, leveraging NLB features like static IP address and PrivateLink, while retaining the rich routing capabilities of ALB.

Checklist

  • Added tests that cover your change (if possible)
  • Added/modified documentation as required (such as the README.md, or the docs directory)
  • Manually tested
  • Made sure the title of the PR is a good description that can go into the release notes

BONUS POINTS checklist: complete for good vibes and maybe prizes?! 🤯

  • Backfilled missing tests for code in same general area 🎉
  • Refactored something and made the world a better place 🌟

@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. approved Indicates a PR has been approved by an approver from all required OWNERS files. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Apr 4, 2025
@@ -152,7 +152,8 @@ func (r *serviceReconciler) buildModel(ctx context.Context, svc *corev1.Service)
}

func (r *serviceReconciler) deployModel(ctx context.Context, svc *corev1.Service, stack core.Stack) error {
if err := r.stackDeployer.Deploy(ctx, stack, r.metricsCollector, "service"); err != nil {
albTargetGroupDesiredState := core.NewFrontendNlbTargetGroupDesiredState()
Copy link
Collaborator

Choose a reason for hiding this comment

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

curious what this is for?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It is not needed for Service. Updated it to pass nil instead

logger: logger,
stack: stack,
frontendNlbTargetGroupDesiredState: frontendNlbTargetGroupDesiredState,
unmatchedResTGs: nil,
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: no need to specify nil

Copy link
Collaborator

Choose a reason for hiding this comment

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

What's the purpose of unmatchedResTGs?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It is not used. Removed it

}

for _, sdkTG := range unmatchedSDKTGs {
if sdkTG.TargetGroup.TargetType != "alb" {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please use elbv2types.TargetTypeEnumAlb :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated it

func filterALBTargetGroups(targetGroups []*elbv2model.TargetGroup) []*elbv2model.TargetGroup {
var filteredTargetGroups []*elbv2model.TargetGroup
for _, tg := range targetGroups {
if tg.Spec.TargetType == "alb" {
Copy link
Collaborator

Choose a reason for hiding this comment

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

same - elbv2types.TargetTypeEnumAlb

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated it

stackTagsLegacy := s.trackingProvider.StackTagsLegacy(s.stack)
return s.taggingManager.ListTargetGroups(ctx,
tracking.TagsAsTagFilter(stackTags),
tracking.TagsAsTagFilter(stackTagsLegacy))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is stack tagging legacy needed as this is a new feature?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch. Removed it

}

func isALbInactiveError(err error) bool {
var awsErr *elbv2types.InvalidTargetException
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we might want to tread carefully here..
https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-elastic-load-balancing-v2/Class/InvalidTargetException/

The InvalidTargetException can be used for a lot of things.

I think it might be better to ignore the error and continue on with the rest of the reconcilers. When all other reconcilers complete, we could return a "please reconcile again in 15 seconds" error to ensure we keep retrying the alb registration.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agree, updated it to ignore the error, and requeue after reconcilation

synthesizers := []ResourceSynthesizer{
ec2.NewSecurityGroupSynthesizer(d.cloud.EC2(), d.trackingProvider, d.ec2TaggingManager, d.ec2SGManager, d.vpcID, d.logger, stack),
}

if controllerName == "ingress" {
Copy link
Collaborator

Choose a reason for hiding this comment

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

haha! Nice. Maybe use a constant here to ensure that we don't have any weird casing issues.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure, updated it to use const

}

func (t *defaultModelBuildTask) buildEnableFrontendNlbViaAnnotation(ctx context.Context) (bool, bool, error) {
explicitEnableFrontendNlb := make(map[bool]struct{})
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a very confusing way to see if there's annotations with differing values.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, it was confusing. I updated it to use a pointer

return t.defaultScheme, nil
}

func (t *defaultModelBuildTask) buildFrontendNlbSubnetMappings(ctx context.Context, scheme elbv2model.LoadBalancerScheme) ([]elbv2model.SubnetMapping, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Did you consider re-using the svc / ingress subnet mapping builder? It would be good to reduce code duplication.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, But I think they are using different annotation. Also existing svc/ingress handle more complex logic.

return elbv2model.LoadBalancerSpec{}, err
}

// use alb security group if it is not explicitly specified
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you want to some kind of validation that ensures the ALB alb sg / subnets are compatible with the NLB? It's ok to say we will differ the validation to ELB API :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, I though we can differ to API. But we can decide if we want to add it after bugbash.

}

func (t *defaultModelBuildTask) buildFrontendNlbListeners(ctx context.Context, albListenerPorts []int32) error {
FrontendNlbListenerConfigsByPort := make(map[int32][]FrontendNlbListenConfigWithIngress)
Copy link
Collaborator

Choose a reason for hiding this comment

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

conventionally, it would be better to start this var with a lower case letter.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated all lower case.

// build frontend nlb config by port for ingress
for _, member := range t.ingGroup.Members {
ingKey := k8s.NamespacedName(member.Ing)
FrontendNlbListenerConfigByPortForIngress, err := t.buildFrontendNlbListenerConfigByPortForIngress(ctx, &member, albListenerPorts)
Copy link
Collaborator

Choose a reason for hiding this comment

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

same, local vars should be lower case.

finalConfig.HealthCheckConfig.TimeoutSeconds = healthCheckConfig.TimeoutSeconds
}

if explicit["HealthyThresholdCount"] {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It looks like this block of code is repeated a couple times with differing hash map keys. Can you refactor this into a helper function that takes in the necessary parameters?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, added helper to cover all expect protocol.

return nil, err
}

FrontendNlbListenerResID := fmt.Sprintf("FrontendNlb-ls-%v-%v", config.Protocol, port)
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: helper function for this

}

func (t *defaultModelBuildTask) buildFrontendNlbTargetGroup(ctx context.Context, port int32, config FrontendNlbListenerConfig) (*elbv2model.TargetGroup, error) {
FrontendNlbtgResID := fmt.Sprintf("FrontendNlb-tg-%v-%v", config.Protocol, port)
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit helper function

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added helper

@wweiwei-li wweiwei-li changed the title Enable frontend NLB (WIP) Enable frontend NLB Apr 10, 2025
@wweiwei-li
Copy link
Collaborator Author

Update:

  1. Added Doc Page
  2. Added E2E test
  3. Added more Unit test to test build listeners

Fix

  1. DNS name
  2. Build NLB name based on ALB name
  3. Using listenerPortConfigByIngress to determine NLB listener port instead of albListenerPorts

if len(a) != len(b) {
return false
}
for i := range a {
Copy link
Collaborator

Choose a reason for hiding this comment

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

do we care about ordering here? Might better to use two sets to compare ingress status equality but it's probably not a big deal.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agree, updated it to use two sets to compare

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: wweiwei-li, zac-nixon

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:
  • OWNERS [wweiwei-li,zac-nixon]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@wweiwei-li wweiwei-li force-pushed the frontendNlb branch 2 times, most recently from 9809175 to 919118f Compare April 15, 2025 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants