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

Container Load Balancer - LB Rules #8218

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
3 changes: 1 addition & 2 deletions pkg/consts/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,7 @@ const (
// LoadBalancerBackendPoolConfigurationTypeNodeIP is the lb backend pool config type node ip
LoadBalancerBackendPoolConfigurationTypeNodeIP = "nodeIP"
// LoadBalancerBackendPoolConfigurationTypePODIP is the lb backend pool config type pod ip
// TODO (nilo19): support pod IP in the future
LoadBalancerBackendPoolConfigurationTypePODIP = "podIP"
LoadBalancerBackendPoolConfigurationTypePodIP = "podIP"
)
Copy link
Member

Choose a reason for hiding this comment

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

Good catch for the variable name here👍

Copy link
Contributor

Choose a reason for hiding this comment

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

Have we decided to use this instead of loadBalancerClass?


// error messages
Expand Down
4 changes: 2 additions & 2 deletions pkg/provider/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,13 +294,13 @@ func (az *Cloud) InitializeCloudFromConfig(ctx context.Context, config *config.C

if config.LoadBalancerBackendPoolConfigurationType == "" ||
// TODO(nilo19): support pod IP mode in the future
strings.EqualFold(config.LoadBalancerBackendPoolConfigurationType, consts.LoadBalancerBackendPoolConfigurationTypePODIP) {
strings.EqualFold(config.LoadBalancerBackendPoolConfigurationType, consts.LoadBalancerBackendPoolConfigurationTypePodIP) {
config.LoadBalancerBackendPoolConfigurationType = consts.LoadBalancerBackendPoolConfigurationTypeNodeIPConfiguration
} else {
supportedLoadBalancerBackendPoolConfigurationTypes := utilsets.NewString(
strings.ToLower(consts.LoadBalancerBackendPoolConfigurationTypeNodeIPConfiguration),
strings.ToLower(consts.LoadBalancerBackendPoolConfigurationTypeNodeIP),
strings.ToLower(consts.LoadBalancerBackendPoolConfigurationTypePODIP))
strings.ToLower(consts.LoadBalancerBackendPoolConfigurationTypePodIP))
if !supportedLoadBalancerBackendPoolConfigurationTypes.Has(strings.ToLower(config.LoadBalancerBackendPoolConfigurationType)) {
return fmt.Errorf("loadBalancerBackendPoolConfigurationType %s is not supported, supported values are %v", config.LoadBalancerBackendPoolConfigurationType, supportedLoadBalancerBackendPoolConfigurationTypes.UnsortedList())
}
Expand Down
34 changes: 34 additions & 0 deletions pkg/provider/azure_loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/intstr"
cloudprovider "k8s.io/cloud-provider"
servicehelpers "k8s.io/cloud-provider/service/helpers"
"k8s.io/klog/v2"
Expand Down Expand Up @@ -2800,6 +2801,39 @@ func (az *Cloud) getExpectedLBRules(
isIPv6 bool,

Choose a reason for hiding this comment

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

add PR desc

Copy link
Author

Choose a reason for hiding this comment

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

updated the PR description

) ([]*armnetwork.Probe, []*armnetwork.LoadBalancingRule, error) {
var expectedRules []*armnetwork.LoadBalancingRule
// If we are using Pod IP in the LB backend, we skip health probes, disable floating IP and use port.TargetPort.
if az.IsLBBackendPoolTypePodIP() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please extract this as a new func to make the main loop concise?

// Check for multi-IP families in the service spec (not allowed for BackendPool of type PodIP).
if len(service.Spec.IPFamilies) > 1 {
return nil, nil, fmt.Errorf("dual-stack services are not supported when LB backend pool type is PodIP")
Copy link
Contributor

@nilo19 nilo19 Feb 20, 2025

Choose a reason for hiding this comment

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

In the design doc it says "For dual stack, 2 services need to be created". Can you double check which one is the right behavior?

Copy link
Author

Choose a reason for hiding this comment

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

yes, that is right. Should I update the error string to say precisely that?

}
// Build rules for each service port but with floatingIP = false, no health probes.
for _, port := range service.Spec.Ports {
if port.TargetPort.Type == intstr.String {
return nil, nil, fmt.Errorf("named targetPort is not supported when LB backend pool type is PodIP: %s", port.TargetPort.StrVal)
Copy link
Member

Choose a reason for hiding this comment

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

are we able to make port name work?

Choose a reason for hiding this comment

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

Since, CLB directly rewrites the Destination Port in the packet after selecting the Destination POD, I think we can't support named Port specified in https://kubernetes.io/docs/concepts/services-networking/service/#field-spec-ports

}
ruleName := az.getLoadBalancerRuleName(service, port.Protocol, port.Port, isIPv6)
transportProto, _, _, err := getProtocolsFromKubernetesProtocol(port.Protocol)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse transport protocol: %w", err)
}
props, err := az.getExpectedLoadBalancingRulePropertiesForPort(service, lbFrontendIPConfigID, lbBackendPoolID, port, transportProto)
if err != nil {
return nil, nil, fmt.Errorf("error generating LB rule: %w", err)
}
// Turn off floating IP and skip health probe attachments.
props.EnableFloatingIP = ptr.To(false)
props.Probe = nil
props.BackendPort = &port.TargetPort.IntVal
expectedRules = append(expectedRules, &armnetwork.LoadBalancingRule{
Name: &ruleName,
Properties: props,
})
}
return nil, expectedRules, nil
}

// Otherwise, the existing flow remains unchanged
var expectedProbes []*armnetwork.Probe

// support podPresence health check when External Traffic Policy is local
Expand Down
45 changes: 45 additions & 0 deletions pkg/provider/azure_loadbalancer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3027,9 +3027,43 @@ func TestReconcileLoadBalancerRuleCommon(t *testing.T) {
expectedProbes: probes,
expectedRules: rules1DualStack,
})

