mirror of
https://github.com/zalando/postgres-operator.git
synced 2026-09-30 09:51:47 +02:00
Add topologySpreadConstraints configuration to pod spec. (#2530)
* Add topologySpreadConstraints configuration to pod spec. * Run update-codegen.sh to add deepcopy for new field to the api. * Reuse configured TopologySpreadConstraints for logical backup. * Remove x-kubernetes-preserve-unknown-fields and XPreserveUnknownFields. * Add topologySpreadConstraint example in the complete manifest. * Add support for helm chart. * Add documentation for topologySpreadConstraint. * Update e2e test to patch topologySpreadConstraints into the postgresqls manifest. * For e2e test, updated the PVC retention policy to remove redundant PVCs. * Fix e2e test, expected PVC count in end-to-end test after config changes.
This commit is contained in:
@@ -504,6 +504,11 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *appsv1.StatefulSet) *compa
|
||||
needsRollUpdate = true
|
||||
reasons = append(reasons, "new statefulset's pod affinity does not match the current one")
|
||||
}
|
||||
if !reflect.DeepEqual(c.Statefulset.Spec.Template.Spec.TopologySpreadConstraints, statefulSet.Spec.Template.Spec.TopologySpreadConstraints) {
|
||||
needsReplace = true
|
||||
needsRollUpdate = true
|
||||
reasons = append(reasons, "new statefulset's pod topologySpreadConstraints does not match the current one")
|
||||
}
|
||||
if len(c.Statefulset.Spec.Template.Spec.Tolerations) != len(statefulSet.Spec.Template.Spec.Tolerations) {
|
||||
needsReplace = true
|
||||
needsRollUpdate = true
|
||||
|
||||
@@ -612,6 +612,13 @@ func generatePodAntiAffinity(podAffinityTerm v1.PodAffinityTerm, preferredDuring
|
||||
return podAntiAffinity
|
||||
}
|
||||
|
||||
func generateTopologySpreadConstraints(labels labels.Set, topologySpreadConstraints []v1.TopologySpreadConstraint) []v1.TopologySpreadConstraint {
|
||||
for _, topologySpreadConstraint := range topologySpreadConstraints {
|
||||
topologySpreadConstraint.LabelSelector = &metav1.LabelSelector{MatchLabels: labels}
|
||||
}
|
||||
return topologySpreadConstraints
|
||||
}
|
||||
|
||||
func tolerations(tolerationsSpec *[]v1.Toleration, podToleration map[string]string) []v1.Toleration {
|
||||
// allow to override tolerations by postgresql manifest
|
||||
if len(*tolerationsSpec) > 0 {
|
||||
@@ -817,6 +824,7 @@ func (c *Cluster) generatePodTemplate(
|
||||
initContainers []v1.Container,
|
||||
sidecarContainers []v1.Container,
|
||||
sharePgSocketWithSidecars *bool,
|
||||
topologySpreadConstraintsSpec []v1.TopologySpreadConstraint,
|
||||
tolerationsSpec *[]v1.Toleration,
|
||||
nodeAffinity *v1.Affinity,
|
||||
schedulerName *string,
|
||||
@@ -887,6 +895,8 @@ func (c *Cluster) generatePodTemplate(
|
||||
podSpec.PriorityClassName = priorityClassName
|
||||
}
|
||||
|
||||
podSpec.TopologySpreadConstraints = generateTopologySpreadConstraints(labels, topologySpreadConstraintsSpec)
|
||||
|
||||
if sharePgSocketWithSidecars != nil && *sharePgSocketWithSidecars {
|
||||
addVarRunVolume(&podSpec)
|
||||
}
|
||||
@@ -1469,6 +1479,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
|
||||
initContainers,
|
||||
sidecarContainers,
|
||||
c.OpConfig.SharePgSocketWithSidecars,
|
||||
spec.TopologySpreadConstraints,
|
||||
&tolerationSpec,
|
||||
c.nodeAffinity(c.OpConfig.NodeReadinessLabel, spec.NodeAffinity),
|
||||
spec.SchedulerName,
|
||||
@@ -2346,6 +2357,8 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) {
|
||||
|
||||
tolerationsSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration)
|
||||
|
||||
topologySpreadConstraintsSpec := generateTopologySpreadConstraints(labels, spec.TopologySpreadConstraints)
|
||||
|
||||
// re-use the method that generates DB pod templates
|
||||
if podTemplate, err = c.generatePodTemplate(
|
||||
c.Namespace,
|
||||
@@ -2355,6 +2368,7 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) {
|
||||
[]v1.Container{},
|
||||
[]v1.Container{},
|
||||
util.False(),
|
||||
topologySpreadConstraintsSpec,
|
||||
&tolerationsSpec,
|
||||
c.nodeAffinity(c.OpConfig.NodeReadinessLabel, nil),
|
||||
nil,
|
||||
|
||||
@@ -4272,3 +4272,56 @@ func TestGenerateCapabilities(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTopologySpreadConstraints(t *testing.T) {
|
||||
clusterName := "acid-test-cluster"
|
||||
namespace := "default"
|
||||
labelSelector := &metav1.LabelSelector{
|
||||
MatchLabels: cluster.labelsSet(true),
|
||||
}
|
||||
|
||||
pg := acidv1.Postgresql{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: clusterName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: acidv1.PostgresSpec{
|
||||
NumberOfInstances: 1,
|
||||
Resources: &acidv1.Resources{
|
||||
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
|
||||
},
|
||||
Volume: acidv1.Volume{
|
||||
Size: "1G",
|
||||
},
|
||||
TopologySpreadConstraints: []v1.TopologySpreadConstraint{
|
||||
{
|
||||
MaxSkew: 1,
|
||||
TopologyKey: "topology.kubernetes.io/zone",
|
||||
WhenUnsatisfiable: v1.DoNotSchedule,
|
||||
LabelSelector: labelSelector,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cluster := New(
|
||||
Config{
|
||||
OpConfig: config.Config{
|
||||
PodManagementPolicy: "ordered_ready",
|
||||
},
|
||||
}, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder)
|
||||
cluster.Name = clusterName
|
||||
cluster.Namespace = namespace
|
||||
cluster.labelsSet(true)
|
||||
|
||||
s, err := cluster.generateStatefulSet(&pg.Spec)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, s.Spec.Template.Spec.TopologySpreadConstraints, v1.TopologySpreadConstraint{
|
||||
MaxSkew: int32(1),
|
||||
TopologyKey: "topology.kubernetes.io/zone",
|
||||
WhenUnsatisfiable: v1.DoNotSchedule,
|
||||
LabelSelector: labelSelector,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user