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

Add a controller for handling L4 Internal LoadBalancer services #991

Merged
merged 3 commits into from
Feb 20, 2020

Conversation

prameshj
Copy link
Contributor

This change adds a controller that will create the GCE resources(Forwarding rule, firewall rules, healthcheck, backend services) for Internal LoadBalancer services. The NEG creation will be done in the NEG controller and those changes are in PR - #959

Most of the code is similar to the service controller code in k/k.

@skmatti
/assign @MrHohn @freehan

@k8s-ci-robot k8s-ci-robot added 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. labels Jan 13, 2020
pkg/backends/syncer.go Outdated Show resolved Hide resolved
}

func (l4c *L4Controller) processServiceDeletion(key string, svc *v1.Service) error {
nm, err := utils.ToNamespacedName(key)
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need this as nm.Namespace and svc.Namespace are same?.

Copy link
Contributor

Choose a reason for hiding this comment

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

method can be update to
func (l4c *L4Controller) processServiceDeletion(svc *v1.Service) error

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 had added this because svc could be nil. But then I modified the main sync() to handle nil services. I am keeping the 'key' parameter to lookup the L4Handler.

pkg/controller/service_controller.go Outdated Show resolved Hide resolved
pkg/utils/namer/namer.go Outdated Show resolved Hide resolved
{Desc: "Empty", Input: []int{}, Result: nil},
} {
result := GetPortRanges(tc.Input)
if !reflect.DeepEqual(result, tc.Result) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The following implementation is much easier to debug test failures:
Refer: https://godoc.org/github.com/google/go-cmp/cmp#Diff

if diff := cmp.Diff(tc.Result, result); diff != "" {
   t.Errorf("GetPortRanges(%s) mismatch (-want +got):\n%s", tc.Desc, diff)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice.. fixed.

pkg/utils/utils.go Outdated Show resolved Hide resolved
Copy link
Member

@MrHohn MrHohn left a comment

Choose a reason for hiding this comment

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

Some drive-by comments. Will look more later:)

pkg/healthchecks/healthchecks_l4.go Outdated Show resolved Hide resolved
pkg/healthchecks/healthchecks_l4.go Outdated Show resolved Hide resolved
pkg/backends/backends.go Outdated Show resolved Hide resolved
pkg/controller/service_controller.go Outdated Show resolved Hide resolved
pkg/controller/service_controller.go Outdated Show resolved Hide resolved
pkg/controller/service_controller.go Outdated Show resolved Hide resolved
pkg/controller/service_controller.go Outdated Show resolved Hide resolved
pkg/controller/service_controller.go Outdated Show resolved Hide resolved
pkg/healthchecks/healthchecks_l4.go Outdated Show resolved Hide resolved
pkg/healthchecks/healthchecks_l4.go Outdated Show resolved Hide resolved
@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 17, 2020
@prameshj
Copy link
Contributor Author

Will work on rebasign and ping back for review. Thanks for the comments so far!

@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 17, 2020
@prameshj
Copy link
Contributor Author

I have fixed the merge conflicts and incorporated most of the comments. This PR is ready for another review.

@skmatti @MrHohn @freehan

@prameshj prameshj force-pushed the ilb-fr branch 2 times, most recently from bc46338 to 44fdbb5 Compare January 31, 2020 00:16
Copy link
Contributor

@skmatti skmatti left a comment

Choose a reason for hiding this comment

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

LGTM. small nits.

pkg/backends/backends.go Show resolved Hide resolved
pkg/controller/service_controller.go Outdated Show resolved Hide resolved
pkg/controller/service_controller_test.go Outdated Show resolved Hide resolved
pkg/controller/translator/translator.go Outdated Show resolved Hide resolved
Copy link
Contributor

@freehan freehan left a comment

Choose a reason for hiding this comment

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

Some structural suggestions.

@@ -0,0 +1,384 @@
/*
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe put this service controller into a separate pkg/l4 package.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}
updated := current.DeepCopy()
updated.Status.LoadBalancer = *newStatus
if _, err := svcClient.UpdateStatus(updated); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

// getForwardingRule with the given name
// rule with the given name exists with the correct parameters and creates a forwarding rule if
// it does not exist already. If shouldExist parameter is true, this function returns if forwarding rule is not found.
func (l *L4) ensureForwardingRule(loadBalancerName, bsLink string, options gce.ILBOptions) (*composite.ForwardingRule, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably worth putting protocol in the forwarding rule name.

This is to leave future room to enable dual-protocol ILB

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@prameshj
Copy link
Contributor Author

prameshj commented Feb 4, 2020

/retest

@prameshj prameshj force-pushed the ilb-fr branch 2 times, most recently from aaa50b1 to 734e3d5 Compare February 6, 2020 19:19
cmd/glbc/main.go Outdated
@@ -218,6 +219,12 @@ func runControllers(ctx *ingctx.ControllerContext) {

fwc := firewalls.NewFirewallController(ctx, flags.F.NodePortRanges.Values())

if flags.F.RunL4Controller {
l4Controller := l4controller.NewL4Controller(ctx, stopCh)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: l4controller.NewL4Controller seems a bit redundant.

I would recommend naming package to l4. I would imagine this package to grow significantly over time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


l4c.handlerMap = make(map[string]*loadbalancers.L4)
l4c.svcQueue = utils.NewPeriodicTaskQueue("l4", "services", l4c.sync)
ctx.ServiceInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the event handling here may be problematic. Consider this case:

The controller is down. Service with ILB is added. Then controller starts back up. Then the event will come in as Update instead of Add. (Please verify!) Then Update will get ignored because it does not have the finalizer.

I think the most conservative handling is to have Add/Update to be handled consistently.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Discussed in person. The exact case mentioned here will actually show up as an Add instead of Update upon controller restart. However, the logic here does not handle cases where a non-ILB service was updated to use ILB, as Minhan pointed out. Fixed this logic and added a test. Thanks!

} else {
oldSvc := old.(*v1.Service)
svcKey := utils.ServiceKeyFunc(curSvc.Namespace, curSvc.Name)
if l4c.needsUpdate(curSvc, oldSvc) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I am wondering if this is needed.
needsUpdate or not. It may be worth reasserting the LB config?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can filter service by if it has 1. finalizer V2 or 2. if it wants ILB.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, reworked this logic.
I included needsUpdate so we can avoid processing an update every time a finalizer is added/neg annotation is added/ status field is updated.

l4c.svcQueue.Shutdown()
}

func (l4c *L4Controller) getL4Handler(svcKey string, service *v1.Service) *loadbalancers.L4 {
Copy link
Contributor

Choose a reason for hiding this comment

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

why do you need to cache L4?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No strong reason. Mainly to avoid creating it over and over for the same service. I thought I could store references to already created resources in it, in future. Removed now, we can add it id the need arises.

needsILB, svcType := annotations.WantsL4ILB(svc)
if !needsILB {
klog.V(2).Infof("Service %s of type %s does not require L4 ILB, cleaning up resources", key, svcType)
return l4c.processServiceDeletion(key, svc)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit:
I recommend merging the deletion condition handling into one function needsDeletion

`
func needToDeleteILB(....) bool {
must have ILBFinalizerV2
AND (has deletion timestamp OR is not type ILB)
to return true
}

`

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good idea, done.

}
// Use the same function for both create and updates. If controller crashes and restarts,
// all existing services will show up as Service Adds.
status, err := l4.EnsureInternalLoadBalancer(nodeNames, service)
Copy link
Contributor

Choose a reason for hiding this comment

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

i see a bunch of SyncLoadBalancerFailed events. But no success event?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

return composite.DeleteHealthCheck(cloud, key, meta.VersionGA)
}

func HealthCheckName(shared bool, clusteruid, lbName string) (string, string) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add comment on the naming? For instance, why there is 2 firewall names

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}
return nil
}
err = deleteFunc(name)
Copy link
Contributor

Choose a reason for hiding this comment

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

is this the correct name?

Copy link
Contributor

Choose a reason for hiding this comment

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

add a comment why needs to delete 2 firewalls.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 10, 2020
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 11, 2020
@prameshj
Copy link
Contributor Author

/assign @freehan

Copy link
Contributor

@freehan freehan left a comment

Choose a reason for hiding this comment

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

This can be a follow up. Remember to collect usage metrics for primary_VM_IP NEG. https://github.com/kubernetes/ingress-gce/blob/master/pkg/metrics/features.go#L78

/lgtm
/approve

@@ -178,6 +178,10 @@ func (s *backendSyncer) GC(svcPorts []utils.ServicePort) error {
// gc deletes the provided backends
func (s *backendSyncer) gc(backends []*composite.BackendService, knownPorts sets.String) error {
for _, be := range backends {
// Skip L4 LB backend services
Copy link
Contributor

Choose a reason for hiding this comment

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

Add comment:
backendSyncer currently only GC backend services for L7 XLB/ILB.
L4 LB is GC as part of the deletion follow as there is no shared backend services among L4 ILBs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@k8s-ci-robot k8s-ci-robot added lgtm "Looks good to me", indicates that a PR is ready to be merged. approved Indicates a PR has been approved by an approver from all required OWNERS files. labels Feb 19, 2020
translator imports loadbalancer which imports firewalls,
which again depends on translator. Removed the translator
-> loadbalancer dependency.
Fixes including adding CustomSubnet support and self links for backend
service, healthcheck.
Removed the operation from queue key
Added connectiondraining to backend service.
Added a new flag to run L4 controller.
Used namer.PrimaryIPNEG() instead of namer.NEG()
Use a common description for all ILB resources.
Added locking for shared healthchecks.
Only process relevant updates in l4 controller
Renamed files, used patch instead of update.

unit tests for L4 ILB

Most of the tests in l4_test.go are from gce_loadbalancer_internal_test.go
@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Feb 20, 2020
@prameshj
Copy link
Contributor Author

This can be a follow up. Remember to collect usage metrics for primary_VM_IP NEG. https://github.com/kubernetes/ingress-gce/blob/master/pkg/metrics/features.go#L78

/lgtm
/approve

Good point, we need to add usage metrics for this L4 controller as well. Will cover PRIMARY_VM_IP NEG as part of that too. Thanks!

@freehan
Copy link
Contributor

freehan commented Feb 20, 2020

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Feb 20, 2020
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: freehan, prameshj

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:

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

@k8s-ci-robot k8s-ci-robot merged commit 444fd52 into kubernetes:master Feb 20, 2020
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. lgtm "Looks good to me", indicates that a PR is ready to be merged. 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.

5 participants