Skip to content

Commit 17fe91c

Browse files
authored
Merge pull request kubernetes#68173 from Huang-Wei/antiaffinity-symmetry-issue
Fix PodAntiAffinity issues in case of multiple affinityTerms
2 parents aa61b66 + 7490542 commit 17fe91c

File tree

4 files changed

+1547
-288
lines changed

4 files changed

+1547
-288
lines changed

pkg/scheduler/algorithm/predicates/metadata.go

Lines changed: 98 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ type predicateMetadata struct {
7272

7373
topologyPairsAntiAffinityPodsMap *topologyPairsMaps
7474
// A map of topology pairs to a list of Pods that can potentially match
75-
// the affinity rules of the "pod" and its inverse.
75+
// the affinity terms of the "pod" and its inverse.
7676
topologyPairsPotentialAffinityPods *topologyPairsMaps
7777
// A map of topology pairs to a list of Pods that can potentially match
78-
// the anti-affinity rules of the "pod" and its inverse.
78+
// the anti-affinity terms of the "pod" and its inverse.
7979
topologyPairsPotentialAntiAffinityPods *topologyPairsMaps
8080
serviceAffinityInUse bool
8181
serviceAffinityMatchingPodList []*v1.Pod
@@ -130,11 +130,14 @@ func (pfactory *PredicateMetadataFactory) GetMetadata(pod *v1.Pod, nodeNameToInf
130130
if pod == nil {
131131
return nil
132132
}
133-
topologyPairsMaps, err := getMatchingTopologyPairs(pod, nodeNameToInfoMap)
133+
// existingPodAntiAffinityMap will be used later for efficient check on existing pods' anti-affinity
134+
existingPodAntiAffinityMap, err := getTPMapMatchingExistingAntiAffinity(pod, nodeNameToInfoMap)
134135
if err != nil {
135136
return nil
136137
}
137-
topologyPairsAffinityPodsMaps, topologyPairsAntiAffinityPodsMaps, err := getPodsMatchingAffinity(pod, nodeNameToInfoMap)
138+
// incomingPodAffinityMap will be used later for efficient check on incoming pod's affinity
139+
// incomingPodAntiAffinityMap will be used later for efficient check on incoming pod's anti-affinity
140+
incomingPodAffinityMap, incomingPodAntiAffinityMap, err := getTPMapMatchingIncomingAffinityAntiAffinity(pod, nodeNameToInfoMap)
138141
if err != nil {
139142
glog.Errorf("[predicate meta data generation] error finding pods that match affinity terms: %v", err)
140143
return nil
@@ -144,9 +147,9 @@ func (pfactory *PredicateMetadataFactory) GetMetadata(pod *v1.Pod, nodeNameToInf
144147
podBestEffort: isPodBestEffort(pod),
145148
podRequest: GetResourceRequest(pod),
146149
podPorts: schedutil.GetContainerPorts(pod),
147-
topologyPairsPotentialAffinityPods: topologyPairsAffinityPodsMaps,
148-
topologyPairsPotentialAntiAffinityPods: topologyPairsAntiAffinityPodsMaps,
149-
topologyPairsAntiAffinityPodsMap: topologyPairsMaps,
150+
topologyPairsPotentialAffinityPods: incomingPodAffinityMap,
151+
topologyPairsPotentialAntiAffinityPods: incomingPodAntiAffinityMap,
152+
topologyPairsAntiAffinityPodsMap: existingPodAntiAffinityMap,
150153
}
151154
for predicateName, precomputeFunc := range predicateMetadataProducers {
152155
glog.V(10).Infof("Precompute: %v", predicateName)
@@ -185,6 +188,9 @@ func (topologyPairsMaps *topologyPairsMaps) removePod(deletedPod *v1.Pod) {
185188
}
186189

187190
func (topologyPairsMaps *topologyPairsMaps) appendMaps(toAppend *topologyPairsMaps) {
191+
if toAppend == nil {
192+
return
193+
}
188194
for pair := range toAppend.topologyPairToPods {
189195
for pod := range toAppend.topologyPairToPods[pair] {
190196
topologyPairsMaps.addTopologyPair(pair, pod)
@@ -232,13 +238,11 @@ func (meta *predicateMetadata) AddPod(addedPod *v1.Pod, nodeInfo *schedulercache
232238
return fmt.Errorf("invalid node in nodeInfo")
233239
}
234240
// Add matching anti-affinity terms of the addedPod to the map.
235-
topologyPairsMaps, err := getMatchingTopologyPairsOfExistingPod(meta.pod, addedPod, nodeInfo.Node())
241+
topologyPairsMaps, err := getMatchingAntiAffinityTopologyPairsOfPod(meta.pod, addedPod, nodeInfo.Node())
236242
if err != nil {
237243
return err
238244
}
239-
if len(topologyPairsMaps.podToTopologyPairs) > 0 {
240-
meta.topologyPairsAntiAffinityPodsMap.appendMaps(topologyPairsMaps)
241-
}
245+
meta.topologyPairsAntiAffinityPodsMap.appendMaps(topologyPairsMaps)
242246
// Add the pod to nodeNameToMatchingAffinityPods and nodeNameToMatchingAntiAffinityPods if needed.
243247
affinity := meta.pod.Spec.Affinity
244248
podNodeName := addedPod.Spec.NodeName
@@ -325,8 +329,8 @@ func getAffinityTermProperties(pod *v1.Pod, terms []v1.PodAffinityTerm) (propert
325329
return properties, nil
326330
}
327331

328-
// podMatchesAffinityTermProperties return true IFF the given pod matches all the given properties.
329-
func podMatchesAffinityTermProperties(pod *v1.Pod, properties []*affinityTermProperties) bool {
332+
// podMatchesAllAffinityTermProperties returns true IFF the given pod matches all the given properties.
333+
func podMatchesAllAffinityTermProperties(pod *v1.Pod, properties []*affinityTermProperties) bool {
330334
if len(properties) == 0 {
331335
return false
332336
}
@@ -338,11 +342,71 @@ func podMatchesAffinityTermProperties(pod *v1.Pod, properties []*affinityTermPro
338342
return true
339343
}
340344

341-
// getPodsMatchingAffinity finds existing Pods that match affinity terms of the given "pod".
342-
// It ignores topology. It returns a set of Pods that are checked later by the affinity
343-
// predicate. With this set of pods available, the affinity predicate does not
345+
// podMatchesAnyAffinityTermProperties returns true if the given pod matches any given property.
346+
func podMatchesAnyAffinityTermProperties(pod *v1.Pod, properties []*affinityTermProperties) bool {
347+
if len(properties) == 0 {
348+
return false
349+
}
350+
for _, property := range properties {
351+
if priorityutil.PodMatchesTermsNamespaceAndSelector(pod, property.namespaces, property.selector) {
352+
return true
353+
}
354+
}
355+
return false
356+
}
357+
358+
// getTPMapMatchingExistingAntiAffinity calculates the following for each existing pod on each node:
359+
// (1) Whether it has PodAntiAffinity
360+
// (2) Whether any AffinityTerm matches the incoming pod
361+
func getTPMapMatchingExistingAntiAffinity(pod *v1.Pod, nodeInfoMap map[string]*schedulercache.NodeInfo) (*topologyPairsMaps, error) {
362+
allNodeNames := make([]string, 0, len(nodeInfoMap))
363+
for name := range nodeInfoMap {
364+
allNodeNames = append(allNodeNames, name)
365+
}
366+
367+
var lock sync.Mutex
368+
var firstError error
369+
370+
topologyMaps := newTopologyPairsMaps()
371+
372+
appendTopologyPairsMaps := func(toAppend *topologyPairsMaps) {
373+
lock.Lock()
374+
defer lock.Unlock()
375+
topologyMaps.appendMaps(toAppend)
376+
}
377+
catchError := func(err error) {
378+
lock.Lock()
379+
defer lock.Unlock()
380+
if firstError == nil {
381+
firstError = err
382+
}
383+
}
384+
385+
processNode := func(i int) {
386+
nodeInfo := nodeInfoMap[allNodeNames[i]]
387+
node := nodeInfo.Node()
388+
if node == nil {
389+
catchError(fmt.Errorf("node not found"))
390+
return
391+
}
392+
for _, existingPod := range nodeInfo.PodsWithAffinity() {
393+
existingPodTopologyMaps, err := getMatchingAntiAffinityTopologyPairsOfPod(pod, existingPod, node)
394+
if err != nil {
395+
catchError(err)
396+
return
397+
}
398+
appendTopologyPairsMaps(existingPodTopologyMaps)
399+
}
400+
}
401+
workqueue.Parallelize(16, len(allNodeNames), processNode)
402+
return topologyMaps, firstError
403+
}
404+
405+
// getTPMapMatchingIncomingAffinityAntiAffinity finds existing Pods that match affinity terms of the given "pod".
406+
// It returns a topologyPairsMaps that are checked later by the affinity
407+
// predicate. With this topologyPairsMaps available, the affinity predicate does not
344408
// need to check all the pods in the cluster.
345-
func getPodsMatchingAffinity(pod *v1.Pod, nodeInfoMap map[string]*schedulercache.NodeInfo) (topologyPairsAffinityPodsMaps *topologyPairsMaps, topologyPairsAntiAffinityPodsMaps *topologyPairsMaps, err error) {
409+
func getTPMapMatchingIncomingAffinityAntiAffinity(pod *v1.Pod, nodeInfoMap map[string]*schedulercache.NodeInfo) (topologyPairsAffinityPodsMaps *topologyPairsMaps, topologyPairsAntiAffinityPodsMaps *topologyPairsMaps, err error) {
346410
allNodeNames := make([]string, 0, len(nodeInfoMap))
347411

348412
affinity := pod.Spec.Affinity
@@ -377,17 +441,13 @@ func getPodsMatchingAffinity(pod *v1.Pod, nodeInfoMap map[string]*schedulercache
377441
}
378442
}
379443

380-
affinityProperties, err := getAffinityTermProperties(pod, GetPodAffinityTerms(affinity.PodAffinity))
381-
if err != nil {
382-
return nil, nil, err
383-
}
384-
antiAffinityProperties, err := getAffinityTermProperties(pod, GetPodAntiAffinityTerms(affinity.PodAntiAffinity))
444+
affinityTerms := GetPodAffinityTerms(affinity.PodAffinity)
445+
affinityProperties, err := getAffinityTermProperties(pod, affinityTerms)
385446
if err != nil {
386447
return nil, nil, err
387448
}
388-
389-
affinityTerms := GetPodAffinityTerms(affinity.PodAffinity)
390449
antiAffinityTerms := GetPodAntiAffinityTerms(affinity.PodAntiAffinity)
450+
391451
processNode := func(i int) {
392452
nodeInfo := nodeInfoMap[allNodeNames[i]]
393453
node := nodeInfo.Node()
@@ -399,7 +459,7 @@ func getPodsMatchingAffinity(pod *v1.Pod, nodeInfoMap map[string]*schedulercache
399459
nodeTopologyPairsAntiAffinityPodsMaps := newTopologyPairsMaps()
400460
for _, existingPod := range nodeInfo.Pods() {
401461
// Check affinity properties.
402-
if podMatchesAffinityTermProperties(existingPod, affinityProperties) {
462+
if podMatchesAllAffinityTermProperties(existingPod, affinityProperties) {
403463
for _, term := range affinityTerms {
404464
if topologyValue, ok := node.Labels[term.TopologyKey]; ok {
405465
pair := topologyPair{key: term.TopologyKey, value: topologyValue}
@@ -408,8 +468,14 @@ func getPodsMatchingAffinity(pod *v1.Pod, nodeInfoMap map[string]*schedulercache
408468
}
409469
}
410470
// Check anti-affinity properties.
411-
if podMatchesAffinityTermProperties(existingPod, antiAffinityProperties) {
412-
for _, term := range antiAffinityTerms {
471+
for _, term := range antiAffinityTerms {
472+
namespaces := priorityutil.GetNamespacesFromPodAffinityTerm(pod, &term)
473+
selector, err := metav1.LabelSelectorAsSelector(term.LabelSelector)
474+
if err != nil {
475+
catchError(err)
476+
return
477+
}
478+
if priorityutil.PodMatchesTermsNamespaceAndSelector(existingPod, namespaces, selector) {
413479
if topologyValue, ok := node.Labels[term.TopologyKey]; ok {
414480
pair := topologyPair{key: term.TopologyKey, value: topologyValue}
415481
nodeTopologyPairsAntiAffinityPodsMaps.addTopologyPair(pair, existingPod)
@@ -425,7 +491,7 @@ func getPodsMatchingAffinity(pod *v1.Pod, nodeInfoMap map[string]*schedulercache
425491
return topologyPairsAffinityPodsMaps, topologyPairsAntiAffinityPodsMaps, firstError
426492
}
427493

428-
// podMatchesAffinity returns true if "targetPod" matches any affinity rule of
494+
// targetPodMatchesAffinityOfPod returns true if "targetPod" matches ALL affinity terms of
429495
// "pod". Similar to getPodsMatchingAffinity, this function does not check topology.
430496
// So, whether the targetPod actually matches or not needs further checks for a specific
431497
// node.
@@ -439,11 +505,11 @@ func targetPodMatchesAffinityOfPod(pod, targetPod *v1.Pod) bool {
439505
glog.Errorf("error in getting affinity properties of Pod %v", pod.Name)
440506
return false
441507
}
442-
return podMatchesAffinityTermProperties(targetPod, affinityProperties)
508+
return podMatchesAllAffinityTermProperties(targetPod, affinityProperties)
443509
}
444510

445-
// targetPodMatchesAntiAffinityOfPod returns true if "targetPod" matches any anti-affinity
446-
// rule of "pod". Similar to getPodsMatchingAffinity, this function does not check topology.
511+
// targetPodMatchesAntiAffinityOfPod returns true if "targetPod" matches ANY anti-affinity
512+
// term of "pod". Similar to getPodsMatchingAffinity, this function does not check topology.
447513
// So, whether the targetPod actually matches or not needs further checks for a specific
448514
// node.
449515
func targetPodMatchesAntiAffinityOfPod(pod, targetPod *v1.Pod) bool {
@@ -456,5 +522,5 @@ func targetPodMatchesAntiAffinityOfPod(pod, targetPod *v1.Pod) bool {
456522
glog.Errorf("error in getting anti-affinity properties of Pod %v", pod.Name)
457523
return false
458524
}
459-
return podMatchesAffinityTermProperties(targetPod, properties)
525+
return podMatchesAnyAffinityTermProperties(targetPod, properties)
460526
}

0 commit comments

Comments
 (0)