k8s的list-watch机制和 pod调度约束
文章目錄
- 一: k8s的list-watch 機制
- 1.1 k8s通過list-watch 機制進行每個組件的寫作
- 1.2 Pod 的典型啟動過程
- 1.3 調(diào)度過程
- 1.3.1 預(yù)算策略(predicate)
- 1.3.2 優(yōu)選策略(priorities)
- 1.4 指定調(diào)度節(jié)點
- 1.4.1 使用nodeName 強制匹配
- 1.4.2 使用nodeSelector強制約束
- 1.5 修改,刪除,查詢label
- 二: 親和性
- 2.1 節(jié)點親和性和Pod親和性
- 2.1.1 節(jié)點親和性
- 2.1.2 Pod親和性
- 2.2 鍵值運算關(guān)系
- 2.3 示例
- 2.3.1 硬策略
- 2.3.2 軟策略
- 2.3.3 軟策略和硬策略一起使用
- 2.4 親和性和反親和
- 2.4.1 pod 親和性
- 2.4.2 pod 反親和性調(diào)度
一: k8s的list-watch 機制
1.1 k8s通過list-watch 機制進行每個組件的寫作
Kubernetes 是通過 List-Watch 的機制進行每個組件的協(xié)作,保持?jǐn)?shù)據(jù)同步的,每個組件之間的設(shè)計實現(xiàn)了解耦。
用戶是通過 kubectl 根據(jù)配置文件,向 APIServer 發(fā)送命令,在 Node 節(jié)點上面建立 Pod 和 Container。
APIServer 經(jīng)過 API 調(diào)用,權(quán)限控制,調(diào)用資源和存儲資源的過程,實際上還沒有真正開始部署應(yīng)用。這里 需要 Controller Manager、Scheduler 和 kubelet 的協(xié)助才能完成整個部署過程。
在 Kubernetes 中,所有部署的信息都會寫到 etcd 中保存。實際上 etcd 在存儲部署信息的時候,會發(fā)送 Create 事件給 APIServer,而 APIServer 會通過監(jiān)聽(Watch)etcd 發(fā)過來的事件。其他組件也會監(jiān)聽(Watch)APIServer 發(fā)出來的事件。
1.2 Pod 的典型啟動過程
Pod 是 Kubernetes 的基礎(chǔ)單元,Pod 啟動典型創(chuàng)建過程如下:
(1)這里有三個 List-Watch,分別是 Controller Manager(運行在 Master),Scheduler(運行在 Master),kubelet(運行在 Node)。他們在進程已啟動就會監(jiān)聽(Watch)APIServer 發(fā)出來的事件。
(2)用戶通過 kubectl 或其他 API 客戶端提交請求給 APIServer 來建立一個 Pod 對象副本。
(3)APIServer 嘗試著將 Pod 對象的相關(guān)元信息存入 etcd 中,待寫入操作執(zhí)行完成,APIServer 即會返回確認(rèn)信息至客戶端。
(4)當(dāng) etcd 接受創(chuàng)建 Pod 信息以后,會發(fā)送一個 Create 事件給 APIServer。
(5)由于 Controller Manager 一直在監(jiān)聽(Watch,通過http的8080端口)APIServer 中的事件。此時 APIServer 接受到了 Create 事件,又會發(fā)送給 Controller Manager。
(6)Controller Manager 在接到 Create 事件以后,調(diào)用其中的 Replication Controller 來保證 Node 上面需要創(chuàng)建的副本數(shù)量。一旦副本數(shù)量少于 RC 中定義的數(shù)量,RC 會自動創(chuàng)建副本。總之它是保證副本數(shù)量的 Controller(PS:擴容縮容的擔(dān)當(dāng))。
(7)在 Controller Manager 創(chuàng)建 Pod 副本以后,APIServer 會在 etcd 中記錄這個 Pod 的詳細(xì)信息。例如 Pod 的副本數(shù),Container 的內(nèi)容是什么。
(8)同樣的 etcd 會將創(chuàng)建 Pod 的信息通過事件發(fā)送給 APIServer。
(9)由于 Scheduler 在監(jiān)聽(Watch)APIServer,并且它在系統(tǒng)中起到了“承上啟下”的作用,“承上”是指它負(fù)責(zé)接收創(chuàng)建的 Pod 事件,為其安排 Node;“啟下”是指安置工作完成后,Node 上的 kubelet 進程會接管后繼工作,負(fù)責(zé) Pod 生命周期中的“下半生”。 換句話說,Scheduler 的作用是將待調(diào)度的 Pod 按照調(diào)度算法和策略綁定到集群中 Node 上。
(10)Scheduler 調(diào)度完畢以后會更新 Pod 的信息,此時的信息更加豐富了。除了知道 Pod 的副本數(shù)量,副本內(nèi)容。還知道部署到哪個 Node 上面了。并將上面的 Pod 信息更新至 API Server,由 APIServer 更新至 etcd 中,保存起來。
(11)etcd 將更新成功的事件發(fā)送給 APIServer,APIServer 也開始反映此 Pod 對象的調(diào)度結(jié)果。
(12)kubelet 是在 Node 上面運行的進程,它也通過 List-Watch 的方式監(jiān)聽(Watch,通過https的6443端口)APIServer 發(fā)送的 Pod 更新的事件。kubelet 會嘗試在當(dāng)前節(jié)點上調(diào)用 Docker 啟動容器,并將 Pod 以及容器的結(jié)果狀態(tài)回送至 APIServer。
(13)APIServer 將 Pod 狀態(tài)信息存入 etcd 中。在 etcd 確認(rèn)寫入操作成功完成后,APIServer將確認(rèn)信息發(fā)送至相關(guān)的 kubelet,事件將通過它被接受。
注意:在創(chuàng)建 Pod 的工作就已經(jīng)完成了后,為什么 kubelet 還要一直監(jiān)聽呢?原因很簡單,假設(shè)這個時候 kubectl 發(fā)命令,要擴充 Pod 副本數(shù)量,那么上面的流程又會觸發(fā)一遍,kubelet 會根據(jù)最新的 Pod 的部署情況調(diào)整 Node 的資源。又或者 Pod 副本數(shù)量沒有發(fā)生變化,但是其中的鏡像文件升級了,kubelet 也會自動獲取最新的鏡像文件并且加載。
1.3 調(diào)度過程
Scheduler 是 kubernetes 的調(diào)度器,主要的任務(wù)是把定義的 pod 分配到集群的節(jié)點上。其主要考慮的問題如下:
- 公平:如何保證每個節(jié)點都能被分配資源
- 資源高效利用:集群所有資源最大化被使用
- 效率:調(diào)度的性能要好,能夠盡快地對大批量的 pod 完成調(diào)度工作
- 靈活:允許用戶根據(jù)自己的需求控制調(diào)度的邏輯
調(diào)度分為幾個部分:
- 首先是過濾掉不滿足條件的節(jié)點,這個過程稱為預(yù)算策略(predicate)
- 然后對通過的節(jié)點按照優(yōu)先級排序,這個是優(yōu)選策略(priorities);
- 最后從中選擇優(yōu)先級最高的節(jié)點。如果中間任何一步驟有錯誤,就直接返回錯誤。
1.3.1 預(yù)算策略(predicate)
Predicate 有一系列的常見的算法可以使用:
- PodFitsResources:節(jié)點上剩余的資源是否大于 pod 請求的資源。
- PodFitsHost:如果 pod 指定了 NodeName,檢查節(jié)點名稱是否和 NodeName 匹配。
- PodFitsHostPorts:節(jié)點上已經(jīng)使用的 port 是否和 pod 申請的 port 沖突。
- PodSelectorMatches:過濾掉和 pod 指定的 label 不匹配的節(jié)點。
- NoDiskConflict:已經(jīng) mount 的 volume 和 pod 指定的 volume 不沖突,除非它們都是只讀。
1.3.2 優(yōu)選策略(priorities)
如果在 predicate 過程中沒有合適的節(jié)點,pod 會一直在 pending 狀態(tài),不斷重試調(diào)度,直到有節(jié)點滿足條件。 經(jīng)過這個步驟,如果有多個節(jié)點滿足條件,就繼續(xù) priorities 過程:按照優(yōu)先級大小對節(jié)點排序。
優(yōu)先級由一系列鍵值對組成,鍵是該優(yōu)先級項的名稱,值是它的權(quán)重(該項的重要性)。有一系列的常見的優(yōu)先級選項包括:
- LeastRequestedPriority:通過計算CPU和Memory的使用率來決定權(quán)重,使用率越低權(quán)重越高。也就是說,這個優(yōu)先級指標(biāo)傾向于資源使用比例更低的節(jié)點。
- BalancedResourceAllocation:節(jié)點上 CPU 和 Memory 使用率越接近,權(quán)重越高。這個一般和上面的一起使用,不單獨使用。比如 node01 的 CPU 和 Memory 使用率 20:60,node02 的 CPU 和 Memory 使用率 50:50,雖然 node01 的總使用率比 node02 低,但 node02 的 CPU 和 Memory 使用率更接近,從而調(diào)度時會優(yōu)選 node02。
- ImageLocalityPriority:傾向于已經(jīng)有要使用鏡像的節(jié)點,鏡像總大小值越大,權(quán)重越高。
1.4 指定調(diào)度節(jié)點
1.4.1 使用nodeName 強制匹配
pod.spec.nodeName 將 Pod 直接調(diào)度到指定的 Node 節(jié)點上,會跳過 Scheduler 的調(diào)度策略,該匹配規(guī)則是強制匹配
[root@master demo]# vim myapp.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata:name: myapp spec:replicas: 3template:metadata:labels:app: myapp#調(diào)度到nodeName為 node01 的節(jié)點 spec:nodeName: node01containers:- name: myappimage: soscscs/myapp:v1ports:- containerPort: 80 [root@master demo]# kubectl apply -f myapp.yaml deployment.extensions/myapp created [root@master demo]# kubectl get pods -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES myapp-786c9896f9-nvqpc 1/1 Running 0 9s 10.244.1.82 node01 <none> <none> myapp-786c9896f9-qnmbz 1/1 Running 0 9s 10.244.1.81 node01 <none> <none> myapp-786c9896f9-xl4tv 1/1 Running 0 9s 10.244.1.83 node01 <none> <none>#查看詳細(xì)事件(發(fā)現(xiàn)未經(jīng)過 scheduler 調(diào)度分配) [root@master demo]# kubectl describe pod myapp-786c9896f9-nvqpc1.4.2 使用nodeSelector強制約束
pod.spec.nodeSelector:通過 kubernetes 的 label-selector 機制選擇節(jié)點,由調(diào)度器調(diào)度策略匹配 label,然后調(diào)度 Pod 到目標(biāo)節(jié)點,該匹配規(guī)則屬于強制約束
獲取標(biāo)簽幫助
kubectl label --help
Usage:
kubectl label [–overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 … KEY_N=VAL_N [–resource-version=version] [options]
1.5 修改,刪除,查詢label
#修改node02 的標(biāo)簽, 將標(biāo)簽修改為apps=c [root@master demo]# kubectl label nodes node02 apps=c --overwrite node/node02 labeled#查看節(jié)點的標(biāo)簽為 apps 的節(jié)點 [root@master demo]# kubectl get nodes -l apps NAME STATUS ROLES AGE VERSION node01 Ready <none> 6d22h v1.15.1 node02 Ready <none> 6d22h v1.15.1#查看節(jié)點標(biāo)簽為 apps=c的節(jié)點 [root@master demo]# kubectl get nodes -l apps=c NAME STATUS ROLES AGE VERSION node02 Ready <none> 6d22h v1.15.1#刪除節(jié)點node02 的標(biāo)簽 apps [root@master demo]# kubectl label nodes node02 apps- node/node02 labeled [root@master demo]# kubectl get nodes -l apps NAME STATUS ROLES AGE VERSION node01 Ready <none> 6d22h v1.15.1#刪除節(jié)點node01 的標(biāo)簽apps [root@master demo]# kubectl label nodes node01 apps- node/node01 labeled [root@master demo]# kubectl get nodes -l apps No resources found.二: 親和性
2.1 節(jié)點親和性和Pod親和性
2.1.1 節(jié)點親和性
pod.spec.nodeAffinitypreferredDuringSchedulingIgnoredDuringExecution:軟策略requiredDuringSchedulingIgnoredDuringExecution:硬策略2.1.2 Pod親和性
pod.spec.affinity.podAffinity/podAntiAffinitypreferredDuringSchedulingIgnoredDuringExecution:軟策略requiredDuringSchedulingIgnoredDuringExecution:硬策略2.2 鍵值運算關(guān)系
| In | label的值在某個列表中 |
| NotIn | label的值不在某個列表中 |
| Gt | label的值大于某個值 |
| Lt | label的值小于某個值 |
| Exists | 某個label存在 |
| DoesNotExist | 某個label不存在 |
2.3 示例
#查看節(jié)點的標(biāo)簽 [root@master demo]# kubectl get nodes --show-labels NAME STATUS ROLES AGE VERSION LABELS master Ready master 6d22h v1.15.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/master= node01 Ready <none> 6d22h v1.15.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node01,kubernetes.io/os=linux node02 Ready <none> 6d22h v1.15.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node02,kubernetes.io/os=linux2.3.1 硬策略
[root@master demo]# vim pod1.yaml apiVersion: v1 kind: Pod metadata:name: affinitylabels:app: node-affinity-pod spec:containers:- name: with-node-affinityimage: soscscs/myapp:v1affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:#指定node的標(biāo)簽- key: kubernetes.io/hostname#設(shè)置Pod安裝到kubernetes.io/hostname 的標(biāo)簽值不在valus列表中的node上operator: NotInvalues:- node02 [root@master demo]# kubectl apply -f pod1.yaml pod/affinity created [root@master demo]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES affinity 1/1 Running 0 9s 10.244.1.94 node01 <none> <none> [root@master demo]# vim pod1.yaml apiVersion: v1 kind: Pod metadata:name: affinitylabels:app: node-affinity-pod spec:containers:- name: with-node-affinityimage: soscscs/myapp:v1affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: kubernetes.io/hostname#設(shè)置Pod安裝到kubernetes.io/hostname的標(biāo)簽值在values列表中的node上operator: Invalues:#當(dāng)前調(diào)度node沒有node03- node03 #硬策略不滿足條件,Pod 狀態(tài)一直會處于 Pending 狀態(tài)。 kubectl delete pod --all && kubectl apply -f pod1.yaml && kubectl get pods -o wide2.3.2 軟策略
[root@master demo]# vim pod2.yaml apiVersion: v1 kind: Pod metadata:name: affinitylabels:app: node-affinity-pod spec:containers:- name: with-node-affinityimage: soscscs/myapp:v1affinity:nodeAffinity:preferredDuringSchedulingIgnoredDuringExecution:#如果有多個軟策略選項的話,權(quán)重越大,優(yōu)先級越高- weight: 1preference:matchExpressions:- key: kubernetes.io/hostnameoperator: Invalues:- node03 [root@master demo]# kubectl get pods -o wide [root@master demo]# vim pod2.yaml apiVersion: v1 kind: Pod metadata:name: affinitylabels:app: node-affinity-pod spec:containers:- name: with-node-affinityimage: soscscs/myapp:v1affinity:nodeAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 1preference:matchExpressions:- key: kubernetes.io/hostnameoperator: Invalues:- node02 [root@master demo]# kubectl delete pod --all && kubectl apply -f pod2.yaml && kubectl get pods -o wide2.3.3 軟策略和硬策略一起使用
如果把硬策略和軟策略合在一起使用,則要先滿足硬策略之后才會滿足軟策略
apiVersion: v1 kind: Pod metadata:name: affinitylabels:app: node-affinity-pod spec:containers:- name: with-node-affinityimage: soscscs/myapp:v1affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution: #先滿足硬策略,排除有kubernetes.io/hostname=node02標(biāo)簽的節(jié)點nodeSelectorTerms:- matchExpressions:- key: kubernetes.io/hostnameoperator: NotInvalues:- node02preferredDuringSchedulingIgnoredDuringExecution: #再滿足軟策略,優(yōu)先選擇有kgc=a標(biāo)簽的節(jié)點- weight: 1preference:matchExpressions:- key: appsoperator: Invalues:- a2.4 親和性和反親和
| nodeAffinity | 主機 | In, NotIn, Exists,DoesNotExist, Gt, Lt | 否 | 指定主機 |
| podAffinit | Pod | In, NotIn, Exists,DoesNotExist | 是 | Pod與指定Pod同一拓?fù)溆?/td> |
| podAntiAffinity | Pod | In, NotIn, Exists,DoesNotExist | 是 | Pod與指定Pod不在同一拓?fù)溆?/td> |
2.4.1 pod 親和性
[root@master demo]# vim pod4.yaml apiVersion: v1 kind: Pod metadata:name: myapp02labels:app: myapp02 spec:containers:- name: myapp02image: soscscs/myapp:v1affinity:#pod親和策略podAffinity:#硬策略requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: appoperator: Invalues:- myapp01#拓?fù)溆騮opologyKey: kubernetes.io/hostname 當(dāng)存在pod 擁有鍵app,值為myapp01時,(暫時稱這個pod為X),其所在節(jié)點的 kubernetes.io/hostname 值,就成為了新pod調(diào)度的依據(jù)。(kubernetes.io/hostname 是由字段topologyKey 定義的 ) 新pod調(diào)度的節(jié)點,其kubernetes.io/hostname值必須和 pod X 所在節(jié)點的kubernetes.io/hostname值一致僅當(dāng)節(jié)點和至少一個已運行且有鍵為“app”且值為“myapp01”的標(biāo)簽 的 Pod 處于同一拓?fù)溆驎r,才可以將該 Pod 調(diào)度到節(jié)點上。 (更確切的說,如果節(jié)點 N 具有帶有鍵 kubernetes.io/hostname 和某個值 V 的標(biāo)簽,則 Pod 有資格在節(jié)點 N 上運行, 以便集群中至少有一個節(jié)點具有鍵 kubernetes.io/hostname 和值為 V 的節(jié)點正在運行具有鍵“app”和值 “myapp01”的標(biāo)簽的 pod。)
topologyKey 是節(jié)點標(biāo)簽的鍵。如果兩個節(jié)點使用此鍵標(biāo)記并且具有相同的標(biāo)簽值,則調(diào)度器會將這兩個節(jié)點視為處于同一拓?fù)溆蛑小?調(diào)度器試圖在每個拓?fù)溆蛑蟹胖脭?shù)量均衡的 Pod。
如果 kubernetes.io/hostname 對應(yīng)的值不一樣就是不同的拓?fù)溆颉1热?Pod1 在 kubernetes.io/hostname=node01 的 Node 上,Pod2 在 kubernetes.io/hostname=node02 的 Node 上,Pod3 在 kubernetes.io/hostname=node01 的 Node 上,則 Pod2 和 Pod1、Pod3 不在同一個拓?fù)溆?#xff0c;而Pod1 和 Pod3在同一個拓?fù)溆颉?/p> [root@master demo]# kubectl apply -f pod4.yaml [root@master demo]# kubectl get pods --show-labels -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS affinity 1/1 Running 0 122m 10.244.1.100 node01 <none> <none> app=node-affinity-pod myapp01 1/1 Running 0 114m 10.244.2.74 node02 <none> <none> app=myapp01 myapp02 1/1 Running 0 46m 10.244.2.75 node02 <none> <none> app=myapp02
2.4.2 pod 反親和性調(diào)度
[root@master demo]# vim pod5.yaml apiVersion: v1 kind: Pod metadata:name: myapp03labels:app: myapp03 spec:containers:- name: myapp03image: soscscs/myapp:v1affinity:#Pod反親和podAntiAffinity:#軟策略preferredDuringSchedulingIgnoredDuringExecution:- weight: 100podAffinityTerm:labelSelector:matchExpressions:- key: appoperator: Invalues:- myapp01topologyKey: kubernetes.io/hostname如果節(jié)點處于 Pod 所在的同一拓?fù)溆蚯揖哂墟I“app”和值“myapp01”的標(biāo)簽, 則該 pod 不應(yīng)將其調(diào)度到該節(jié)點上。 (如果 topologyKey 為 kubernetes.io/hostname,則意味著當(dāng)節(jié)點和具有鍵 “app”和值“myapp01”的 Pod 處于相同的區(qū)域,Pod 不能被調(diào)度到該節(jié)點上。)
即:當(dāng)某個pod擁有鍵 app,值為myapp01 的標(biāo)簽時(稱此pod為X),就不將新pod 調(diào)度到和pod X處于 同一個拓?fù)溆虻墓?jié)點(即擁有相同的 topologyKey: kubernetes.io/hostname值 )
[root@master demo]# kubectl apply -f pod5.yaml pod/myapp03 created [root@master demo]# kubectl get pods --show-labels -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS affinity 1/1 Running 0 136m 10.244.1.100 node01 <none> <none> app=node-affinity-pod myapp01 1/1 Running 0 128m 10.244.2.74 node02 <none> <none> app=myapp01 myapp02 1/1 Running 0 60m 10.244.2.75 node02 <none> <none> app=myapp02 myapp03 1/1 Running 0 20s 10.244.1.101 node01 <none> <none> app=myapp03node01 節(jié)點的topologyKey值為 kubernetes.io/hostname=node01
node02 節(jié)點的 topologyKey值為 kubernetes.io/hostname=node02
因此,node01 和node02是兩個拓?fù)溆?/p>
而擁有標(biāo)簽 app=myapp01 ,的pod myapp01在node02 節(jié)點上。,所以,所有topologyKey值為 kubernetes.io/hostname=node02的節(jié)點,都不會被該新pod 選擇。
因此,該pod被調(diào)度到了 node01上
總結(jié)
以上是生活随笔為你收集整理的k8s的list-watch机制和 pod调度约束的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pod进阶(资源管理和探针)
- 下一篇: pod 的亲和性,反亲和性 实验