1. 程式人生 > >Kubernetes排程之親和性和反親和性

Kubernetes排程之親和性和反親和性

背景

Kubernetes中的排程策略可以大致分為兩種,一種是全域性的排程策略,要在啟動排程器時配置,包括kubernetes排程器自帶的各種predicates和priorities演算法;另一種是執行時排程策略,包括nodeAffinity(主機親和性),podAffinity(POD親和性)以及podAntiAffinity(POD反親和性)。

podAffinity 主要解決POD可以和哪些POD部署在同一個拓撲域中的問題(拓撲域用主機標籤實現,可以是單個主機,也可以是多個主機組成的cluster、zone等),podAntiAffinity主要解決POD不能和哪些POD部署在同一個拓撲域中的問題。它們處理的是Kubernetes叢集內部POD和POD之間的關係。

使用場景介紹

  • podAntiAffinity使用場景:

    將一個服務的POD分散在不同的主機或者拓撲域中,提高服務本身的穩定性。
    給POD對於一個節點的獨佔訪問許可權來保證資源隔離,保證不會有其它pod來分享節點資源。
    把可能會相互影響的服務的POD分散在不同的主機上。

對於親和性和反親和性,每種都有兩種規則可以設定:

  1. RequiredDuringSchedulingIgnoredDuringExecution:在排程期間要求滿足親和性或者反親和性規則,如果不能滿足規則,則POD不能被排程到對應的主機上。在之後的執行過程中,系統不會再檢查這些規則是否滿足。(硬規則)

  2. PreferredDuringSchedulingIgnoredDuringExecution:在排程期間儘量滿足親和性或者反親和性規則,如果不能滿足規則,POD也有可能被排程到對應的主機上。在之後的執行過程中,系統不會再檢查這些規則是否滿足。(軟規則)

使用示例

  • podAntiAffinity 使用示例:

使用hostname作為拓撲域,把pod建立在不同主機上,每個主機上最多隻有一個同類型的POD(同類型用標籤區分)。其中matchExpressions中填寫內容對應到RC中POD自身的標籤。可以通過修改需要匹配的標籤內容來控制把一個服務中的POD和其它服務的POD部署在不同主機上。

yaml中的定義如下:
...
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchLabels:
            Anti_wmonitorTest6: wmonitorTest
        topologyKey: kubernetes.io/hostname
...

上邊的例子中可以通過修改topologyKey來限制拓撲域的範圍,實現把相關服務部署在不同的容災域等其它功能。

具體實現過程

整個計算過程摘選自Kubernetes 1.7原始碼,為便於閱讀部分函式刪除了無關程式碼

  1. 對於待排程的Pod,首先分別判斷是否存在親和性限制和反親和性限制,若有都會呼叫processTerms方法,若為親和性則傳入引數multiplier = 1,若為反親和性則傳入引數multiplier = -1

    	if existingHasAffinityConstraints {
    		terms := existingPodAffinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution
    		pm.processTerms(terms, existingPod, pod, existingPodNode, 1)
    	}
    	if existingHasAntiAffinityConstraints {
    		terms := existingPodAffinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution
    		pm.processTerms(terms, existingPod, pod, existingPodNode, -1)
    	}
    
  2. 對於具體的processTerms方法會遍歷Pod設定的所有affinityTerm或者antiaffinityTerm(即比如某個Pod設定CPU密集型和IO密集型則是兩個term),針對每個具體antiaffinityTerm(或者affinityTerm)呼叫processTerm方法

    func (p *podAffinityPriorityMap) processTerms(terms []v1.WeightedPodAffinityTerm, podDefiningAffinityTerm, podToCheck *v1.Pod, fixedNode *v1.Node, multiplier int) {
    for i := range terms {
    	term := &terms[i]
    	p.processTerm(&term.PodAffinityTerm, podDefiningAffinityTerm, podToCheck, fixedNode, float64(term.Weight*int32(multiplier)))
    }
    }
    
  3. 帶排程的Pod的具體某種親和性選項(PodAffinityTerm)會將自身的權重(weight)加入一個key為主機名(node.Name)value為當前累計weight和的一個map(p.counts)中,該map可以看作單次優選時候的一個全域性的map

    func (p *podAffinityPriorityMap) processTerm(term *v1.PodAffinityTerm, podDefiningAffinityTerm, podToCheck *v1.Pod, fixedNode *v1.Node, weight float64) {
    	func() {
    		for _, node := range p.nodes {
    			if p.failureDomains.NodesHaveSameTopologyKey(node, fixedNode, term.TopologyKey) {
    				p.counts[node.Name] += weight
    			}
    		}
    	}()
    }
    
  4. 等到該待排程Pod計算完所有節點的親和性及反親和性Term的累計權重值後,開始為所有節點打分數,最終分數在0到10分之間

    func (ipa *InterPodAffinity) CalculateInterPodAffinityPriority {
    for _, node := range nodes {
    	if p.counts[node.Name] > maxCount {
    		maxCount = p.counts[node.Name]
    	}
    	if p.counts[node.Name] < minCount {
    		minCount = p.counts[node.Name]
    	}
    }
    for _, node := range nodes {
    	fScore := float64(0)
    	if (maxCount - minCount) > 0 {
    		fScore = 10 * ((p.counts[node.Name] - minCount) / (maxCount - minCount))
    	}
    }
    

總結

對於單個Pod,可以設定多個親和性項和多個反親和性項,每個親和性項和反親和性項自身都可以設定獨立的權重,然後一次親和性優選排程會將自身的所有親和項權重加入每個節點的累計權值(或將自身的所有反親和項權重減去每個節點的累計權值),然後對於每個節點所帶的累計權重算出一個0到10分的分數。

ref:Kubernetes排程之親和性和反親和性