K8S集羣調度原理及調度策略[轉]

k8s調度器Scheduler

Scheduler工作原理

請求及Scheduler調度步驟:

  • 節點預選(Predicate):排除完全不滿足條件的節點,如內存大小,端口等條件不滿足。
  • 節點優先級排序(Priority):根據優先級選出最佳節點
  • 節點擇優(Select):根據優先級選定節點
  1. 首先用戶通過 Kubernetes 客戶端 Kubectl 提交創建 Pod 的 Yaml 的文件,向Kubernetes 系統發起資源請求,該資源請求被提交到

  2. Kubernetes 系統中,用戶通過命令行工具 Kubectl 向 Kubernetes 集羣即 APIServer 用 的方式發送“POST”請求,即創建 Pod 的請求。

  3. APIServer 接收到請求後把創建 Pod 的信息存儲到 Etcd 中,從集羣運行那一刻起,資源調度系統 Scheduler 就會定時去監控 APIServer

  4. 通過 APIServer 得到創建 Pod 的信息,Scheduler 採用 watch 機制,一旦 Etcd 存儲 Pod 信息成功便會立即通知APIServer,

  5. APIServer會立即把Pod創建的消息通知Scheduler,Scheduler發現 Pod 的屬性中 Dest Node 爲空時(Dest Node=””)便會立即觸發調度流程進行調度。

  6. 而這一個創建Pod對象,在調度的過程當中有3個階段:節點預選、節點優選、節點選定,從而篩選出最佳的節點

    • 節點預選:基於一系列的預選規則對每個節點進行檢查,將那些不符合條件的節點過濾,從而完成節點的預選
    • 節點優選:對預選出的節點進行優先級排序,以便選出最合適運行Pod對象的節點
    • 節點選定:從優先級排序結果中挑選出優先級最高的節點運行Pod,當這類節點多於1個時,則進行隨機選擇

k8s的調用工作方式

Kubernetes調度器作爲集羣的大腦,在如何提高集羣的資源利用率、保證集羣中服務的穩定運行中也會變得越來越重要Kubernetes的資源分爲兩種屬性。

  1. 可壓縮資源(例如CPU循環,Disk I/O帶寬)都是可以被限制和被回收的,對於一個Pod來說可以降低這些資源的使用量而不去殺掉Pod。
  2. 不可壓縮資源(例如內存、硬盤空間)一般來說不殺掉Pod就沒法回收。未來Kubernetes會加入更多資源,如網絡帶寬,存儲IOPS的支持。

常用預選策略

預選策略 作用
CheckNodeCondition 檢查是否可以在節點報告磁盤、網絡不可用或未準備好時將Pod調度其上
HostName 如果Pod對象擁有spec.hostname屬性,則檢查節點名稱字符串是否和該屬性值匹配。
PodFitsHostPorts Pod的spec.hostPort屬性時,檢查端口是否被佔用
MatchNodeSelector Pod的spec.nodeSelector屬性時,檢查節點標籤
NoDiskConflict Pod依賴的存儲卷在此節點是否可用,默認沒有啓用
PodFitsResources 檢查節點上的資源(CPU、內存)可用性是否滿足Pod對象的運行需求。
PodToleratesNodeTaints Pod的spec.tolerations屬性,僅關注NoSchedule和NoExecute兩個效用標識的污點
PodToleratesNodeNoExecuteTaints Pod的spec.tolerations屬性,是否能接納節點的NoExecute類型污點,默認沒有啓用
CheckNodeLabelPresence 僅檢查節點上指定的所有標籤的存在性,默認沒有啓用
CheckServiceAffinity 將相同Service的Pod對象放置在同一個或同一類節點上以提高效率,默認沒有啓用
MaxEBSVolumeCount 檢查節點已掛載的EBS(亞馬遜彈性塊存儲)存儲卷數量是否超過設置的最大值,默認爲39
MaxGCEPDVolumeCount 檢查節點上已掛載的GCE PD(谷歌雲存儲) 存儲卷數量是否超過最大值,默認爲16
MaxAzureDiskVolumeCount 檢查節點上已掛載的Azure Disk存儲卷數量是否超過最大值,默認爲16
CheckVolumeBinding 檢查節點上已綁定和未綁定的PVC是否滿足需求
NoVolumeZoneConflict 在給定區域zone限制下,檢查此節點部署的Pod對象是否存在存儲卷衝突
CheckNodeMemoryPressure 檢查節點內存壓力,如果壓力過大,那就不會講pod調度至此
CheckPodePIDPressure 檢查節點PID資源壓力
CheckNodeDiskPressure 檢查節點磁盤資源壓力
MatchInterPodAffinity 檢查節點是否滿足Pod對象親和性或反親和性條件

常用優先函數

函數名稱 詳細說明
LeastRequestedPriority 節點的優先級就由節點空閒資源與節點總容量的比值,即由(總容量-節點上Pod的容量總和-新Pod的容量)/總容量)來決定。 CPU和內存具有相同權重,資源空閒比越高的節點得分越高。 cpu((capacity – sum(requested)) * 10 / capacity) + memory((capacity – sum(requested)) * 10 / capacity) / 2
BalancedResourceAllocation CPU和內存使用率越接近的節點權重越高,該策略不能單獨使用,必須和LeastRequestedPriority組合使用,儘量選擇在部署Pod後各項資源更均衡的機器。 如果請求的資源(CPU或者內存)需求大於節點的capacity,那麼該節點永遠不會被調度到。
InterPodAffinityPriority 通過迭代 weightedPodAffinityTerm 的元素計算和,並且如果對該節點滿足相應的PodAffinityTerm,則將 “weight” 加到和中,具有最高和的節點是最優選的。
SelectorSpreadPriority 爲了更好的容災,對同屬於一個service、replication controller或者replica的多個Pod副本,儘量調度到多個不同的節點上。 如果指定了區域,調度器則會盡量把Pod分散在不同區域的不同節點上。當一個Pod的被調度時,會先查找Pod對於的service或者replication controller, 然後查找service或replication controller中已存在的Pod,運行Pod越少的節點的得分越高。本質就是往運行同類pod少的節點上分配。
NodeAffinityPriority 親和性機制。Node Selectors(調度時將pod限定在指定節點上), 支持多種操作符(In, NotIn, Exists, DoesNotExist, Gt, Lt),而不限於對節點labels的精確匹配。 另外支持兩種類型的選擇器,一種是“hard(requiredDuringSchedulingIgnoredDuringExecution)”選擇器, 它保證所選的主機必須滿足所有Pod對主機的規則要求。 這種選擇器更像是之前的nodeselector,在nodeselector的基礎上增加了更合適的表現語法。 另一種是“soft(preferresDuringSchedulingIgnoredDuringExecution)”選擇器, 它作爲對調度器的提示,調度器會盡量但不保證滿足NodeSelector的所有要求。
NodePreferAvoidPodsPriority(權重1W) 如果 節點的 Anotation (註解信息)沒有設置 key-value:scheduler. alpha.kubernetes.io/ preferAvoidPods = "...",則節點對該 policy 的得分就是10分, 加上權重10000,那麼該node對該policy的得分至少10W分。如果Node的Anotation設置了, scheduler.alpha.kubernetes.io/preferAvoidPods = "..." ,如果該 pod 對應的 Controller 是 ReplicationController 或 ReplicaSet, 則該 node 對該 policy 的得分就是0分。
TaintTolerationPriority 使用 Pod 中 tolerationList 與 節點 Taint 列表項進行匹配,配對成功的項越多,則得分越低。污點越匹配,得分越低
ImageLocalityPriority 根據Node上是否存在一個pod的容器運行所需鏡像大小對優先級打分,分值爲0-10。遍歷全部Node, 如果某個Node上pod容器所需的鏡像一個都不存在,分值爲0; 如果Node上存在Pod容器部分所需鏡像,則根據滿足當前需求的鏡像的大小來決定分值,鏡像越大,分值就越高;如果Node上存在pod所需全部鏡像,分值爲10。默認沒有啓用
EqualPriority 是一個優先級函數,它給予所有節點相等權重。
MostRequestedPriority 在 ClusterAutoscalerProvider 中,替換 LeastRequestedPriority,給使用多資源的節點,更高的優先級。 計算公式爲: (cpu(10 sum(requested) / capacity) + memory(10 sum(requested) / capacity)) / 2默認沒有啓用

節點親和性調度

節點親和性規則:硬親和性 required 、軟親和性 preferred。

  • 硬親和性規則不滿足時,Pod會置於Pending狀態,軟親和性規則不滿足時,會選擇一個不匹配的節點
  • 當節點標籤改變而不再符合此節點親和性規則時,不會將Pod從該節點移出,僅對新建的Pod對象生效

節點硬親和性

requiredDuringSchedulingIgnoredDuringExecution

  • 方式一:Pod使用 spec.nodeSelector (基於等值關係)
  • 方式二:Pod使用 spec.affinity 支持matchExpressions屬性 (複雜標籤選擇機制)
  • 注意:所有的選擇都是通過主機名,或給 node打標籤後,再根據標籤選擇的
# 調度至 zone = foo 的節點
kubectl label nodes kube-node1 zone=foo
apiVersion: v1
kind: Pod
metadata:
  name: with-required-nodeaffinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:  # 定義硬親和性
        nodeSelectorTerms:
        - matchExpressions:   #集合選擇器
          - {key: zone,operator: In,values: ["foo"]}
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1

節點軟親和性

preferredDuringSchedulingIgnoredDuringExecution

  • 柔性控制邏輯,當條件不滿足時,能接受被編排於其他不符合條件的節點之上
  • 權重 weight 定義優先級,1-100 值越大優先級越高
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy-with-node-affinity
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: myapp-pod
      labels:
        app: myapp
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:   #節點軟親和性
          - weight: 60
            preference:
              matchExpressions:
              - {key: zone, operator: In, values: ["foo"]}
          - weight: 30
            preference:
              matchExpressions:
              - {key: ssd, operator: Exists, values: []}
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1

Pod資源親和調度

  • Pod對象間親和性,將一些Pod對象組織在相近的位置(同一節點、機架、區域、地區)
  • Pod對象間反親和性,將一些Pod在運行位置上隔開

調度器將第一個Pod放置於任何位置,然後與其有親和或反親和關係的Pod據此動態完成位置編排
# 基於MatchInterPodAffinity預選策略完成節點預選,基於InterPodAffinityPriority優選函數進行各節點的優選級評估

位置拓撲,定義"同一位置"

Pod硬親和調度

requiredDuringSchedulingIgnoredDuringExecution

Pod親和性描述一個Pod與具有某特徵的現存Pod運行位置的依賴關係;即需要事先存在被依賴的Pod對象

# 被依賴Pod
kubectl run tomcat -l app=tomcat --image tomcat:alpine
kubectl explain pod.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution.topologyKey
apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:  # 硬親和調度
      - labelSelector:
          matchExpressions:    #集合選擇器
          - {key: app, operator: In, values: ["tomcat"]}  # 選擇被依賴Pod
          # 上面意思是,當前pod要跟標籤爲app值爲tomcat的pod在一起
        topologyKey: kubernetes.io/hostname  # 根據挑選出的Pod所有節點的hostname作爲同一位置的判定
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1

Pod軟親和調度

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-with-preferred-pod-affinity
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: myapp
      labels:
        app: myapp
    spec:
      affinity:
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 80
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - {key: app, operator: In, values: ["cache"]}
              topologyKey: zone
          - weight: 20
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - {key: app, operator: In, values: ["db"]}
              topologyKey: zone
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1

Pod反親和調度

Pod反親和調度用於分散同一類應用,調度至不同的區域、機架或節點等
將 spec.affinity.podAffinity替換爲 spec.affinity.podAntiAffinity

反親和調度也分爲柔性約束和強制約束

apiVersion: v1
kind: Pod
metadata:
    name: pod-first
    labels: 
        app: myapp
        tier: fronted
spec:
    containers:
    - name: myapp
      image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
    name: pod-second
    labels:
        app: backend
        tier: db
spec:
    containers:
    - name: busybox
      image: busybox:latest
      imagePullPolicy: IfNotPresent
      command: ["/bin/sh", "-c", "sleep 3600"]
    affinity:
      podAntiAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchExpressions:
            - {key: app, operator: In, values: ["myapp"]}
          topologyKey: zone

污點和容忍度

污點 taints 是定義在節點上的鍵值型屬性數據,用於讓節點拒絕將Pod調度運行於其上,除非Pod有接納節點污點的容忍度容忍度 tolerations 是定義在Pod上的鍵值屬性數據,用於配置可容忍的污點,且調度器將Pod調度至其能容忍該節點污點的節點上或沒有污點的節點上

使用PodToleratesNodeTaints預選策略和TaintTolerationPriority優選函數完成該機制

  • 節點親和性使得Pod對象被吸引到一類特定的節點 (nodeSelector和affinity)
  • 污點提供讓節點排斥特定Pod對象的能力

定義污點和容忍度

污點定義於nodes.spec.taints容忍度定義於pods.spec.tolerations
語法: key=value:effect

effect定義排斥等級:

  • NoSchedule,不能容忍,但僅影響調度過程,已調度上去的pod不受影響,僅對新增加的pod生效。
  • PreferNoSchedule,柔性約束,節點現存Pod不受影響,如果實在是沒有符合的節點,也可以調度上來
  • NoExecute,不能容忍,當污點變動時,Pod對象會被驅逐

在Pod上定義容忍度時:

  1. 等值比較 容忍度與污點在key、value、effect三者完全匹配
  2. 存在性判斷 key、effect完全匹配,value使用空值

一個節點可配置多個污點,一個Pod也可有多個容忍度

管理節點的污點

同一個鍵值數據,effect不同,也屬於不同的污點

給節點添加污點:

kubectl taint node <node-name> <key>=<value>:<effect>
kubectl taint node node2 node-type=production:NoShedule  #舉例

查看節點污點:

kubectl get nodes <nodename> -o go-template={{.spec.taints}}

刪除節點污點:

kubectl taint node <node-name> <key>[:<effect>]-
kubectl patch nodes <node-name> -p '{"spec":{"taints":[]}}'

kubectl taint node kube-node1 node-type=production:NoSchedule
kubectl get nodes kube-node1 -o go-template={{.spec.taints}}
# 刪除key爲node-type,effect爲NoSchedule的污點
kubectl taint node kube-node1 node-type:NoSchedule-

# 刪除key爲node-type的所有污點
kubectl taint node kube-node1 node-type-

# 刪除所有污點
kubectl patch nodes kube-node1 -p '{"spec":{"taints":[]}}'

給Pod對象容忍度

spec.tolerations字段添加
tolerationSeconds用於定義延遲驅逐Pod的時長

# 等值判斷
tolerations:
- key: "key1"
  operator: "Equal"  #判斷條件爲Equal
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600
 
# 存在性判斷
tolerations:
- key: "key1"
  operator: "Exists"    #存在性判斷,只要污點鍵存在,就可以匹配
  effect: "NoExecute"
  tolerationSeconds: 3600
apiVersion: v1
kind: Deployment
metadata:
    name: myapp-deploy
    namespace: default
spec:
    replicas: 3
    selector:
        matchLabels:
            app: myapp
            release: canary
    template:
        metadata:
            labels:
                app: myapp
                release: canary
        spec:
            containers:
            - name: myapp
            image: ikubernetes/myapp:v1
            ports:
            - name: http
              containerPort: 80
            tolerations:
            - key: "node-type"
              operator: "Equal"
              value: "production":
              effect: "NoExecute"
              tolerationSeconds: 3600

問題節點標識

自動爲節點添加污點信息,使用NoExecute效用標識,會驅逐現有Pod
K8s核心組件通常都容忍此類污點

node.kubernetes.io/not-ready 節點進入NotReady狀態時自動添加
node.alpha.kubernetes.io/unreachable 節點進入NotReachable狀態時自動添加
node.kubernetes.io/out-of-disk 節點進入OutOfDisk狀態時自動添加
node.kubernetes.io/memory-pressure 節點內存資源面臨壓力
node.kubernetes.io/disk-pressure 節點磁盤面臨壓力
node.kubernetes.io/network-unavailable 節點網絡不可用
node.cloudprovider.kubernetes.io/uninitialized kubelet由外部雲環境程序啓動時,自動添加,待到去控制器初始化此節點時再將其刪除

Pod優選級和搶佔式調度

優選級,Pod對象的重要程度
優選級會影響節點上Pod的調度順序和驅逐次序
一個Pod對象無法被調度時,調度器會嘗試搶佔(驅逐)較低優先級的Pod對象,以便可以調度當前Pod

Pod優選級和搶佔機制默認處於禁用狀態
啓用:同時爲kube-apiserver、kube-scheduler、kubelet程序的 --feature-gates 添加 PodPriority=true
使用:
事先創建優先級類別,並在創建Pod資源時通過 priorityClassName屬性指定所屬的優選級類別

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章