-
Notifications
You must be signed in to change notification settings - Fork 1
Support allocating shared LB for services #1
Conversation
} | ||
if svc.Annotations[service.LoadBalancerAllocatingPortKey] == "true" { |
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.
When service is in "shared" mode, it will use the nodePort as the listener. This avoids the need to allocate a dedicated port for service, as nodePort is already unique across cluster that is allocated by k8s server.
main.go
Outdated
elbv2webhook.NewTargetGroupBindingMutator(cloud.ELBV2(), ctrl.Log).SetupWithManager(mgr) | ||
elbv2webhook.NewTargetGroupBindingValidator(mgr.GetClient(), cloud.ELBV2(), ctrl.Log).SetupWithManager(mgr) | ||
networkingwebhook.NewIngressValidator(mgr.GetClient(), controllerCFG.IngressConfig, ctrl.Log).SetupWithManager(mgr) | ||
//podReadinessGateInjector := inject.NewPodReadinessGate(controllerCFG.PodWebhookConfig, |
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.
Need to uncomment this, used for local development.
} | ||
|
||
func (b *defaultModelBuilder) Build(ctx context.Context, service *corev1.Service) (core.Stack, *elbv2model.LoadBalancer, bool, error) { | ||
stack := core.NewDefaultStack(core.StackID(k8s.NamespacedName(service))) | ||
// Initialize the global cache if not initialized | ||
if !b.initialized { |
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.
This makes sure that when services are in shared "stack", it reused the global cache that contains the stack. So
- Initialize the global cache when controller is started.
- Always looks for existin stack in cache when deploying. This is so that two services that in shared stack will be deployed together by sharing the same LB.
pkg/service/model_builder.go
Outdated
@@ -221,8 +283,49 @@ func (t *defaultModelBuildTask) run(ctx context.Context) error { | |||
return errors.Errorf("deletion_protection is enabled, cannot delete the service: %v", t.service.Name) | |||
} | |||
} | |||
// When service is deleted, update resources in the stack to make sure things are cleaned up properly. | |||
for _, port := range t.service.Spec.Ports { |
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.
This also cleans up resources when service is deleted.
} | ||
for _, svc := range serviceList.Items { | ||
if svc.Annotations[service.LoadBalancerStackKey] != "" { | ||
r.allocatedServices[svc.Annotations[service.LoadBalancerStackKey]] = map[int]bool{} |
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.
nit: if you only want to track existence, this is more efficient.
r.allocatedServices[svc.Annotations[service.LoadBalancerStackKey]] = map[int]bool{} | |
r.allocatedServices[svc.Annotations[service.LoadBalancerStackKey]] = map[int]struct{}{} |
You could also make the maps map[int32]struct{}{}
to avoid the int(port.NodePort)
everywhere.
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.
👍 addressed
r.lock.Lock() | ||
for _, port := range svc.Spec.Ports { | ||
r.allocatedServices[stackName] = map[int]bool{ | ||
int(port.Port): true, |
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.
This is the only place you use port.Port
instead of port.NodePort
. Just wanted to make sure that is intentional.
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.
yeah mistake. Should be nodePort 👍
@@ -487,6 +501,10 @@ func (t *defaultModelBuildTask) buildLoadBalancerName(_ context.Context, scheme | |||
_, _ = uuidHash.Write([]byte(scheme)) | |||
uuid := hex.EncodeToString(uuidHash.Sum(nil)) | |||
|
|||
if t.service.Annotations[LoadBalancerStackKey] != "" { | |||
return fmt.Sprintf("k8s-%.8s", t.service.Annotations[LoadBalancerStackKey]), nil |
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.
This format is used in at least a couple different places. Can it be made a function?
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.
done
pkg/service/model_builder.go
Outdated
if !b.initialized { | ||
// if not initialized, we need to build the global cache based on existing services | ||
var serviceList corev1.ServiceList | ||
if err := b.client.List(context.Background(), &serviceList); err != nil { |
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.
Can this be
if err := b.client.List(context.Background(), &serviceList); err != nil { | |
if err := b.client.List(ctx, &serviceList); err != nil { |
pkg/service/model_builder.go
Outdated
var stack core.Stack | ||
stack = core.NewDefaultStack(stackID) | ||
if service.Annotations[LoadBalancerAllocatingPortKey] == "true" { | ||
// service will be allocated to a stack. The external controller will be |
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.
Seems like an incomplete comment.
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.
yeah added
01317fc
to
39f802d
Compare
Signed-off-by: Daishan Peng <[email protected]>
39f802d
to
337919f
Compare
pkg/service/model_builder.go
Outdated
if b.stackGlobalCache[stackID] == nil { | ||
b.lock.Lock() | ||
b.stackGlobalCache[stackID] = core.NewDefaultStack(stackID) | ||
b.lock.Unlock() | ||
} | ||
b.lock.Lock() | ||
b.stackGlobalCache[stackID].AddService(&svc) | ||
b.lock.Unlock() |
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.
The read should also be protected here.
if b.stackGlobalCache[stackID] == nil { | |
b.lock.Lock() | |
b.stackGlobalCache[stackID] = core.NewDefaultStack(stackID) | |
b.lock.Unlock() | |
} | |
b.lock.Lock() | |
b.stackGlobalCache[stackID].AddService(&svc) | |
b.lock.Unlock() | |
b.lock.Lock() | |
if b.stackGlobalCache[stackID] == nil { | |
b.stackGlobalCache[stackID] = core.NewDefaultStack(stackID) | |
} | |
b.stackGlobalCache[stackID].AddService(&svc) | |
b.lock.Unlock() |
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.
Actually, is the lock
here even necessary? You have the other lock and that should take care of this, too, right?
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.
Yeah I will change to lock the whole snippet. It is necessary since another lock onlys locks per stack, not globally.
Signed-off-by: Daishan Peng <[email protected]>
4c8f739
to
6c74e5f
Compare
This PR adds ability to configure services to share load balancers for different listeners. The purpose of this is to reduce cost when bring NLB for service loadbalancer, while one service will provision one dedicated NLB.
There are two major changes for the implemetation: