From 4f8aa40ce8cd4e4dc5b259c2c182256b5699e5b8 Mon Sep 17 00:00:00 2001 From: Raphael Torquato <89878688+raphaeltorquat0@users.noreply.github.com> Date: Sun, 26 Apr 2026 12:07:18 -0300 Subject: [PATCH] Add support for loadBalancerClass in LoadBalancer services This adds a new configuration option `load_balancer_class` that allows users to specify which load balancer provider should handle the service when running multiple providers in the same cluster. The setting can be configured globally via the OperatorConfiguration CRD and will be applied to all LoadBalancer services (master, replica, and connection pooler services). Fixes #2600 --- docs/reference/operator_parameters.md | 7 +++++++ manifests/operatorconfiguration.crd.yaml | 3 +++ manifests/postgresql-operator-default-configuration.yaml | 1 + pkg/apis/acid.zalan.do/v1/operator_configuration_type.go | 1 + pkg/cluster/k8sres.go | 3 +++ pkg/controller/operator_config.go | 1 + pkg/util/config/config.go | 1 + 7 files changed, 17 insertions(+) diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 83f693acc..85fbcabab 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -719,6 +719,13 @@ In the CRD-based configuration they are grouped under the `load_balancer` key. defines external traffic policy for load balancers. Allowed values are `Cluster` (default) and `Local`. +* **load_balancer_class** + specifies the load balancer class to use for LoadBalancer services. This is + useful when running multiple load balancer providers in the same cluster, + allowing you to select which provider should handle the service. For example, + `service.k8s.aws/nlb` for AWS Network Load Balancer. When not set, the + cluster's default load balancer class is used. + * **master_dns_name_format** defines the DNS name string template for the master load balancer cluster. The default is `{cluster}.{namespace}.{hostedzone}`, where `{cluster}` is diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 3be545b65..44409a927 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -473,6 +473,9 @@ spec: replica_legacy_dns_name_format: type: string default: "{cluster}-repl.{team}.{hostedzone}" + load_balancer_class: + type: string + description: "LoadBalancerClass to use for LoadBalancer services" aws_or_gcp: type: object properties: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 1c6a0e34a..6b27a4858 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -149,6 +149,7 @@ configuration: enable_replica_load_balancer: false enable_replica_pooler_load_balancer: false external_traffic_policy: "Cluster" + # load_balancer_class: "service.k8s.aws/nlb" master_dns_name_format: "{cluster}.{namespace}.{hostedzone}" # master_legacy_dns_name_format: "{cluster}.{team}.{hostedzone}" replica_dns_name_format: "{cluster}-repl.{namespace}.{hostedzone}" diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 453d618d3..33f01fd5b 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -147,6 +147,7 @@ type LoadBalancerConfiguration struct { ReplicaDNSNameFormat config.StringTemplate `json:"replica_dns_name_format,omitempty"` ReplicaLegacyDNSNameFormat config.StringTemplate `json:"replica_legacy_dns_name_format,omitempty"` ExternalTrafficPolicy string `json:"external_traffic_policy" default:"Cluster"` + LoadBalancerClass string `json:"load_balancer_class,omitempty"` } // AWSGCPConfiguration defines the configuration for AWS diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 2eb867f06..ae5c66dd6 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -2020,6 +2020,9 @@ func (c *Cluster) configureLoadBalanceService(serviceSpec *v1.ServiceSpec, sourc c.logger.Debugf("final load balancer source ranges as seen in a service spec (not necessarily applied): %q", serviceSpec.LoadBalancerSourceRanges) serviceSpec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyType(c.OpConfig.ExternalTrafficPolicy) + if c.OpConfig.LoadBalancerClass != "" { + serviceSpec.LoadBalancerClass = &c.OpConfig.LoadBalancerClass + } serviceSpec.Type = v1.ServiceTypeLoadBalancer } diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 0a458618b..feed6bc20 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -176,6 +176,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.ReplicaDNSNameFormat = fromCRD.LoadBalancer.ReplicaDNSNameFormat result.ReplicaLegacyDNSNameFormat = fromCRD.LoadBalancer.ReplicaLegacyDNSNameFormat result.ExternalTrafficPolicy = util.Coalesce(fromCRD.LoadBalancer.ExternalTrafficPolicy, "Cluster") + result.LoadBalancerClass = fromCRD.LoadBalancer.LoadBalancerClass // AWS or GCP config result.WALES3Bucket = fromCRD.AWSGCP.WALES3Bucket diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 914d7a180..961a47de9 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -220,6 +220,7 @@ type Config struct { StorageResizeMode string `name:"storage_resize_mode" default:"pvc"` EnableLoadBalancer *bool `name:"enable_load_balancer"` // deprecated and kept for backward compatibility ExternalTrafficPolicy string `name:"external_traffic_policy" default:"Cluster"` + LoadBalancerClass string `name:"load_balancer_class"` MasterDNSNameFormat StringTemplate `name:"master_dns_name_format" default:"{cluster}.{namespace}.{hostedzone}"` MasterLegacyDNSNameFormat StringTemplate `name:"master_legacy_dns_name_format" default:"{cluster}.{team}.{hostedzone}"` ReplicaDNSNameFormat StringTemplate `name:"replica_dns_name_format" default:"{cluster}-repl.{namespace}.{hostedzone}"`