testCases = append(testCases, []struct {
desc string
service v1.Service
loadBalancerSKU string
probeProtocol string
probePath string
expectedProbes map[bool][]*armnetwork.Probe
expectedRules map[bool][]*armnetwork.LoadBalancingRule
expectedErr bool
}{
{
desc: "LB backend pool of type PodIP - getExpectedLBRules should return error when the service is dual-stack",
service: getTestServiceDualStack("test", v1.ProtocolTCP, nil, 80),
loadBalancerSKU: "standardV2",
expectedErr: true,
},
{
desc: "LB backend pool of type PodIP - getExpectedLBRules should return error when the service port is named",
service: getTestServiceWithNamedTargetPorts("test", v1.ProtocolTCP, nil, false, 8080, "http-web-svc"),
loadBalancerSKU: "standardV2",
expectedErr: true,
},
{
desc: "LB backend pool of type PodIP - getExpectedLBRules should return expected rules and probes",
service: getTestServiceWithIntTargetPorts("test1", v1.ProtocolTCP, nil, false, 8080, 1234),
loadBalancerSKU: "standardV2",
expectedRules: getTestRuleCLB(false, 8080, 1234, false),
expectedProbes: nil,
},
}...)
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
az := GetTestCloud(ctrl)
if test.loadBalancerSKU == "standardV2" {
az.LoadBalancerBackendPoolConfigurationType = consts.LoadBalancerBackendPoolConfigurationTypePodIP
}
az.Config.LoadBalancerSKU = test.loadBalancerSKU
service := test.service
firstPort := service.Spec.Ports[0]
Expand Down Expand Up @@ -3130,6 +3164,17 @@ func getTCPResetTestRules(enableTCPReset bool) map[bool][]*armnetwork.LoadBalanc
}
}

func getTestRuleCLB(enableTCPReset bool, servicePort int32, targetPort int32, isIPv6 bool) map[bool][]*armnetwork.LoadBalancingRule {
rule := getTestRule(enableTCPReset, servicePort, isIPv6)
rule.Properties.EnableFloatingIP = to.Ptr(false)
rule.Properties.Probe = nil
rule.Properties.BackendPort = &targetPort

return map[bool][]*armnetwork.LoadBalancingRule{
false: {rule},
}
}

