Skip to content

Commit 6fcf0ea

Browse files
committed
support pod topology spread constraints
also replace preferred affinity with topology spread constraint
1 parent 8302fdf commit 6fcf0ea

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+2931
-410
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ require (
2525
github.com/gophercloud/gophercloud v0.17.0
2626
github.com/gophercloud/utils v0.0.0-20210323225332-7b186010c04f
2727
github.com/goware/urlx v0.3.1
28-
github.com/openshift/api v0.0.0-20220203140920-bfe251c51d2d
28+
github.com/openshift/api v0.0.0-20220421141645-441fe135b2fc
2929
github.com/openshift/build-machinery-go v0.0.0-20211213093930-7e33a7eb4ce3
3030
github.com/openshift/client-go v0.0.0-20211209144617-7385dd6338e3
3131
github.com/openshift/library-go v0.0.0-20211220195323-eca2c467c492

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -627,8 +627,8 @@ github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDs
627627
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
628628
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
629629
github.com/openshift/api v0.0.0-20211209135129-c58d9f695577/go.mod h1:DoslCwtqUpr3d/gsbq4ZlkaMEdYqKxuypsDjorcHhME=
630-
github.com/openshift/api v0.0.0-20220203140920-bfe251c51d2d h1:WuD14VS4SFKKH5hKeYiHTswlEByICzMNvaZrDXUjZiY=
631-
github.com/openshift/api v0.0.0-20220203140920-bfe251c51d2d/go.mod h1:F/eU6jgr6Q2VhMu1mSpMmygxAELd7+BUxs3NHZ25jV4=
630+
github.com/openshift/api v0.0.0-20220421141645-441fe135b2fc h1:snZcb7+9+Hnb2VIp4QbUpClqK/4R3CK/lTyeDoGhefA=
631+
github.com/openshift/api v0.0.0-20220421141645-441fe135b2fc/go.mod h1:F/eU6jgr6Q2VhMu1mSpMmygxAELd7+BUxs3NHZ25jV4=
632632
github.com/openshift/build-machinery-go v0.0.0-20210712174854-1bb7fd1518d3/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE=
633633
github.com/openshift/build-machinery-go v0.0.0-20211213093930-7e33a7eb4ce3 h1:65oBhJYHzYK5VL0gF1eiYY37lLzyLZ47b9y5Kib1nf8=
634634
github.com/openshift/build-machinery-go v0.0.0-20211213093930-7e33a7eb4ce3/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE=

manifests/02-rbac.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ rules:
6464
resources:
6565
- limitranges
6666
- resourcequotas
67+
- nodes
6768
verbs:
6869
- list
6970
- apiGroups:

pkg/client/fake/fixtures.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type FixturesBuilder struct {
3434
registryConfigsIndexer cache.Indexer
3535
proxyConfigsIndexer cache.Indexer
3636
infraIndexer cache.Indexer
37+
nodeIndexer cache.Indexer
3738

3839
kClientSet []runtime.Object
3940
}
@@ -59,11 +60,24 @@ func NewFixturesBuilder() *FixturesBuilder {
5960
registryConfigsIndexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}),
6061
proxyConfigsIndexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}),
6162
infraIndexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}),
63+
nodeIndexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}),
6264
kClientSet: []runtime.Object{},
6365
}
6466
return factory
6567
}
6668

69+
// AddNodes adds corev1.Nodes to the lister cache
70+
func (f *FixturesBuilder) AddNodes(objs ...*corev1.Node) *FixturesBuilder {
71+
for _, v := range objs {
72+
err := f.nodeIndexer.Add(v)
73+
if err != nil {
74+
panic(err)
75+
}
76+
f.kClientSet = append(f.kClientSet, v)
77+
}
78+
return f
79+
}
80+
6781
// AddDeployments adds appsv1.Deployments to the lister cache
6882
func (f *FixturesBuilder) AddDeployments(objs ...*appsv1.Deployment) *FixturesBuilder {
6983
for _, v := range objs {

pkg/resource/podtemplatespec.go

Lines changed: 67 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -380,45 +380,83 @@ func makePodTemplateSpec(coreClient coreset.CoreV1Interface, proxyLister configl
380380
resources = *cr.Spec.Resources
381381
}
382382

383+
nodes, err := coreClient.Nodes().List(context.TODO(), metav1.ListOptions{LabelSelector: "topology.kubernetes.io/zone"})
384+
if err != nil {
385+
return corev1.PodTemplateSpec{}, deps, fmt.Errorf("could not check nodes for zone failure domain: %s", err)
386+
}
387+
hasZoneFailureDomain := len(nodes.Items) >= 1
388+
389+
// defaults topology spread constraints to both zone, node and workers.
390+
// on SNO environments, these constraints will always work, since the
391+
// skew will always be 0.
392+
// some bare metal cluster nodes will not include the zone labels, in
393+
// which case we just omit the related constraint.
394+
// we constraint scheduling to workers because we want to reduce the
395+
// scope of scheduling to workers only. we need this constraint
396+
// because tainted nodes (such as control plane nodes) are not excluded
397+
// from skew calculations, so if we don't limit scheduling to workers
398+
// the maxSkew won't allow more than one pod to be scheduled per node.
399+
// see https://k8s.io/docs/concepts/workloads/pods/pod-topology-spread-constraints
400+
// and https://github.com/kubernetes/kubernetes/issues/80921 for details.
401+
topologySpreadConstraints := []corev1.TopologySpreadConstraint{
402+
{
403+
MaxSkew: 1,
404+
TopologyKey: "kubernetes.io/hostname",
405+
WhenUnsatisfiable: corev1.DoNotSchedule,
406+
LabelSelector: &metav1.LabelSelector{
407+
MatchLabels: defaults.DeploymentLabels,
408+
},
409+
},
410+
{
411+
MaxSkew: 1,
412+
TopologyKey: "node-role.kubernetes.io/worker",
413+
WhenUnsatisfiable: corev1.DoNotSchedule,
414+
LabelSelector: &metav1.LabelSelector{
415+
MatchLabels: defaults.DeploymentLabels,
416+
},
417+
},
418+
}
419+
if hasZoneFailureDomain {
420+
zoneConstraint := corev1.TopologySpreadConstraint{
421+
MaxSkew: 1,
422+
TopologyKey: "topology.kubernetes.io/zone",
423+
WhenUnsatisfiable: corev1.DoNotSchedule,
424+
LabelSelector: &metav1.LabelSelector{
425+
MatchLabels: defaults.DeploymentLabels,
426+
},
427+
}
428+
topologySpreadConstraints = append(topologySpreadConstraints, zoneConstraint)
429+
}
430+
431+
// topology spread constraints might conflict with node selectors, so we
432+
// do not set defaults when they're specified.
433+
if cr.Spec.NodeSelector != nil {
434+
topologySpreadConstraints = nil
435+
}
436+
437+
if cr.Spec.TopologySpreadConstraints != nil {
438+
topologySpreadConstraints = cr.Spec.TopologySpreadConstraints
439+
}
440+
383441
// if user has provided an affinity through config spec we use it here, if not
384442
// then we fallback to a preferred affinity configuration. we only require a
385443
// certain affinity during schedule if the number of replicas is defined to two.
386444
affinity := cr.Spec.Affinity
387-
if affinity == nil {
445+
if affinity == nil && cr.Spec.Replicas == 2 {
388446
affinity = &corev1.Affinity{
389447
PodAntiAffinity: &corev1.PodAntiAffinity{
390-
PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{
448+
RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{
391449
{
392-
Weight: 100,
393-
PodAffinityTerm: corev1.PodAffinityTerm{
394-
TopologyKey: "kubernetes.io/hostname",
395-
Namespaces: []string{
396-
defaults.ImageRegistryOperatorNamespace,
397-
},
398-
LabelSelector: &metav1.LabelSelector{
399-
MatchLabels: defaults.DeploymentLabels,
400-
},
450+
TopologyKey: "kubernetes.io/hostname",
451+
Namespaces: []string{
452+
defaults.ImageRegistryOperatorNamespace,
401453
},
402-
},
403-
},
404-
},
405-
}
406-
if cr.Spec.Replicas == 2 {
407-
affinity = &corev1.Affinity{
408-
PodAntiAffinity: &corev1.PodAntiAffinity{
409-
RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{
410-
{
411-
TopologyKey: "kubernetes.io/hostname",
412-
Namespaces: []string{
413-
defaults.ImageRegistryOperatorNamespace,
414-
},
415-
LabelSelector: &metav1.LabelSelector{
416-
MatchLabels: defaults.DeploymentLabels,
417-
},
454+
LabelSelector: &metav1.LabelSelector{
455+
MatchLabels: defaults.DeploymentLabels,
418456
},
419457
},
420458
},
421-
}
459+
},
422460
}
423461
}
424462

@@ -478,6 +516,7 @@ func makePodTemplateSpec(coreClient coreset.CoreV1Interface, proxyLister configl
478516
ServiceAccountName: defaults.ServiceAccountName,
479517
SecurityContext: securityContext,
480518
Affinity: affinity,
519+
TopologySpreadConstraints: topologySpreadConstraints,
481520
TerminationGracePeriodSeconds: &gracePeriod,
482521
},
483522
}

0 commit comments

Comments
 (0)