6. kubernetes 資源和調(diào)度

6. kubernetes 資源和調(diào)度

一、資源配額與限制

資源配額用于管理命名空間(NameSpace)中對象使用的資源量,我們可以按 CPU 、內(nèi)存用量、對象數(shù)量來設(shè)置配額。通過資源配額,可以確保租戶不會使用超過其分配份額的集群資源。

資源配額的工作方式如下:

  • 管理員為每個 namespace 創(chuàng)建一個或多個資源配額對象
  • 用戶在 namespace 下創(chuàng)建資源 (pods、 services 等),同時配額系統(tǒng)會跟蹤使用情況,來確保其不超過資源配額中定義的硬性資源限額
  • 如果資源的創(chuàng)建或更新違反了配額約束,則請求會失敗,并返回 HTTP 狀態(tài)碼 403 FORBIDDEN,以及說明違反配額約束的信息
  • 如果 namespace 下的計算資源(如 cpu 和 memory)的配額被啟用,則用戶必須為這些資源設(shè)定請求值(request) 和約束值(limit),否則配額系統(tǒng)將拒絕 Pod 的創(chuàng)建。

Kubernetes 中主要有3個層級的資源配額控制:

  • 容器:可以對 CPU 和 Memory 進行限制
  • POD:可以對一個 Pod 內(nèi)所有容器的的資源進行限制
  • Namespace:為一個命名空間下的資源進行限制

其中容器層次主要利用容器本身的支持,比如 Docker 對 CPU、內(nèi)存等的支持;Pod 方面可以限制系統(tǒng)內(nèi)創(chuàng)建 Pod 的資源范圍,比如最大或者最小的 CPU、memory 需求;Namespace 層次就是對用戶級別的資源限額了,包括 CPU、內(nèi)存,還可以限定 Pod、RC、Service 的數(shù)量。

要使用資源配額的話需要確保 api server的 --enable-admission-plugins= 參數(shù)中包含 ResourceQuota,當 namespace 中存在一個 ResourceQuota 對象時,該 namespace 即開始實施資源配額的管理工作了,另外需要注意的是一個 namespace 中最多只應(yīng)存在一個 ResourceQuota 對象。

資源配額控制器支持的配額控制資源主要包括:計算資源配額、存儲資源配額、對象數(shù)量資源配額。

1. 計算資源配額

在namespace下我們可以針對性的設(shè)置計算資源配額,有兩種方式:ResourceQuotaLimitRange

資源名稱 資源類型 描述
limits.cpu cpu 所有容器的cpu 請求資源總和
limits.memory memory 所有容器的內(nèi)存 請求資源總和
requests.cpu cpu 所有容器的cpu 限制資源總和
requests.memory memory 所有容器的內(nèi)存 限制資源總和

在創(chuàng)建pod時,無論是 請求資源超出限制 或者是 限制資源超出限制,都無法創(chuàng)建成功?。?!

ResourceQuota

下面是一個演示案例

這里要注意的是:

我們在 resourcequota中 設(shè)勝多負少的置 cpu 和 memory 的格式 要和 pod 文件中 cpu、 memory 的格式保存統(tǒng)一。

比如: resourcequota memory 賦值時 我們是用的 "300Mi", 那么我們在pod 中配置 memory 時 也應(yīng)該用相同的格式 "xxMi"

# 首先我們新建一個namespace
kubectl create ns  tmp-quota

# 創(chuàng)建 計算資源配額 文件
vim quota-mem-cpu.yaml
#-------------------------------
apiVersion: v1
kind: ResourceQuota
metadata:
  name: quota-test1
  namespace: tmp-quota
spec:
  hard: # 硬件配額
    requests.cpu: "1"   # cpu 請求資源配額總和,這里有兩個單位 100m = 0.1 cpu;1000m cpu = 1 cpu; 精度1m;
    requests.memory: "300Mi"   # 內(nèi)存 請求資源配額總和,這里和我們平時使用的單位一樣,默認是 byte,也可以用 E、P、T、G、M、K為單位后綴或Ei、Pi...
    limits.cpu: "2"   # cpu 限制資源總和
    limits.memory: "600Mi" # 內(nèi)存 限制資源總和
#-------------------------------

# 運行
kubectl apply -f quota-mem-cpu.yaml
#---------------------------------
resourcequota/quota-test1 created
#---------------------------------

# 然后我們查看資源詳情
# kubectl describe quota 資源文件name屬性 -n namespace名稱
kubectl describe quota quota-test1 -n tmp-quota
#---------------------------------
Name:            quota-test1
Namespace:       tmp-quota
Resource         Used  Hard
--------         ----  ----
limits.cpu       0     2
limits.memory    0     600m
requests.cpu     0     1
requests.memory  0     300m
#---------------------------------

# 而后我們創(chuàng)建一個pod 并申請限額
vim quta-pod-test1.yaml
#---------------------------------
apiVersion: v1
kind: Pod
metadata:
  name: quta-pod-test1
  namespace: tmp-quota
spec:
  containers:
  - name: quota-mem-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        cpu: 1000m
        memory: 200M
      requests:
        cpu: 300m
        memory: 150M
#---------------------------------

# 啟動pod
kubectl apply -f quta-pod-test1.yaml

# 我們查看 資源占用情況
# 通過describe查詢
kubectl describe quota quota-test1 -n tmp-quota
#---------------------------------
Name:            quota-test1
Namespace:       tmp-quota
Resource         Used   Hard
--------         ----   ----
limits.cpu       1      2
limits.memory    200Mi  600Mi
requests.cpu     800m   1500m
requests.memory  150Mi  500Mi
#---------------------------------

# 通過 get resourcequota查詢
kubectl get resourcequota -n tmp-quota
#---------------------------------
NAME          AGE    REQUEST                                                  LIMIT
quota-test1   108m   requests.cpu: 800m/1500m, requests.memory: 150Mi/500Mi   limits.cpu: 1/2, limits.memory: 200Mi/600Mi
#---------------------------------

# 下面我們看一下不滿足資源限制的情況

# 我們新建一個 quta-pod-test2.yaml
vim quta-pod-test2.yaml
#-------------------------
apiVersion: v1
kind: Pod
metadata:
  name: quta-pod-test2
  namespace: tmp-quota
spec:
  containers:
  - name: quota-mem-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        cpu: "1000m"
        memory: "200Mi"
      requests:
        cpu: "800m"
        memory: "550Mi" # 這里我們的 request.memory 超過了 資源限制 "300Mi"
#-------------------------

# 創(chuàng)建pod
kubectl apply -f quta-pod-test2.yaml
# 報錯信息如下
#-------------------------
The Pod "quta-pod-test2" is invalid: spec.containers[0].resources.requests: Invalid value: "550Mi": must be less than or equal to memory limit
#-------------------------


ResourceQuota 用來限制 namespace 中所有的 Pod 占用的總的資源 request 和 limit

LimitRange

而實際場景中 我們會出現(xiàn)這樣的情況:

  • 我們要對單個容器進行配額的限制
  • 我們新建了一個namespace 并未他設(shè)置了配額,但是pod 并沒有配置request 和limit,這樣我們也無法成功創(chuàng)建pod。

使用LimitRange 我們就可以很好的解決上述問題。

LimitRange 用來限制 namespace 中 單個Pod 默認資源 request 和 limit

# 首先我們新建一個namespace
kubectl create ns tmp-quota-range

# 新建 limitRange 資源文件
vim quota-range.yaml
#-------------------------
apiVersion: v1
kind: LimitRange
metadata:
  name: quta-limit-range
  namespace: tmp-quota-range
spec:
  limits:
    - default:
        memory: "130Mi"   # default  limits.memory = 130Mi
        cpu: "600m"
      defaultRequest:
        cpu: "550m"
        memory: "110Mi"   # default request.memory = 110Mi
      max:
        cpu: "800m"
        memory: "200Mi"   # max memory = 200Mi
      min:
        cpu: "500m"
        memory: "100Mi"  # min memory = 100Mi
      type: Container
#-------------------------

# 生效配置
kubectl apply -f quota-range.yaml

# 然后我們查看 describe
kubectl describe limits quta-limit-range -n tmp-quota-range
#-------------------------
Name:       quta-limit-range
Namespace:  tmp-quota-range
Type        Resource  Min    Max    Default Request  Default Limit  Max Limit/Request Ratio
----        --------  ---    ---    ---------------  -------------  -----------------------
Container   cpu       500m   800m   550m             600m           -
Container   memory    100Mi  200Mi  110Mi            130Mi          -
#-------------------------


# 新建一個pod.yaml
vim quta-pod-test3.yaml
#-------------------
apiVersion: v1
kind: Pod
metadata:
  name: quta-pod-test3
  namespace: tmp-quota-range
spec:
  containers:
  - name: quota-mem-cpu-demo-ctr2
    image: nginx
#------------------------

# 運行
kubectl apply -f quta-pod-test3.yaml

# 然后我們檢查pod describe
kubectl describe pod quta-pod-test3 -n tmp-quota-range
#------------------------
Name:         quta-pod-test3
Namespace:    tmp-quota-range
Priority:     0
Node:         k8s-slave03/192.168.56.107
Start Time:   Tue, 05 Jul 2022 15:29:46 +0800
Labels:       <none>
Annotations:  cni.projectcalico.org/containerID: bd8322be8529794f3350dcd4ecc5aa579e496ca384a2bfbc990c0f14c768bf55
              cni.projectcalico.org/podIP: 10.244.211.196/32
              cni.projectcalico.org/podIPs: 10.244.211.196/32
              kubernetes.io/limit-ranger:
                LimitRanger plugin set: cpu, memory request for container quota-mem-cpu-demo-ctr2; cpu, memory limit for container quota-mem-cpu-demo-ctr2
Status:       Running
IP:           10.244.211.196
IPs:
  IP:  10.244.211.196
Containers:
  quota-mem-cpu-demo-ctr2:
    Container ID:   docker://68ece5793c03b4d7aae8a37977cdb389509ec8e24b3f10fb7be498d691866efd
    Image:          nginx
    Image ID:       docker-pullable://nginx@sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Tue, 05 Jul 2022 15:29:48 +0800
    Ready:          True
    Restart Count:  0
    # 看這里,pod 使用了默認的 limit 和 request 配置 ---------------------
    Limits:
      cpu:     600m
      memory:  130Mi
    Requests:
      cpu:        550m
      memory:     110Mi
     # --------------------------------------------------------------  
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-plgww (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  kube-api-access-plgww:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   Burstable
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  53s   default-scheduler  Successfully assigned tmp-quota-range/quta-pod-test3 to k8s-slave03
  Normal  Pulling    52s   kubelet            Pulling image "nginx"
  Normal  Pulled     51s   kubelet            Successfully pulled image "nginx" in 927.414978ms
  Normal  Created    51s   kubelet            Created container quota-mem-cpu-demo-ctr2
  Normal  Started    51s   kubelet            Started container quota-mem-cpu-demo-ctr2
#------------------------

2. 存儲資源配額

PersistentVolume (PV) 是外部存儲系統(tǒng)中的一塊存儲空間,由管理員創(chuàng)建和維護。與 Volume 一樣,PV 具有持久性,生命周期獨立于 Pod。

PersistentVolumeClaim (PVC) 是對 PV 的申請 (Claim)。PVC 通常由普通用戶創(chuàng)建和維護。需要為 Pod 分配存儲資源時,用戶可以創(chuàng)建一個 PVC,指明存儲資源的容量大小和訪問模式(比如只讀)等信息,Kubernetes 會查找并提供滿足條件的 PV。

有了 PersistentVolumeClaim,用戶只需要告訴 Kubernetes 需要什么樣的存儲資源,而不必關(guān)心真正的空間從哪里分配,如何訪問等底層細節(jié)信息。這些 Storage Provider 的底層信息交給管理員來處理,只有管理員才應(yīng)該關(guān)心創(chuàng)建 PersistentVolume 的細節(jié)信息。

資源名稱 描述
requests.storage 所有的pvc中,存儲資源的需求不能超過該值
persistentvolumeclaims namespace中所允許的pvc總量
<storage-class-name>.storageclass.storage.k8s.io/requests.storage 所有該storage-class-name相關(guān)的pvc中,存儲資源的需求不能超過改值
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims namespace中所允許的該storage-class-name相關(guān)的pvc的總量

這里我們簡單帶過,只演示下核心的資源文件,使用方法和 計算資源配額 類似

# 不管多少個PVC,現(xiàn)在只要pvc總和請求量超過10G,就不會讓其創(chuàng)建了
[root@master volume]# cat storage-resource.yaml 
apiVersion: v1
kind: ResourceQuota
metadata:
  name: storage-resources
  namespace: dev1
spec:
  hard:
    requests.storage: "10G" # 設(shè)置pvc 10G
    # managed-nfs-storage.storageclass.storage.k8s.io/requests.storage: "5G"
[root@master volume]# kubectl apply -f storage-resource.yaml 
resourcequota/storage-resources created
[root@master volume]# kubectl get quota -n dev1
NAME                AGE   REQUEST                   LIMIT
storage-resources   12s   requests.storage: 0/10G   
 
 
[root@master volume]# cat pvc-nfs-dy.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
  namespace: dev1
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests: 
      storage: 11Gi  
  storageClassName: managed-nfs-storage
 
[root@master volume]# kubectl apply -f  pvc-nfs-dy.yaml 
Error from server (Forbidden): error when creating "pvc-nfs-dy.yaml": persistentvolumeclaims "nfs-pvc" is forbidden: exceeded quota: storage-resources, requested: requests.storage=11Gi, used: requests.storage=0, limited: requests.storage=10G

3. 對象數(shù)量資源配額

這個就比較好理解了,我們可以限制 pods 、deployment、 services configmaps 等的數(shù)量

apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
  namespace: test
spec:
hard:
  pods: "10"
  count/deployments.apps: "3"
  count/services: "3"

二、資源調(diào)度

在k8s上有一個非常重要的組件kube-scheduler,它主要作用是監(jiān)聽apiserver上的pod資源中的nodename字段是否為空,如果該字段為空就表示對應(yīng)pod還沒有被調(diào)度,此時kube-scheduler就會從k8s眾多節(jié)點中,根據(jù)pod資源的定義相關(guān)屬性,從眾多節(jié)點中挑選一個最佳運行pod的節(jié)點,并把對應(yīng)主機名稱填充到對應(yīng)pod的nodename字段,然后把pod定義資源存回apiserver;此時apiserver就會根據(jù)pod資源上的nodename字段中的主機名,通知對應(yīng)節(jié)點上的kubelet組件來讀取對應(yīng)pod資源定義,kubelet從apiserver讀取對應(yīng)pod資源定義清單,根據(jù)資源清單中定義的屬性,調(diào)用本地docker把對應(yīng)pod運行起來;然后把pod狀態(tài)反饋給apiserver,由apiserver把對應(yīng)pod的狀態(tài)信息存回etcd中;整個過程,kube-scheduler主要作用是調(diào)度pod,并把調(diào)度信息反饋給apiserver,那么問題來了,kube-scheduler它是怎么評判眾多節(jié)點哪個節(jié)點最適合運行對應(yīng)pod的呢?

在k8s上調(diào)度器的工作邏輯是根據(jù)調(diào)度算法來實現(xiàn)對應(yīng)pod的調(diào)度的;不同的調(diào)度算法,調(diào)度結(jié)果也有所不同,其評判的標準也有所不同,當調(diào)度器發(fā)現(xiàn)apiserver上有未被調(diào)度的pod時,它會把k8s上所有節(jié)點信息,挨個套進對應(yīng)的預(yù)選策略函數(shù)中進行篩選,把不符合運行pod的節(jié)點淘汰掉,我們把這個過程叫做調(diào)度器的預(yù)選階段(Predicate);剩下符合運行pod的節(jié)點會進入下一個階段優(yōu)選(Priority),所謂優(yōu)選是在這些符合運行pod的節(jié)點中根據(jù)各個優(yōu)選函數(shù)的評分,最后把每個節(jié)點通過各個優(yōu)選函數(shù)評分加起來,選擇一個最高分,這個最高分對應(yīng)的節(jié)點就是調(diào)度器最后調(diào)度結(jié)果,如果最高分有多個節(jié)點,此時調(diào)度器會從最高分相同的幾個節(jié)點隨機挑選一個節(jié)點當作最后運行pod的節(jié)點;我們把這個這個過程叫做pod選定過程(select);簡單講調(diào)度器的調(diào)度過程會通過三個階段,第一階段是預(yù)選階段,此階段主要是篩選不符合運行pod節(jié)點,并將這些節(jié)點淘汰掉;第二階段是優(yōu)選,此階段是通過各個優(yōu)選函數(shù)對節(jié)點評分,篩選出得分最高的節(jié)點;第三階段是節(jié)點選定,此階段是從多個高分節(jié)點中隨機挑選一個作為最終運行pod的節(jié)點;大概過程如下圖所示


image.png

提示:預(yù)選過程是一票否決機制,只要其中一個預(yù)選函數(shù)不通過,對應(yīng)節(jié)點則直接被淘汰;剩下通過預(yù)選的節(jié)點會進入優(yōu)選階段,此階段每個節(jié)點會通過對應(yīng)的優(yōu)選函數(shù)來對各個節(jié)點評分,并計算每個節(jié)點的總分;最后調(diào)度器會根據(jù)每個節(jié)點的最后總分來挑選一個最高分的節(jié)點,作為最終調(diào)度結(jié)果;如果最高分有多個節(jié)點,此時調(diào)度器會從對應(yīng)節(jié)點集合中隨機挑選一個作為最后調(diào)度結(jié)果,并把最后調(diào)度結(jié)果反饋給apiserver;

影響調(diào)度的因素:

  • nodeName
  • nodeSelector: 選擇節(jié)點
  • nodeAffinity:節(jié)點親和性
  • podAffinity:pod親和性
  • taints:污點
  • tolerations:容忍

解析

集群調(diào)度原理

Scheduler調(diào)度步驟
  1. 首先用戶在通過 Kubernetes 客戶端 Kubectl 提交創(chuàng)建 Pod 的 Yaml 的文件,向Kubernetes 系統(tǒng)發(fā)起資源請求,該資源請求被提交到Kubernetes 系統(tǒng)。

  2. Kubernetes 系統(tǒng)中,用戶通過命令行工具 Kubectl 向 Kubernetes 集群即用API Server 的方式發(fā)送“POST”請求,即創(chuàng)建 Pod 的請求。

  3. API Server 接收到請求后把創(chuàng)建 Pod 的信息存儲到 Etcd 中,從集群運行那一刻起,資源調(diào)度系統(tǒng)Scheduler 就會定時去監(jiān)控 API Server

  4. 通過 API Server 得到創(chuàng)建 Pod 的信息,Scheduler 采用 watch 機制,一旦 Etcd 存儲 Pod 信息成功便會立即通知API Server

  5. API Server 會立即把Pod創(chuàng)建的消息通知Scheduler,Scheduler發(fā)現(xiàn) Pod 的屬性中 Dest Node 為空時(Dest Node="")便會立即觸發(fā)調(diào)度流程進行調(diào)度。

  6. 而這一個創(chuàng)建Pod對象,在調(diào)度的過程當中有3個階段:節(jié)點預(yù)選、節(jié)點優(yōu)選、節(jié)點選定,從而篩選出最佳的節(jié)點

    • 節(jié)點預(yù)選:基于一系列的預(yù)選規(guī)則對每個節(jié)點進行檢查,將那些不符合條件的節(jié)點過濾,從而完成節(jié)點的預(yù)選
    • 節(jié)點優(yōu)選:對預(yù)選出的節(jié)點進行優(yōu)先級排序,以便選出最合適運行Pod對象的節(jié)點
    • 節(jié)點選定:從優(yōu)先級排序結(jié)果中挑選出優(yōu)先級最高的節(jié)點運行Pod,當這類節(jié)點多于1個時,則進行隨機選擇
集群調(diào)度策略

Kubernetes調(diào)度器作為集群的大腦,在如何提高集群的資源利用率、保證集群中服務(wù)的穩(wěn)定運行中也會變得越來越重要Kubernetes的資源分為兩種屬性。

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

集群調(diào)度特別復(fù)雜,有各種各樣的規(guī)則。調(diào)度器會根據(jù)各種規(guī)則計算出一個分數(shù),根據(jù)分數(shù)的大小來選擇節(jié)點。

常用預(yù)選策略
預(yù)選策略 作用
CheckNodeCondition 檢查節(jié)點網(wǎng)絡(luò)、磁盤等是否正常
HostName 如果Pod對象擁有spec.hostname屬性,則檢查節(jié)點名
稱字符串是否和該屬性值匹配。
PodFitsHostPorts Pod的spec.hostPort屬性時,檢查端口是否被占用
MatchNodeSelector Pod的spec.nodeSelector屬性時,檢查節(jié)點標簽
NoDiskConflict Pod依賴的存儲卷在此節(jié)點是否可用
PodFitsResources 檢查節(jié)點上的資源(CPU、內(nèi)存)可用性是否滿足Pod對
象的運行需求。
PodToleratesNodeTaints Pod的spec.tolerations屬性,僅關(guān)注NoSchedule和
NoExecute兩個效用標識的污點
PodToleratesNodeNoExecuteTaints Pod的spec.tolerations屬性,是否能接納節(jié)點的
NoExecute類型污點,默認沒有啟用
CheckNodeLabelPresence 僅檢查節(jié)點上指定的所有標簽的存在性,默認沒有啟用
CheckServiceAffinity 將相同Service的Pod對象放置在同一個或同一類節(jié)點上
以提高效率,默認沒有啟用
MaxEBSVolumeCount 檢查節(jié)點已掛載的EBS(亞馬遜彈性塊存儲)存儲卷數(shù)量是
否超過設(shè)置的最大值,默認為39
MaxGCEPDVolumeCount 檢查節(jié)點上已掛載的GCE PD(谷歌云存儲) 存儲卷數(shù)量
是否超過最大值,默認為16
MaxAzureDiskVolumeCount 檢查節(jié)點上已掛載的Azure Disk存儲卷數(shù)量是否超過最
大值,默認為16
CheckVolumeBinding 檢查節(jié)點上已綁定和未綁定的PVC是否滿足需求
NoVolumeZoneConflict 在給定區(qū)域zone限制下,檢查此節(jié)點部署的Pod對象是
否存在存儲卷沖突
CheckNodeMemoryPressure 檢查節(jié)點內(nèi)存壓力,如果壓力過大,那就不會講pod調(diào)度
至此
CheckPodePIDPressure 檢查節(jié)點PID資源壓力
CheckNodeDiskPressure 檢查節(jié)點磁盤資源壓力
MatchInterPodAffinity 檢查節(jié)點是否滿足Pod對象親和性或反親和性條件
常用優(yōu)先函數(shù)
函數(shù)名稱 說明
LeastRequestedPriority 節(jié)點的優(yōu)先級就由節(jié)點空閑資源與節(jié)點總?cè)萘康谋戎?,即由(總?cè)萘?節(jié)點上
Pod的容量總和-新Pod的容量)/總?cè)萘浚﹣頉Q定。 CPU和內(nèi)存具有相同權(quán)
重,資源空閑比越高的節(jié)點得分越高。 cpu((capacity –
sum(requested)) * 10 / capacity) + memory((capacity –
sum(requested)) * 10 / capacity) / 2
BalancedResourceAllocation CPU和內(nèi)存使用率越接近的節(jié)點權(quán)重越高,該策略不能單獨使用,必須和
LeastRequestedPriority組合使用,盡量選擇在部署Pod后各項資源更均衡
的機器。 如果請求的資源(CPU或者內(nèi)存)需求大于節(jié)點的capacity,那么
該節(jié)點永遠不會被調(diào)度到。
InterPodAffinityPriority 通過迭代 weightedPodAffinityTerm 的元素計算和,并且如果對該節(jié)點滿足
相應(yīng)的PodAffinityTerm,則將 “weight” 加到和中,具有最高和的節(jié)點是最
優(yōu)選的。
SelectorSpreadPriority 為了更好的容災(zāi),對同屬于一個service、replication controller或者replica
的多個Pod副本,盡量調(diào)度到多個不同的節(jié)點上。 如果指定了區(qū)域,調(diào)度器
則會盡量把Pod分散在不同區(qū)域的不同節(jié)點上。當一個Pod的被調(diào)度時,會
先查找Pod對于的service或者replication controller, 然后查找service或
replication controller中已存在的Pod,運行Pod越少的節(jié)點的得分越高。本
質(zhì)就是往運行同類pod少的節(jié)點上分配。
NodeAffinityPriority 親和性機制。Node Selectors(調(diào)度時將pod限定在指定節(jié)點上), 支持多
種操作符(In, NotIn, Exists, DoesNotExist, Gt, Lt),而不限于對節(jié)點labels
的精確匹配。 另外支持兩種類型的選擇器,一種是
“hard(requiredDuringSchedulingIgnoredDuringExecution)”選擇器,
它保證所選的主機必須滿足所有Pod對主機的規(guī)則要求。 這種選擇器更像是
之前的nodeselector,在nodeselector的基礎(chǔ)上增加了更合適的表現(xiàn)語法。
另一種是“soft(preferresDuringSchedulingIgnoredDuringExecution)”選
擇器, 它作為對調(diào)度器的提示,調(diào)度器會盡量但不保證滿足NodeSelector的
所有要求。
NodePreferAvoidPodsPriority(權(quán)重1W) 如果 節(jié)點的 Anotation (注解信息)沒有設(shè)置 key-value:scheduler.
alpha.kubernetes.io/ preferAvoidPods = "...",則節(jié)點對該 policy 的得分
就是10分, 加上權(quán)重10000,那么該node對該policy的得分至少10W分。如
果Node的Anotation設(shè)置了,
scheduler.alpha.kubernetes.io/preferAvoidPods = "..." ,如果該 pod 對應(yīng)
的 Controller 是 ReplicationController 或 ReplicaSet, 則該 node 對該
policy 的得分就是0分。
TaintTolerationPriority 使用 Pod 中 tolerationList 與 節(jié)點 Taint 列表項進行匹配,配對成功的項越
多,則得分越低。污點越匹配,得分越低
ImageLocalityPriority 根據(jù)Node上是否存在一個pod的容器運行所需鏡像大小對優(yōu)先級打分,分值
為0-10。遍歷全部Node, 如果某個Node上pod容器所需的鏡像一個都不存
在,分值為0; 如果Node上存在Pod容器部分所需鏡像,則根據(jù)滿足當前需
求的鏡像的大小來決定分值,鏡像越大,分值就越高;如果Node上存在pod
所需全部鏡像,分值為10。默認沒有啟用
EqualPriority 是一個優(yōu)先級函數(shù),它給予所有節(jié)點相等權(quán)重。
MostRequestedPriority 在 ClusterAutoscalerProvider 中,替換 LeastRequestedPriority,給使用
多資源的節(jié)點,更高的優(yōu)先級。 計算公式為: (cpu(10 sum(requested)
/ capacity) + memory(10 sum(requested) / capacity)) / 2 默認沒
有啟用

1. label

label是kubernetes核心概念之一,以key / value 的形式附加到各種對象上,如Pod、Service、Deployment、Node等。達到識別對象,關(guān)聯(lián)關(guān)系等目的。

查看 label

# 通常我們在kubectl 命令上追加`--show-labels` 命令即可
# 下面我們演示一下常用對象的label 查看情況

# 查看node label
kubectl get node --show-labels
#--------------------------
NAME           STATUS                     ROLES                  AGE     VERSION   LABELS
k8s-master01   Ready,SchedulingDisabled   control-plane,master   7d16h   v1.23.8   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master01,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
k8s-slave02    Ready                      <none>                 6d22h   v1.23.8   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-slave02,kubernetes.io/os=linux
k8s-slave03    Ready                      <none>                 6d21h   v1.23.8   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-slave03,kubernetes.io/os=linux
#--------------------------

# 查看 pods label
kubectl get pods --show-labels
#--------------------------

NAME                               READY   STATUS    RESTARTS   AGE     LABELS
demonsetdemo-bgtgc                 1/1     Running   0          40h     app=demonsetdemo,controller-revision-hash=84978464f6,pod-template-generation=1
demonsetdemo-xnnf6                 1/1     Running   0          40h     app=demonsetdemo,controller-revision-hash=84978464f6,pod-template-generation=1
deploymentdemo1-5bc649f558-92bc9   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-fwhdr   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-gmpzz   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-pzvmd   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-qtt4r   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-slg5r   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-vskqr   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-xtp9s   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-xwt2s   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-z5xhr   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
nginx-app-74d589986c-bdf66         1/1     Running   0          5d15h   app=nginx,pod-template-hash=74d589986c
nginx-app-74d589986c-gfmsd         1/1     Running   0          5d15h   app=nginx,pod-template-hash=74d589986c
nginx-app-74d589986c-kdq6r         1/1     Running   0          5d15h   app=nginx,pod-template-hash=74d589986c
nginx-app-74d589986c-p4mbl         1/1     Running   0          5d16h   app=nginx,pod-template-hash=74d589986c
nginx-app-74d589986c-slszp         1/1     Running   0          5d15h   app=nginx,pod-template-hash=74d589986c
#--------------------------

# 查看deployment label
kubectl get deployment --show-labels
#--------------------------
NAME              READY   UP-TO-DATE   AVAILABLE   AGE     LABELS
deploymentdemo1   10/10   10           10          45h     app=deploymentdemo1
nginx-app         5/5     5            5           5d16h   <none>
#--------------------------

# 查看service label
kubectl get service --show-labels
#--------------------------
NAME                      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE     LABELS
deploymentdemo1-service   NodePort    10.222.58.200    <none>        8002:32002/TCP   44h     app=deploymentdemo1
kubernetes                ClusterIP   10.222.0.1       <none>        443/TCP          7d16h   component=apiserver,provider=kubernetes
nginx-app-service         NodePort    10.222.191.172   <none>        8001:32001/TCP   4d21h   app=nginx-app
#--------------------------

# 查看namespace label
kubectl get ns --show-labels
#--------------------------
NAME                   STATUS   AGE     LABELS
default                Active   7d17h   kubernetes.io/metadata.name=default
kube-node-lease        Active   7d17h   kubernetes.io/metadata.name=kube-node-lease
kube-public            Active   7d17h   kubernetes.io/metadata.name=kube-public
kube-system            Active   7d17h   kubernetes.io/metadata.name=kube-system
kubernetes-dashboard   Active   7d15h   kubernetes.io/metadata.name=kubernetes-dashboard
tmp-quota              Active   19h     kubernetes.io/metadata.name=tmp-quota
tmp-quota-range        Active   17h     kubernetes.io/metadata.name=tmp-quota-range
#--------------------------

設(shè)置 label

# 我們先選擇一個pods 節(jié)點
# NAME                               READY   STATUS    RESTARTS   AGE     LABELS
# nginx-app-74d589986c-slszp         1/1     Running   0          5d16h   app=nginx,pod-template-hash=74d589986c

# 然后我們對該節(jié)點,設(shè)置label
# 語法: kubectl label 對象類型 對象名稱 key=value
kubectl label pods nginx-app-74d589986c-slszp myCustomLabel=abc123
#---------------------------
pod/nginx-app-74d589986c-slszp labeled
#---------------------------

# 我們查詢驗證下
kubectl get pods --show-labels
#---------------------------
NAME                               READY   STATUS    RESTARTS   AGE     LABELS
nginx-app-74d589986c-slszp         1/1     Running   0          5d16h   app=nginx,myCustomLabel=abc123,pod-template-hash=74d589986c
#---------------------------

# 我們發(fā)現(xiàn)在 LABELS 項中 多了 我們自定義添加的這一項`myCustomLabel=abc123`

修改 label

# 修改label 與 設(shè)置label 一樣,只需要在 最后加上`--overwrite`即可
kubectl label pods nginx-app-74d589986c-slszp myCustomLabel=abc456 --overwrite

# 修改后
kubectl get pods --show-labels
#---------------------------
NAME                               READY   STATUS    RESTARTS   AGE     LABELS
nginx-app-74d589986c-slszp         1/1     Running   0          5d16h   app=nginx,myCustomLabel=abc456,pod-template-hash=74d589986c
#---------------------------

刪除 label

# 刪除label 也很簡單,我們只要把 label 的value替換成`-`即可
kubectl label pods nginx-app-74d589986c-slszp myCustomLabel-
#---------------------------
pod/nginx-app-74d589986c-slszp unlabeled
#---------------------------

# 驗證
kubectl get pods --show-labels
#---------------------------
nginx-app-74d589986c-slszp         1/1     Running   0          5d16h   app=nginx,pod-template-hash=74d589986c
#---------------------------

2. nodeName

nodeName是節(jié)點選擇約束的最簡單形式,但是由于其限制,通常很少使用它。nodeName是PodSpec的領(lǐng)域。

正常調(diào)度我們是不會在 master 節(jié)點上創(chuàng)建pod的

<font color=red size=4>pod.spec.nodeName將Pod直接調(diào)度到指定的Node節(jié)點上(包括master),會【跳過Scheduler的調(diào)度策略】,該匹配規(guī)則是【強制】匹配??梢栽竭^Taints污點進行調(diào)度。</font>

nodeName用于選擇節(jié)點的一些限制是:

  • 如果指定的節(jié)點不存在,則容器將不會運行,并且在某些情況下可能會自動刪除。
  • 如果指定的節(jié)點沒有足夠的資源來容納該Pod,則該Pod將會失敗,并且其原因?qū)⒈恢赋?,例如OutOfmemory或OutOfcpu。
  • 云環(huán)境中的節(jié)點名稱并非總是可預(yù)測或穩(wěn)定的。
  • 如果指定的節(jié)點不存在,容器不會運行,一直處于Pending狀態(tài)

資源配置文件

vim scheduler_node-name.yml
#---------------------------
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-name-test
  labels:
    app: node-name-test
spec:
  replicas: 3
  template:
    metadata:
      name: node-name-test
      labels:
        app: node-name-test
    spec:
      containers:
        - name: node-name-test
          image: nginx
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
      nodeName: k8s-master01 # 指定node節(jié)點
  selector:
    matchLabels:
      app: node-name-test
#---------------------------
# 執(zhí)行
kubectl apply -f scheduler_node-name.yml

# 查詢驗證
kubectl get pods -o wide
#---------------------------

NAME                               READY   STATUS    RESTARTS   AGE     IP               NODE           NOMINATED NODE   READINESS GATES
node-name-test-7fd854bfc9-4l2kd    1/1     Running   0          12s     10.244.32.135    k8s-master01   <none>           <none>
node-name-test-7fd854bfc9-hgvzf    1/1     Running   0          12s     10.244.32.136    k8s-master01   <none>           <none>
node-name-test-7fd854bfc9-vr995    1/1     Running   0          12s     10.244.32.134    k8s-master01   <none>           <none>
#---------------------------


3. nodeSelector

nodeSelector是節(jié)點選擇約束的最簡單推薦形式。nodeSelector是PodSpec的領(lǐng)域。它指定鍵值對的映射(label)。

<font color=red size=4>Pod.spec.nodeSelector是通過Kubernetes的label-selector機制選擇調(diào)度到那個節(jié)點??梢詾橐慌鷑ode節(jié)點打上指定的標簽,由調(diào)度器去匹配符合的標簽,并讓Pod調(diào)度到這些節(jié)點,該匹配規(guī)則屬于【強制】約束。由于是調(diào)度器調(diào)度,因此不能越過Taints污點進行調(diào)度。</font>

資源配置文件

# 我們先簡述下操作流程:
# 演示業(yè)務(wù)場景: 我們把磁盤io高的服務(wù),部署到有ssd的node節(jié)點上。
# > step 1: 我們針對node `k8s-slave02` 設(shè)置label,標識當前節(jié)點是 ssd
# > step 2: 我們創(chuàng)建deployment資源文件,并指定`nodeSelector=ssd`
# > step 3: 查詢驗證,是否所有的pod 都運行在 node `k8s-slave02`上

# step 1
kubectl label nodes k8s-slave02 disk=ssd
# 驗證
kubectl get nodes --show-labels
# ---------------------
NAME           STATUS                     ROLES                  AGE     VERSION   LABELS
k8s-master01   Ready,SchedulingDisabled   control-plane,master   7d22h   v1.23.8   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master01,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
k8s-slave02    Ready                      <none>                 7d3h    v1.23.8   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disk=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-slave02,kubernetes.io/os=linux
k8s-slave03    Ready                      <none>                 7d2h    v1.23.8   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-slave03,kubernetes.io/os=linux
# ---------------------

# step 2
vim scheduler_node-selector.yml
# ---------------------
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-selector-test
  labels:
    app: node-selector-test
spec:
  replicas: 5
  template:
    metadata:
      name: node-selector-test
      labels:
        app: node-selector-test
    spec:
      containers:
        - name: node-selector-test
          image: nginx
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
      # nodeSelector 選擇 disk = ssd
      nodeSelector:
        disk: ssd
  selector:
    matchLabels:
      app: node-selector-test
# ---------------------

# 執(zhí)行
kubectl apply -f scheduler_node-selector.yml

# step 3
kubectl get pods -o wide
# ---------------------

NAME                                  READY   STATUS    RESTARTS   AGE     IP               NODE          NOMINATED NODE   READINESS GATES
node-selector-test-8485477cbf-492lg   1/1     Running   0          11s     10.244.220.252   k8s-slave02   <none>           <none>
node-selector-test-8485477cbf-8vgx8   1/1     Running   0          11s     10.244.220.250   k8s-slave02   <none>           <none>
node-selector-test-8485477cbf-d2gwf   1/1     Running   0          11s     10.244.220.251   k8s-slave02   <none>           <none>
node-selector-test-8485477cbf-nzmbp   1/1     Running   0          11s     10.244.220.249   k8s-slave02   <none>           <none>
node-selector-test-8485477cbf-r8bk8   1/1     Running   0          11s     10.244.220.248   k8s-slave02   <none>           <none>
# ---------------------

# 驗證通過,我們發(fā)現(xiàn)所有的 pod 都運行在了 ssd 節(jié)點 node `k8s-slave02` 上

4. nodeAffinity 親和性

nodeAffinity 比 nodeSelector 更靈活。它可以在nodeSelector的基礎(chǔ)上進行一些簡單的邏輯組合。不只是簡單的匹配。調(diào)度可以分成軟策略硬策略兩種。其中邏輯支持操作符

節(jié)點親和性規(guī)則

required(硬親和性,硬策略,不能商量,必須執(zhí)行) 、preferred(軟親和性,軟策略,可以商量,選擇執(zhí)行)。

  • 硬親和性規(guī)則不滿足時,Pod會置于Pending狀態(tài),軟親和性規(guī)則不滿足時,會選擇一個不匹配的節(jié)點

  • 當節(jié)點標簽改變而不再符合此節(jié)點親和性規(guī)則時,不會將Pod從該節(jié)點移出,僅對新建的Pod對象生效

    nodeAffinity可對應(yīng)的兩種策略: preferredDuringSchedulingrequiredDuringScheduling

類型 描述
requiredDuringScheduling pod資源在配置中聲明一種標簽,只有node節(jié)點跟我聲明的標簽一致,pod才能
被調(diào)度到此節(jié)點,如果沒有匹配上,那我就一直等有匹配上的節(jié)點。
preferredDuringScheduling pod資源在配置中聲明一種標簽,只有node節(jié)點跟我聲明的標簽一致,pod才能
被調(diào)度到此節(jié)點,但如果沒有匹配上,那我就不等了,隨機找節(jié)點。
IgnoredDuringExecution 如果pod已經(jīng)部署到此節(jié)點,但如果此節(jié)點labels發(fā)生變化,已經(jīng)運行的pod會
怎么辦?pod也會繼續(xù)運行,直到停止此pod生命周期。
RequiredDuringExecution 如果pod已經(jīng)部署到此節(jié)點,但如果此節(jié)點labels發(fā)生變化,已經(jīng)運行的pod會
怎么辦?立刻停止生命周期,pod會重新調(diào)度到滿足條件的節(jié)點。

如果同時部署硬策略、軟策略,當然兩種策略都會執(zhí)行,但是硬策略會苛刻一些,所以必須滿足硬策略的節(jié)點,然后在這些節(jié)點中選擇滿足軟策略的節(jié)點,但如果軟策略一個都不滿足,觸發(fā)軟策略的忽略這個軟策略規(guī)則,讓默認調(diào)度算法,調(diào)度這個滿足硬策略的節(jié)點,直到遇到同時滿足硬策略、軟策略。

硬策略

一個pod資源聲明了一個策略,這個策略中可以寫限制條件,比如標簽,在此pod資源交付到k8s集群后,他會在集群中所有節(jié)點找到符合我這個策略的節(jié)點,比如定義的標簽,如果在所有節(jié)點中找到符合我定義的標簽,我就在這個節(jié)點部署我的pod,如果所有節(jié)點都沒有滿足條件的話,就不斷重試直到遇見滿足條件的node節(jié)點為止,不然這個pod就別啟動。將一直處于掛起,直到有符合要求才會在匹配上的節(jié)點上創(chuàng)建pod。硬策略適用于 pod 必須運行在某種節(jié)點,否則會出現(xiàn)問題的情況,比如集群中節(jié)點的架構(gòu)不同,而運行的服務(wù)必須依賴某種架構(gòu)提供的功能。

  • 方式一:Pod使用 spec.nodeSelector (基于等值關(guān)系);Pod使用 spec.nodeName
  • 方式二:Pod使用 spec.affinity 支持matchExpressions屬性 (復(fù)雜標簽選擇機制)

requiredDuringScheduling<font color=red>IgnoredDuringExecution</font>
表示pod必須部署到滿足條件的節(jié)點上,如果沒有滿足條件的節(jié)點,就不停重試。其中IgnoreDuringExecution表示pod部署之后運行的時候,如果節(jié)點標簽發(fā)生了變化,不再滿足pod指定的條件,pod也會繼續(xù)運行,直到停止此pod生命周期。

requiredDuringScheduling<font color=red>RequiredDuringExecution</font>
表示pod必須部署到滿足條件的節(jié)點上,如果沒有滿足條件的節(jié)點,就不停重試。其中RequiredDuringExecution表示pod部署之后運行的時候,如果節(jié)點標簽發(fā)生了變化,不再滿足pod指定的條件,停止此pod生命周期,重新選擇符合要求的節(jié)點。

軟策略

跟硬策略一樣,都是pod資源聲明了一個策略,這個策略中可以寫限制條件,比如標簽,如果有節(jié)點能滿足我的條件,就在此節(jié)點部署pod,但是如果所有節(jié)點全部沒有滿足調(diào)度要求的話,POD 就會忽略這條規(guī)則,通過默認的Scheduler調(diào)度算法,進行pod部署,直到有符合要求node節(jié)點,才會重新按照軟策略匹配到符合的節(jié)點上創(chuàng)建pod,說白了就是滿足條件最好了,沒有的話也無所謂了的策略,此調(diào)度適用于服務(wù)最好運行在某個區(qū)域,減少網(wǎng)絡(luò)傳輸?shù)取_@種區(qū)分是用戶的具體需求決定的,并沒有絕對的技術(shù)依賴。

  • 柔性控制邏輯,當條件不滿足時,能接受被編排于其他不符合條件的節(jié)點之上
  • 權(quán)重 weight 定義優(yōu)先級,1-100 值越大優(yōu)先級越高

preferredDuringScheduling<font color=red>IgnoredDuringExecution</font>
表示優(yōu)先部署到滿足條件的節(jié)點上,如果沒有滿足條件的節(jié)點,就忽略這些條件,按照正常邏輯部署。其中IgnoreDuringExecution表示pod部署之后運行的時候,如果節(jié)點標簽發(fā)生了變化,不再滿足pod指定的條件,pod也會繼續(xù)運行,直到停止此pod生命周期。

preferredDuringScheduling<font color=red>RequiredDuringExecution</font>
表示優(yōu)先部署到滿足條件的節(jié)點上,如果沒有滿足條件的節(jié)點,就忽略這些條件,按照正常邏輯部署。其中RequiredDuringExecution表示如果后面節(jié)點標簽發(fā)生了變化,滿足了條件,停止此pod生命周期,重新調(diào)度到滿足條件的節(jié)點。

操作符

  • In: label的值在某個列表中
  • NotIn:label的值不在某個列表中
  • Exists:某個label存在
  • DoesNotExist:某個label不存在
  • Gt:label的值大于某個值(字符串比較)
  • Lt:label的值小于某個值(字符串比較)

資源配置文件

# 節(jié)點親和性,相對來說要復(fù)雜一些。也更靈活。
# 完整的文件太多,這里我們只挑重點。
# 推薦參考文章: https://blog.csdn.net/Jerry00713/article/details/124003264

# 硬策略(硬親和性)
# pod.sepc.affinity
#----------------------------
    affinity:
        nodeAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                    - matchExpressions:
                        - key: kubernetes.io/hostname #node節(jié)點的標簽
                          operator: In
                          values:
                            - k8s-node05 #集群真實節(jié)點名稱

#----------------------------

# 軟親和性
# pod.sepc.affinity
#----------------------------
...
    affinity:
        nodeAffinity:
            preferredDuringSchedulingIgnoredDuringExecution:
                - preference:
                    matchExpressions:
                        - key: kubernetes.io/hostname
                          operator: In
                          values:
                        - k8s-node02
                weight: 1 # 配置權(quán)重分
#----------------------------

5. podAffinity 親和性

podAffinity 和 nodeAffinity 相似。

同樣 適用上述:硬親和性軟親和性、操作符

pod硬親和性調(diào)度

Pod親和性描述一個Pod與具有某特征的現(xiàn)存Pod運行位置的依賴關(guān)系;即需要事先存在被依賴的pod對象。

pod軟親和性調(diào)度

pod軟親和性調(diào)度用于 分散同一類應(yīng)用,調(diào)度至不同區(qū)域、機架、節(jié)點等。

6. taints 污點與容忍

<font color=red> 污點taints 是定義在node節(jié)點</font> 上的鍵值型屬性數(shù)據(jù),用于讓節(jié)點拒絕將Pod調(diào)度運行于其上,除非Pod有接納節(jié)點污點的容忍度。

<font color=red> 容忍度 tolerations 是定義在Pod </font>上的鍵值屬性數(shù)據(jù),用于配置可容忍的污點,且調(diào)度器將Pod調(diào)度至其能容忍該節(jié)點污點的節(jié)點上或沒有污點的節(jié)點上 。

對于nodeAffinity無論是硬策略(硬親和)還是軟策略(軟親和)方式,都是調(diào)度 pod 到預(yù)期節(jié)點上,而Taints恰好與之相反,如果一個節(jié)點標記為 Taints ,除非 pod 也被標識為可以容忍污點節(jié)點,否則該Taints 節(jié)點不會被調(diào)度 pod。

節(jié)點親和性,是 pod 的一種屬性(偏好或硬性要求),它使 pod 被吸引到一類特定的節(jié)點。Taint 則相反,它使節(jié)點 能夠 排斥 一類特定的 pod

Taint 和 toleration 相互配合,可以用來避免 pod 被分配到不合適的節(jié)點上。每個節(jié)點上都可以應(yīng)用一個或多個taint ,這表示對于那些不能容忍這些 taint 的 pod,是不會被該節(jié)點接受的。如果將 toleration 應(yīng)用于pod上,則表示這些 pod 可以(但不要求)被調(diào)度到具有匹配 taint 的節(jié)點上

定義污點和容忍度

污點定義:nodes.spec.taints

容忍度定義:pods.spec.tolerations

使用kubectl taint命令可以給某個Node節(jié)點設(shè)置污點,Node被設(shè)置上污點之后就和Pod之間存在一種互斥的關(guān)系,可以讓node 拒絕 pod的調(diào)度執(zhí)行,甚至將node 已經(jīng)存在的pod驅(qū)逐出去

污點

污點的命令和 label比較像,除了要設(shè)置key / value 之外 還要指定 排斥等級 effect

排斥等級 說明 說明
NoSchedule 不能容忍
但僅影響調(diào)度過程,已調(diào)度上去的pod不受影響,僅對新增的pod效
表示 k8s 將不會將 Pod 調(diào)度到具有該污點的 Node 上
NoExecute 不能容忍
當污點變動時,Pod對象會被驅(qū)逐。
表示 k8s 將不會將 Pod 調(diào)度到具有該污點的 Node 上,
同時會<font color=red>將 Node 上已經(jīng)存在的Pod 驅(qū)逐出去 </font>
PreferNoSchedule 柔性約束
節(jié)點現(xiàn)存Pod不受影響,如果實在是沒有符合的節(jié)點,也可以被調(diào)度
表示 k8s 將盡量不會將 Pod 調(diào)度到具有該污點的 Node 上 。
盡量不把pod調(diào)度到此節(jié)點上運行,若POD資源無法容忍所有節(jié)點,改成可以容忍 所有污點,通過默認調(diào)度算法啟動POD,要容忍就容忍所有的污點,不會按照污點數(shù)少進行優(yōu)先調(diào)度

為了不影響之前的環(huán)境,我們先創(chuàng)建一個namespace :taint-demo-ns

kubectl create namespace taint-demo-ns

環(huán)境情況:

Node effect
k8s-master01 -
k8s-slave02 -
k8s-slave03 -
創(chuàng)建污點
# 創(chuàng)建單個污點
kubectl taint nodes k8s-slave03 taintKey=abc123:NoSchedule
# 創(chuàng)建多個污點
kubectl taint nodes k8s-slave03 taintKey=abc123:NoSchedule key2=def456:NoSchedule key3=ghj789:NoSchedule
刪除污點
 kubectl taint nodes k8s-slave03 taintKey=abc123:NoExecute-
修改污點
kubectl taint nodes k8s-slave03 taintKey=abc456:NoSchedule --overwrite
查看污點
kubectl describe node k8s-slave03 | grep -i taint
#-----------------------------------
Taints:             <none>
#-----------------------------------

# 我們對`k8s-slave03` 添加污點后再查看
kubectl describe node k8s-slave03 | grep -i taint
#-----------------------------------
Taints:             taintKey=abc123:NoSchedule
#-----------------------------------
資源配置文件
污點effect - NoSchedule

測試案例:

新部署的pod不會被調(diào)度到具有污點標記且 effect = NoSchedule 的節(jié)點上。

> step 1:

  我們針對 node `k8s-slave03` 設(shè)置污點, effect 規(guī)則為`NoSchedule`。

> step 2:

  我們編寫一個deployment,然后執(zhí)行。

> step 3:

  我們查看pods所在節(jié)點,是否存在在 `k8s-slave03`節(jié)點上

污點effect情況

Node effect
k8s-master01 -
k8s-slave02 -
k8s-slave03 NoSchedule
# step 1
kubectl taint nodes k8s-slave03 taintKey=abc123:NoSchedule

# step 2
vim scheduler_taint-test1.yml
#----------------------------
# 污點測試1
apiVersion: apps/v1
kind: Deployment
metadata:
  name: taint-test1
  # 指定命名空間
  namespace: taint-demo-ns
  labels:
    app: taint-test1
spec:
  replicas: 3
  template:
    metadata:
      name: taint-test1
      labels:
        app: taint-test1
    spec:
      containers:
        - name: taint-test1
          image: nginx
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
  selector:
    matchLabels:
      app: taint-test1
#----------------------------
kubectl apply -f scheduler_taint-test1.yml

# step 3
kubectl get pods -n taint-demo-ns -o wide
#----------------------------
NAME                           READY   STATUS    RESTARTS   AGE   IP               NODE          NOMINATED NODE   READINESS GATES
taint-test1-74dd797cc4-hr9qc   1/1     Running   0          50s   10.244.220.254   k8s-slave02   <none>           <none>
taint-test1-74dd797cc4-ncffj   1/1     Running   0          50s   10.244.220.253   k8s-slave02   <none>           <none>
taint-test1-74dd797cc4-qwhtw   1/1     Running   0          50s   10.244.220.255   k8s-slave02   <none>           <none>
#----------------------------

# 結(jié)論,符合我們的期望。k8s-slave02 設(shè)置了NoSchedule,新建的pods不會被調(diào)度給 k8s-slave02
污點effect - NoExecute

測試案例:

node 節(jié)點 設(shè)置 污點-effect-NoExecute后,此節(jié)點上的pod 會被驅(qū)逐。(我們會驅(qū)逐k8s-slave03上的所有pods)

> step 1:

  我們先查看node`k8s-slave03` 上 的pods,記錄下來。

> step 2:

  我們針對 node `k8s-slave03` 設(shè)置污點, effect 規(guī)則為`NoSchedule`。

> step 3:

  我們查看pods所在節(jié)點,是否存在在 `k8s-slave02`節(jié)點上

污點effect情況

Node effect
k8s-master01 -
k8s-slave02 -
k8s-slave03 NoExecute
# step 1
kubectl describe node k8s-slave03
#-------------------------
...

  Namespace                   Name                                CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                                ------------  ----------  ---------------  -------------  ---
  default                     demonsetdemo-xnnf6                  0 (0%)        0 (0%)      0 (0%)           0 (0%)         2d17h
  default                     deploymentdemo1-5bc649f558-92bc9    0 (0%)        0 (0%)      0 (0%)           0 (0%)         2d18h
  default                     deploymentdemo1-5bc649f558-qtt4r    0 (0%)        0 (0%)      0 (0%)           0 (0%)         2d18h
  default                     deploymentdemo1-5bc649f558-vskqr    0 (0%)        0 (0%)      0 (0%)           0 (0%)         2d18h
  default                     deploymentdemo1-5bc649f558-xtp9s    0 (0%)        0 (0%)      0 (0%)           0 (0%)         2d18h
  default                     deploymentdemo1-5bc649f558-z5xhr    0 (0%)        0 (0%)      0 (0%)           0 (0%)         2d18h
  default                     nginx-app-74d589986c-bdf66          0 (0%)        0 (0%)      0 (0%)           0 (0%)         6d16h
  default                     nginx-app-74d589986c-gfmsd          0 (0%)        0 (0%)      0 (0%)           0 (0%)         6d16h
  default                     nginx-app-74d589986c-p4mbl          0 (0%)        0 (0%)      0 (0%)           0 (0%)         6d16h
  default                     nginx-app-74d589986c-slszp          0 (0%)        0 (0%)      0 (0%)           0 (0%)         6d16h
  kube-system                 calico-node-q789c                   250m (12%)    0 (0%)      0 (0%)           0 (0%)         7d22h
  kube-system                 kube-proxy-wwn68                    0 (0%)        0 (0%)      0 (0%)           0 (0%)         5d19h
  tmp-quota-range             quta-pod-test3                      550m (27%)    600m (30%)  110Mi (6%)       130Mi (7%)     41h
  tmp-quota                   quta-pod-test1                      800m (40%)    1 (50%)     150Mi (8%)       200Mi (11%)    42h

...
#-------------------------

# step 2
kubectl taint nodes k8s-slave03 taintKey=abc123:NoExecute 

# step 3
kubectl describe nodes k8s-slave03
#-------------------------
...
  Namespace                   Name                 CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                 ------------  ----------  ---------------  -------------  ---
  kube-system                 calico-node-q789c    250m (12%)    0 (0%)      0 (0%)           0 (0%)         7d23h
  kube-system                 kube-proxy-wwn68     0 (0%)        0 (0%)      0 (0%)           0 (0%)         5d19h
...
#-------------------------

# 結(jié)論,符合我們的期望。node `k8s-slave03` 上的所有節(jié)點被驅(qū)逐。我們發(fā)現(xiàn)`kube-system` namespace 還存在兩個節(jié)點`calico` 和 `kube-proxy`
污點effect - PreferNoSchedule

測試案例:

node 節(jié)點 設(shè)置 污點-effect-PreferNoSchedule后,如果沒有其他滿足條件的節(jié)點,那么新建的pods會被分配到 有PreferNoSchedule的節(jié)點上。

> step 1:

  我們針對 node `k8s-slave02` 設(shè)置污點, effect 規(guī)則為`PreferNoSchedule`。

> step 2:

  我們針對 node `k8s-slave03` 設(shè)置污點, effect 規(guī)則為`NoSchedule`。

  我們新建deployment并運行。

> step 3:

  查看驅(qū)逐過程,查看pods所在節(jié)點,是否存在在 `k8s-slave02`節(jié)點上

污點effect情況

Node effect
k8s-master01 -
k8s-slave02 PreferNoSchedule
k8s-slave03 NoSchedule
# step 1
kubectl taint nodes k8s-slave02 taintKey=abc123:PreferNoSchedule

# step 2
vim scheduler_taint-test2.yml
#---------------------------
# 污點測試2
apiVersion: apps/v1
kind: Deployment
metadata:
  name: taint-test2
  namespace: taint-demo-ns
  labels:
    app: taint-test2
spec:
  replicas: 3
  template:
    metadata:
      name: taint-test2
      labels:
        app: taint-test2
    spec:
      containers:
        - name: taint-test2
          image: nginx
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
  selector:
    matchLabels:
      app: taint-test2
#---------------------------
kubectl apply -f scheduler_taint-test2.yml

# step 3
kubectl get pods -n taint-demo-ns -o wide
#---------------------------
NAME                           READY   STATUS    RESTARTS   AGE   IP               NODE          NOMINATED NODE   READINESS GATES
taint-test2-6878fdbc99-j2tvj   1/1     Running   0          26s   10.244.220.195   k8s-slave02   <none>           <none>
taint-test2-6878fdbc99-mmzrj   1/1     Running   0          26s   10.244.220.194   k8s-slave02   <none>           <none>
taint-test2-6878fdbc99-xfl57   1/1     Running   0          26s   10.244.220.193   k8s-slave02   <none>           <none>
#---------------------------

# 結(jié)論,符合我們的期望,PreferNoSchedule 是柔性約束,如果除了他之外沒有滿足條件的node,那么pods 會被調(diào)度給 具有PreferNoSchedule的節(jié)點上。

容忍度

容忍度是針對pod的配置。 通過 Pod.spec.tolerations屬性設(shè)置

資源配置文件

測試案例:

node k8s-slave02: 我們設(shè)置 污點 NoSchedule;

node k8s-slave03:我們設(shè)置污點 NoExecute;

正常情況下,我們新建的pods是無法被調(diào)度到 k8s-slave02 或 k8s-slave03 上的。

我們配置容忍度,然后再觀察調(diào)度情況

> step 1:

  我們針對 node `k8s-slave02` 設(shè)置污點, effect 規(guī)則為`NoSchedule`。

> step 2:

  我們針對 node `k8s-slave03` 設(shè)置污點, effect 規(guī)則為`NoExecute`。

  我們新建deployment,配置容忍度,容忍` k8s-slave03`污點的所有情況

  執(zhí)行

> step 3:

查看pods所在節(jié)點,是否存在在 k8s-slave03節(jié)點上

污點effect情況

Node effect
k8s-master01 -
k8s-slave02 NoSchedule
k8s-slave03 NoExecute
# step 1
kubectl taint nodes k8s-slave02 taintKey=abc123:NoSchedule

# step 2
kubectl taint nodes k8s-slave03 taintKey=abc123:NoExecute
vim  scheduler_tolerations.yml
#----------------------------------
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tolerations-test
  namespace: taint-demo-ns
  labels:
    app: tolerations-test
spec:
  replicas: 10
  template:
    metadata:
      name: tolerations-test
      labels:
        app: tolerations-test
    spec:
      containers:
        - name: tolerations-test
          image: nginx
          imagePullPolicy: IfNotPresent
      # 容忍度配置,要與對應(yīng)的污點 設(shè)置信息【完全匹配】,才會生效
      tolerations:
        - key: "taintKey"
          operator: "Equal"
          value: "abc123"
          effect: "NoExecute"
      restartPolicy: Always
  selector:
    matchLabels:
      app: tolerations-test
#----------------------------------
kubectl apply -f scheduler_tolerations.yml

# step 3
kubectl get pods -n taint-demo-ns -o wide
#----------------------------------
NAME                               READY   STATUS    RESTARTS   AGE   IP               NODE          NOMINATED NODE   READINESS GATES
tolerations-test-f47f9d965-56zvf   1/1     Running   0          9s    10.244.211.205   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-7rwvm   1/1     Running   0          9s    10.244.211.198   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-87n4v   1/1     Running   0          9s    10.244.211.202   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-dnjcs   1/1     Running   0          9s    10.244.211.204   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-fdlm8   1/1     Running   0          9s    10.244.211.207   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-gkjbf   1/1     Running   0          9s    10.244.211.200   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-kps9d   1/1     Running   0          9s    10.244.211.199   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-nq2fm   1/1     Running   0          9s    10.244.211.197   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-p4s2k   1/1     Running   0          9s    10.244.211.203   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-pf5mb   1/1     Running   0          9s    10.244.211.206   k8s-slave03   <none>           <none>
#----------------------------------
kubectl describe node k8s-slave03 | grep -i taint
#----------------------------------
Taints:             taintKey=abc123:NoExecute
  taint-demo-ns               tolerations-test-f47f9d965-56zvf    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-7rwvm    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-87n4v    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-dnjcs    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-fdlm8    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-gkjbf    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-kps9d    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-nq2fm    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-p4s2k    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-pf5mb    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
#----------------------------------

# 結(jié)論,符合我們的期望;我們對`k8s-slave03`配置了容忍度,因此在創(chuàng)建新的pods時,有可能會被調(diào)度到`k8s-slave03`上。 注意:是有可能,具體還要看每個節(jié)點的effect配置。

附錄

參考資料

Kubernetes 多租戶:資源配額

Kubernetes最佳實踐(四):資源請求和限制

Kubernetes 資源配額 ResourceQuota

k8s資源調(diào)度

【k8s】kubectl label命令(對node添加、刪除label)

Kubernetes K8S節(jié)點選擇

K8s資源調(diào)度方式

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容