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è)置計算資源配額,有兩種方式:ResourceQuota和 LimitRange
| 資源名稱 | 資源類型 | 描述 |
|---|---|---|
| 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)度步驟
首先用戶在通過 Kubernetes 客戶端 Kubectl 提交創(chuàng)建 Pod 的 Yaml 的文件,向Kubernetes 系統(tǒng)發(fā)起資源請求,該資源請求被提交到Kubernetes 系統(tǒng)。
Kubernetes 系統(tǒng)中,用戶通過命令行工具 Kubectl 向 Kubernetes 集群即用
API Server的方式發(fā)送“POST”請求,即創(chuàng)建 Pod 的請求。API Server接收到請求后把創(chuàng)建 Pod 的信息存儲到 Etcd 中,從集群運行那一刻起,資源調(diào)度系統(tǒng)Scheduler 就會定時去監(jiān)控API Server通過
API Server得到創(chuàng)建 Pod 的信息,Scheduler 采用 watch 機制,一旦 Etcd 存儲 Pod 信息成功便會立即通知API Server,API Server會立即把Pod創(chuàng)建的消息通知Scheduler,Scheduler發(fā)現(xiàn) Pod 的屬性中 Dest Node 為空時(Dest Node="")便會立即觸發(fā)調(diào)度流程進行調(diào)度。-
而這一個創(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的資源分為兩種屬性。
- 可壓縮資源(例如CPU循環(huán),Disk I/O帶寬)都是可以被限制和被回收的,對于一個Pod來說可以降低這些資源的使用量而不去殺掉Pod。
- 不可壓縮資源(例如內(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)的兩種策略:
preferredDuringScheduling和requiredDuringScheduling
類型 描述 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 PreferNoSchedulek8s-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 NoSchedulek8s-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配置。