// getTestRule returns a rule for dualStack.
func getTestRule(enableTCPReset bool, port int32, isIPv6 bool) *armnetwork.LoadBalancingRule {
suffix := ""
Expand Down
62 changes: 62 additions & 0 deletions pkg/provider/azure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
cloudprovider "k8s.io/cloud-provider"
Expand Down Expand Up @@ -1259,6 +1260,67 @@ func getTestServiceCommon(identifier string, proto v1.Protocol, annotations map[
return svc
}

func getTestServiceWithNamedTargetPorts(identifier string, proto v1.Protocol, annotations map[string]string, isIPv6 bool, servicePort int32, namedTargetPorts ...string) v1.Service {
targetPorts := []intstr.IntOrString{}
for _, port := range namedTargetPorts {
targetPorts = append(targetPorts, intstr.FromString(port))
}
svc := getTestServiceWithTargetPortsCommon(identifier, proto, annotations, servicePort, targetPorts...)
svc.Spec.ClusterIP = "10.0.0.2"
svc.Spec.IPFamilies = []v1.IPFamily{v1.IPv4Protocol}
if isIPv6 {
svc.Spec.ClusterIP = "fd00::1907"
svc.Spec.IPFamilies = []v1.IPFamily{v1.IPv6Protocol}
}

return svc
}

func getTestServiceWithIntTargetPorts(identifier string, proto v1.Protocol, annotations map[string]string, isIPv6 bool, servicePort int32, intTargetPorts ...int32) v1.Service {
targetPorts := []intstr.IntOrString{}
for _, port := range intTargetPorts {
targetPorts = append(targetPorts, intstr.FromInt(int(port)))
}
svc := getTestServiceWithTargetPortsCommon(identifier, proto, annotations, servicePort, targetPorts...)
svc.Spec.ClusterIP = "10.0.0.2"
svc.Spec.IPFamilies = []v1.IPFamily{v1.IPv4Protocol}
if isIPv6 {
svc.Spec.ClusterIP = "fd00::1907"
svc.Spec.IPFamilies = []v1.IPFamily{v1.IPv6Protocol}
}

return svc
}

func getTestServiceWithTargetPortsCommon(identifier string, proto v1.Protocol, annotations map[string]string, servicePort int32, targetPorts ...intstr.IntOrString) v1.Service {
ports := []v1.ServicePort{}
for _, port := range targetPorts {
ports = append(ports, v1.ServicePort{
Name: "target-port",
Protocol: proto,
TargetPort: port,
Port: servicePort,
})
}

svc := v1.Service{
Spec: v1.ServiceSpec{
Type: v1.ServiceTypeLoadBalancer,
Ports: ports,
},
}
svc.Name = identifier
svc.Namespace = "default"
svc.UID = types.UID(identifier)
if annotations == nil {
svc.Annotations = make(map[string]string)
} else {
svc.Annotations = annotations
}

return svc
}

func getInternalTestService(identifier string, requestedPorts ...int32) v1.Service {
return getTestServiceWithAnnotation(identifier, map[string]string{consts.ServiceAnnotationLoadBalancerInternal: consts.TrueAnnotationValue}, false, requestedPorts...)
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/provider/config/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ func (az *Config) IsLBBackendPoolTypeNodeIP() bool {
return strings.EqualFold(az.LoadBalancerBackendPoolConfigurationType, consts.LoadBalancerBackendPoolConfigurationTypeNodeIP)
}

func (az *Config) IsLBBackendPoolTypePodIP() bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

Need a check to block podIP + non-standardV2?

Copy link
Contributor

Choose a reason for hiding this comment

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

If conflicted with multi-slb, need to block as well.

return strings.EqualFold(az.LoadBalancerBackendPoolConfigurationType, consts.LoadBalancerBackendPoolConfigurationTypePodIP)
}

func (az *Config) GetPutVMSSVMBatchSize() int {
return az.PutVMSSVMBatchSize
}
Expand Down
Loading