add pooler suffix to DNS annotation of pooler LoadBalancer service (#2188)
* add pooler suffix to DNS annotation of pooler LoadBalancer service * need generatePoolerServiceAnnotations function
This commit is contained in:
parent
7887ebbbce
commit
c9cada66c7
|
|
@ -810,6 +810,9 @@ Load balancer services can also be enabled for the [connection pooler](user.md#c
|
||||||
pods with manifest flags `enableMasterPoolerLoadBalancer` and/or
|
pods with manifest flags `enableMasterPoolerLoadBalancer` and/or
|
||||||
`enableReplicaPoolerLoadBalancer` or in the operator configuration with
|
`enableReplicaPoolerLoadBalancer` or in the operator configuration with
|
||||||
`enable_master_pooler_load_balancer` and/or `enable_replica_pooler_load_balancer`.
|
`enable_master_pooler_load_balancer` and/or `enable_replica_pooler_load_balancer`.
|
||||||
|
For the `external-dns.alpha.kubernetes.io/hostname` annotation the `-pooler`
|
||||||
|
suffix will be appended to the cluster name used in the template which is
|
||||||
|
defined in `master|replica_dns_name_format`.
|
||||||
|
|
||||||
## Running periodic 'autorepair' scans of K8s objects
|
## Running periodic 'autorepair' scans of K8s objects
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -670,6 +670,20 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
'LoadBalancer',
|
'LoadBalancer',
|
||||||
"Expected LoadBalancer service type for replica pooler pod, found {}")
|
"Expected LoadBalancer service type for replica pooler pod, found {}")
|
||||||
|
|
||||||
|
master_annotations = {
|
||||||
|
"external-dns.alpha.kubernetes.io/hostname": "acid-minimal-cluster-pooler.default.db.example.com",
|
||||||
|
"service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600",
|
||||||
|
}
|
||||||
|
self.eventuallyTrue(lambda: k8s.check_service_annotations(
|
||||||
|
master_pooler_label+","+pooler_label, master_annotations), "Wrong annotations")
|
||||||
|
|
||||||
|
replica_annotations = {
|
||||||
|
"external-dns.alpha.kubernetes.io/hostname": "acid-minimal-cluster-pooler-repl.default.db.example.com",
|
||||||
|
"service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600",
|
||||||
|
}
|
||||||
|
self.eventuallyTrue(lambda: k8s.check_service_annotations(
|
||||||
|
replica_pooler_label+","+pooler_label, replica_annotations), "Wrong annotations")
|
||||||
|
|
||||||
# Turn off only master connection pooler
|
# Turn off only master connection pooler
|
||||||
k8s.api.custom_objects_api.patch_namespaced_custom_object(
|
k8s.api.custom_objects_api.patch_namespaced_custom_object(
|
||||||
'acid.zalan.do', 'v1', 'default',
|
'acid.zalan.do', 'v1', 'default',
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ spec:
|
||||||
enableReplicaLoadBalancer: false
|
enableReplicaLoadBalancer: false
|
||||||
enableConnectionPooler: false # enable/disable connection pooler deployment
|
enableConnectionPooler: false # enable/disable connection pooler deployment
|
||||||
enableReplicaConnectionPooler: false # set to enable connectionPooler for replica service
|
enableReplicaConnectionPooler: false # set to enable connectionPooler for replica service
|
||||||
|
enableMasterPoolerLoadBalancer: false
|
||||||
|
enableReplicaPoolerLoadBalancer: false
|
||||||
allowedSourceRanges: # load balancers' source ranges for both master and replica services
|
allowedSourceRanges: # load balancers' source ranges for both master and replica services
|
||||||
- 127.0.0.1/32
|
- 127.0.0.1/32
|
||||||
databases:
|
databases:
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ type ConnectionPoolerObjects struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) connectionPoolerName(role PostgresRole) string {
|
func (c *Cluster) connectionPoolerName(role PostgresRole) string {
|
||||||
name := c.Name + "-pooler"
|
name := fmt.Sprintf("%s-%s", c.Name, constants.ConnectionPoolerResourceSuffix)
|
||||||
if role == Replica {
|
if role == Replica {
|
||||||
name = fmt.Sprintf("%s-%s", name, "repl")
|
name = fmt.Sprintf("%s-%s", name, "repl")
|
||||||
}
|
}
|
||||||
|
|
@ -163,24 +163,27 @@ func (c *Cluster) createConnectionPooler(LookupFunction InstallFunction) (SyncRe
|
||||||
return reason, nil
|
return reason, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Generate pool size related environment variables.
|
// Generate pool size related environment variables.
|
||||||
//
|
//
|
||||||
// MAX_DB_CONN would specify the global maximum for connections to a target
|
// MAX_DB_CONN would specify the global maximum for connections to a target
|
||||||
// database.
|
//
|
||||||
|
// database.
|
||||||
//
|
//
|
||||||
// MAX_CLIENT_CONN is not configurable at the moment, just set it high enough.
|
// MAX_CLIENT_CONN is not configurable at the moment, just set it high enough.
|
||||||
//
|
//
|
||||||
// DEFAULT_SIZE is a pool size per db/user (having in mind the use case when
|
// DEFAULT_SIZE is a pool size per db/user (having in mind the use case when
|
||||||
// most of the queries coming through a connection pooler are from the same
|
//
|
||||||
// user to the same db). In case if we want to spin up more connection pooler
|
// most of the queries coming through a connection pooler are from the same
|
||||||
// instances, take this into account and maintain the same number of
|
// user to the same db). In case if we want to spin up more connection pooler
|
||||||
// connections.
|
// instances, take this into account and maintain the same number of
|
||||||
|
// connections.
|
||||||
//
|
//
|
||||||
// MIN_SIZE is a pool's minimal size, to prevent situation when sudden workload
|
// MIN_SIZE is a pool's minimal size, to prevent situation when sudden workload
|
||||||
// have to wait for spinning up a new connections.
|
//
|
||||||
|
// have to wait for spinning up a new connections.
|
||||||
//
|
//
|
||||||
// RESERVE_SIZE is how many additional connections to allow for a pooler.
|
// RESERVE_SIZE is how many additional connections to allow for a pooler.
|
||||||
|
|
||||||
func (c *Cluster) getConnectionPoolerEnvVars() []v1.EnvVar {
|
func (c *Cluster) getConnectionPoolerEnvVars() []v1.EnvVar {
|
||||||
spec := &c.Spec
|
spec := &c.Spec
|
||||||
connectionPoolerSpec := spec.ConnectionPooler
|
connectionPoolerSpec := spec.ConnectionPooler
|
||||||
|
|
@ -475,23 +478,23 @@ func (c *Cluster) generateConnectionPoolerDeployment(connectionPooler *Connectio
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) generateConnectionPoolerService(connectionPooler *ConnectionPoolerObjects) *v1.Service {
|
func (c *Cluster) generateConnectionPoolerService(connectionPooler *ConnectionPoolerObjects) *v1.Service {
|
||||||
|
|
||||||
spec := &c.Spec
|
spec := &c.Spec
|
||||||
|
poolerRole := connectionPooler.Role
|
||||||
serviceSpec := v1.ServiceSpec{
|
serviceSpec := v1.ServiceSpec{
|
||||||
Ports: []v1.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Name: connectionPooler.Name,
|
Name: connectionPooler.Name,
|
||||||
Port: pgPort,
|
Port: pgPort,
|
||||||
TargetPort: intstr.IntOrString{IntVal: c.servicePort(connectionPooler.Role)},
|
TargetPort: intstr.IntOrString{IntVal: c.servicePort(poolerRole)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Type: v1.ServiceTypeClusterIP,
|
Type: v1.ServiceTypeClusterIP,
|
||||||
Selector: map[string]string{
|
Selector: map[string]string{
|
||||||
"connection-pooler": c.connectionPoolerName(connectionPooler.Role),
|
"connection-pooler": c.connectionPoolerName(poolerRole),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.shouldCreateLoadBalancerForPoolerService(connectionPooler.Role, spec) {
|
if c.shouldCreateLoadBalancerForPoolerService(poolerRole, spec) {
|
||||||
c.configureLoadBalanceService(&serviceSpec, spec.AllowedSourceRanges)
|
c.configureLoadBalanceService(&serviceSpec, spec.AllowedSourceRanges)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -500,7 +503,7 @@ func (c *Cluster) generateConnectionPoolerService(connectionPooler *ConnectionPo
|
||||||
Name: connectionPooler.Name,
|
Name: connectionPooler.Name,
|
||||||
Namespace: connectionPooler.Namespace,
|
Namespace: connectionPooler.Namespace,
|
||||||
Labels: c.connectionPoolerLabels(connectionPooler.Role, false).MatchLabels,
|
Labels: c.connectionPoolerLabels(connectionPooler.Role, false).MatchLabels,
|
||||||
Annotations: c.annotationsSet(c.generateServiceAnnotations(connectionPooler.Role, spec)),
|
Annotations: c.annotationsSet(c.generatePoolerServiceAnnotations(poolerRole, spec)),
|
||||||
// make StatefulSet object its owner to represent the dependency.
|
// make StatefulSet object its owner to represent the dependency.
|
||||||
// By itself StatefulSet is being deleted with "Orphaned"
|
// By itself StatefulSet is being deleted with "Orphaned"
|
||||||
// propagation policy, which means that it's deletion will not
|
// propagation policy, which means that it's deletion will not
|
||||||
|
|
@ -515,6 +518,32 @@ func (c *Cluster) generateConnectionPoolerService(connectionPooler *ConnectionPo
|
||||||
return service
|
return service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) generatePoolerServiceAnnotations(role PostgresRole, spec *acidv1.PostgresSpec) map[string]string {
|
||||||
|
var dnsString string
|
||||||
|
annotations := c.getCustomServiceAnnotations(role, spec)
|
||||||
|
|
||||||
|
if c.shouldCreateLoadBalancerForPoolerService(role, spec) {
|
||||||
|
// set ELB Timeout annotation with default value
|
||||||
|
if _, ok := annotations[constants.ElbTimeoutAnnotationName]; !ok {
|
||||||
|
annotations[constants.ElbTimeoutAnnotationName] = constants.ElbTimeoutAnnotationValue
|
||||||
|
}
|
||||||
|
// -repl suffix will be added by replicaDNSName
|
||||||
|
clusterNameWithPoolerSuffix := c.connectionPoolerName(Master)
|
||||||
|
if role == Master {
|
||||||
|
dnsString = c.masterDNSName(clusterNameWithPoolerSuffix)
|
||||||
|
} else {
|
||||||
|
dnsString = c.replicaDNSName(clusterNameWithPoolerSuffix)
|
||||||
|
}
|
||||||
|
annotations[constants.ZalandoDNSNameAnnotation] = dnsString
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(annotations) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return annotations
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cluster) shouldCreateLoadBalancerForPoolerService(role PostgresRole, spec *acidv1.PostgresSpec) bool {
|
func (c *Cluster) shouldCreateLoadBalancerForPoolerService(role PostgresRole, spec *acidv1.PostgresSpec) bool {
|
||||||
|
|
||||||
switch role {
|
switch role {
|
||||||
|
|
@ -546,7 +575,7 @@ func (c *Cluster) listPoolerPods(listOptions metav1.ListOptions) ([]v1.Pod, erro
|
||||||
return pods.Items, nil
|
return pods.Items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//delete connection pooler
|
// delete connection pooler
|
||||||
func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) {
|
func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) {
|
||||||
c.logger.Infof("deleting connection pooler spilo-role=%s", role)
|
c.logger.Infof("deleting connection pooler spilo-role=%s", role)
|
||||||
|
|
||||||
|
|
@ -605,7 +634,7 @@ func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//delete connection pooler
|
// delete connection pooler
|
||||||
func (c *Cluster) deleteConnectionPoolerSecret() (err error) {
|
func (c *Cluster) deleteConnectionPoolerSecret() (err error) {
|
||||||
// Repeat the same for the secret object
|
// Repeat the same for the secret object
|
||||||
secretName := c.credentialSecretName(c.OpConfig.ConnectionPooler.User)
|
secretName := c.credentialSecretName(c.OpConfig.ConnectionPooler.User)
|
||||||
|
|
@ -654,7 +683,7 @@ func updateConnectionPoolerDeployment(KubeClient k8sutil.KubernetesClient, newDe
|
||||||
return deployment, nil
|
return deployment, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//updateConnectionPoolerAnnotations updates the annotations of connection pooler deployment
|
// updateConnectionPoolerAnnotations updates the annotations of connection pooler deployment
|
||||||
func updateConnectionPoolerAnnotations(KubeClient k8sutil.KubernetesClient, deployment *appsv1.Deployment, annotations map[string]string) (*appsv1.Deployment, error) {
|
func updateConnectionPoolerAnnotations(KubeClient k8sutil.KubernetesClient, deployment *appsv1.Deployment, annotations map[string]string) (*appsv1.Deployment, error) {
|
||||||
patchData, err := metaAnnotationsPatch(annotations)
|
patchData, err := metaAnnotationsPatch(annotations)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1901,6 +1901,28 @@ func (c *Cluster) configureLoadBalanceService(serviceSpec *v1.ServiceSpec, sourc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) generateServiceAnnotations(role PostgresRole, spec *acidv1.PostgresSpec) map[string]string {
|
func (c *Cluster) generateServiceAnnotations(role PostgresRole, spec *acidv1.PostgresSpec) map[string]string {
|
||||||
|
annotations := c.getCustomServiceAnnotations(role, spec)
|
||||||
|
|
||||||
|
if c.shouldCreateLoadBalancerForService(role, spec) {
|
||||||
|
dnsName := c.dnsName(role)
|
||||||
|
|
||||||
|
// Just set ELB Timeout annotation with default value, if it does not
|
||||||
|
// have a custom value
|
||||||
|
if _, ok := annotations[constants.ElbTimeoutAnnotationName]; !ok {
|
||||||
|
annotations[constants.ElbTimeoutAnnotationName] = constants.ElbTimeoutAnnotationValue
|
||||||
|
}
|
||||||
|
// External DNS name annotation is not customizable
|
||||||
|
annotations[constants.ZalandoDNSNameAnnotation] = dnsName
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(annotations) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return annotations
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) getCustomServiceAnnotations(role PostgresRole, spec *acidv1.PostgresSpec) map[string]string {
|
||||||
annotations := make(map[string]string)
|
annotations := make(map[string]string)
|
||||||
maps.Copy(annotations, c.OpConfig.CustomServiceAnnotations)
|
maps.Copy(annotations, c.OpConfig.CustomServiceAnnotations)
|
||||||
|
|
||||||
|
|
@ -1915,22 +1937,6 @@ func (c *Cluster) generateServiceAnnotations(role PostgresRole, spec *acidv1.Pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.shouldCreateLoadBalancerForService(role, spec) {
|
|
||||||
dnsName := c.dnsName(role)
|
|
||||||
|
|
||||||
// Just set ELB Timeout annotation with default value, if it does not
|
|
||||||
// have a cutom value
|
|
||||||
if _, ok := annotations[constants.ElbTimeoutAnnotationName]; !ok {
|
|
||||||
annotations[constants.ElbTimeoutAnnotationName] = constants.ElbTimeoutAnnotationValue
|
|
||||||
}
|
|
||||||
// External DNS name annotation is not customizable
|
|
||||||
annotations[constants.ZalandoDNSNameAnnotation] = dnsName
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(annotations) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return annotations
|
return annotations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -509,9 +509,9 @@ func (c *Cluster) dnsName(role PostgresRole) string {
|
||||||
var dnsString, oldDnsString string
|
var dnsString, oldDnsString string
|
||||||
|
|
||||||
if role == Master {
|
if role == Master {
|
||||||
dnsString = c.masterDNSName()
|
dnsString = c.masterDNSName(c.Name)
|
||||||
} else {
|
} else {
|
||||||
dnsString = c.replicaDNSName()
|
dnsString = c.replicaDNSName(c.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if cluster name starts with teamID we might need to provide backwards compatibility
|
// if cluster name starts with teamID we might need to provide backwards compatibility
|
||||||
|
|
@ -528,17 +528,17 @@ func (c *Cluster) dnsName(role PostgresRole) string {
|
||||||
return dnsString
|
return dnsString
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) masterDNSName() string {
|
func (c *Cluster) masterDNSName(clusterName string) string {
|
||||||
return strings.ToLower(c.OpConfig.MasterDNSNameFormat.Format(
|
return strings.ToLower(c.OpConfig.MasterDNSNameFormat.Format(
|
||||||
"cluster", c.Name,
|
"cluster", clusterName,
|
||||||
"namespace", c.Namespace,
|
"namespace", c.Namespace,
|
||||||
"team", c.teamName(),
|
"team", c.teamName(),
|
||||||
"hostedzone", c.OpConfig.DbHostedZone))
|
"hostedzone", c.OpConfig.DbHostedZone))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) replicaDNSName() string {
|
func (c *Cluster) replicaDNSName(clusterName string) string {
|
||||||
return strings.ToLower(c.OpConfig.ReplicaDNSNameFormat.Format(
|
return strings.ToLower(c.OpConfig.ReplicaDNSNameFormat.Format(
|
||||||
"cluster", c.Name,
|
"cluster", clusterName,
|
||||||
"namespace", c.Namespace,
|
"namespace", c.Namespace,
|
||||||
"team", c.teamName(),
|
"team", c.teamName(),
|
||||||
"hostedzone", c.OpConfig.DbHostedZone))
|
"hostedzone", c.OpConfig.DbHostedZone))
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package constants
|
||||||
|
|
||||||
// Connection pooler specific constants
|
// Connection pooler specific constants
|
||||||
const (
|
const (
|
||||||
|
ConnectionPoolerResourceSuffix = "pooler"
|
||||||
ConnectionPoolerUserName = "pooler"
|
ConnectionPoolerUserName = "pooler"
|
||||||
ConnectionPoolerSchemaName = "pooler"
|
ConnectionPoolerSchemaName = "pooler"
|
||||||
ConnectionPoolerDefaultType = "pgbouncer"
|
ConnectionPoolerDefaultType = "pgbouncer"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue