漢中建網(wǎng)站整合營銷策略
K8S調(diào)度
- 一、Kubernetes 調(diào)度
- 1. Pod 調(diào)度介紹
- 2. Pod 啟動(dòng)創(chuàng)建過程
- 3. Kubernetes 的調(diào)度過程
- 3.1 調(diào)度需要考慮的問題
- 3.2 具體調(diào)度過程
- 二、影響kubernetes調(diào)度的因素
- 1. nodeName
- 2. nodeSelector
- 3. 親和性
- 3.1 三種親和性的區(qū)別
- 3.2 鍵值運(yùn)算關(guān)系
- 3.3 節(jié)點(diǎn)親和性
- 3.4 Pod 親和性
- 3.5 Pod 反親和性
- 4. 污點(diǎn)和容忍
- 4.1 污點(diǎn)(Taint)
- 4.2 容忍(Tolerations)
- 5. cordon 和 drain
- 6. Pod啟動(dòng)階段(相位 phase)
- 7. phase 的可能狀態(tài)有
- 8. 如何刪除 Unknown 狀態(tài)的 Pod ?
- 9. 故障排除步驟
- 總結(jié)
- 1. K8S 如何實(shí)現(xiàn)每個(gè)組件的協(xié)作
- 2. scheduler 的調(diào)度算法
- 3. Pod 調(diào)度到指定的 node 節(jié)點(diǎn)
- 4. 標(biāo)簽的管理操作
- 5. 親和性
- 6. 親和性的策略
- 7. 污點(diǎn)和容忍
- 8. 不可調(diào)度方式
- 9. Pod 的啟動(dòng)過程
- 10. Pod 生命周期的 5 種狀態(tài)
- 11. K8S 中的排障手段
一、Kubernetes 調(diào)度
1. Pod 調(diào)度介紹
??Kubernetes 是通過 List-Watch 的機(jī)制進(jìn)行每個(gè)組件的協(xié)作,保持?jǐn)?shù)據(jù)同步的,每個(gè)組件之間的設(shè)計(jì)實(shí)現(xiàn)了解耦。
??用戶是通過 kubectl 根據(jù)配置文件,向 APIServer 發(fā)送命令,在 Node 節(jié)點(diǎn)上面建立 Pod 和 Container。
APIServer 經(jīng)過 API 調(diào)用,權(quán)限控制,調(diào)用資源和存儲(chǔ)資源的過程,實(shí)際上還沒有真正開始部署應(yīng)用。這里 需要 Controller Manager、Scheduler 和 kubelet 的協(xié)助才能完成整個(gè)部署過程。
??在 Kubernetes 中,所有部署的信息都會(huì)寫到 etcd 中保存。實(shí)際上 etcd 在存儲(chǔ)部署信息的時(shí)候,會(huì)發(fā)送 Create 事件給 APIServer,而 APIServer 會(huì)通過監(jiān)聽(Watch)etcd 發(fā)過來的事件。其他組件也會(huì)監(jiān)聽(Watch)APIServer 發(fā)出來的事件。
??調(diào)度框架:https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/scheduling-framework/
??調(diào)度策略:https://kubernetes.io/zh-cn/docs/reference/scheduling/policies/
2. Pod 啟動(dòng)創(chuàng)建過程
??Pod 是 Kubernetes 的基礎(chǔ)單元,Pod 啟動(dòng)典型創(chuàng)建過程如下:
-
(1)這里有三個(gè) List-Watch,分別是 Controller Manager(運(yùn)行在 Master),Scheduler(運(yùn)行在 Master),kubelet(運(yùn)行在 Node)。 他們?cè)谶M(jìn)程已啟動(dòng)就會(huì)監(jiān)聽(Watch)APIServer 發(fā)出來的事件。
-
(2)用戶通過 kubectl 或其他 API 客戶端提交請(qǐng)求給 APIServer 來建立一個(gè) Pod 對(duì)象副本。
-
(3)APIServer 嘗試著將 Pod 對(duì)象的相關(guān)元信息存入 etcd 中,待寫入操作執(zhí)行完成,APIServer 即會(huì)返回確認(rèn)信息至客戶端。
-
(4)當(dāng) etcd 接受創(chuàng)建 Pod 信息以后,會(huì)發(fā)送一個(gè) Create 事件給 APIServer。
-
(5)由于 Controller Manager 一直在監(jiān)聽(Watch,通過https的6443端口)APIServer 中的事件。此時(shí) APIServer 接受到了 Create 事件,又會(huì)發(fā)送給 Controller Manager。
-
(6)Controller Manager 在接到 Create 事件以后,調(diào)用其中的 Replication Controller 來保證 Node 上面需要?jiǎng)?chuàng)建的副本數(shù)量。一旦副本數(shù)量少于 RC 中定義的數(shù)量,RC 會(huì)自動(dòng)創(chuàng)建副本??傊潜WC副本數(shù)量的 Controller(PS:擴(kuò)容縮容的擔(dān)當(dāng))。
-
(7)在 Controller Manager 創(chuàng)建 Pod 副本以后,APIServer 會(huì)在 etcd 中記錄這個(gè) Pod 的詳細(xì)信息。例如 Pod 的副本數(shù),Container 的內(nèi)容是什么。
-
(8)同樣的 etcd 會(huì)將創(chuàng)建 Pod 的信息通過事件發(fā)送給 APIServer。
-
(9)由于 Scheduler 在監(jiān)聽(Watch)APIServer,并且它在系統(tǒng)中起到了“承上啟下”的作用,“承上”是指它負(fù)責(zé)接收創(chuàng)建的 Pod 事件,為其安排 Node;“啟下”是指安置工作完成后,Node 上的 kubelet 進(jìn)程會(huì)接管后繼工作,負(fù)責(zé) Pod 生命周期中的“下半生”。 換句話說,Scheduler 的作用是將待調(diào)度的 Pod 按照調(diào)度算法和策略綁定到集群中 Node 上。
-
(10)Scheduler 調(diào)度完畢以后會(huì)更新 Pod 的信息,此時(shí)的信息更加豐富了。除了知道 Pod 的副本數(shù)量,副本內(nèi)容。還知道部署到哪個(gè) Node 上面了。并將上面的 Pod 信息更新至 API Server,由 APIServer 更新至 etcd 中,保存起來。
-
(11)etcd 將更新成功的事件發(fā)送給 APIServer,APIServer 也開始反映此 Pod 對(duì)象的調(diào)度結(jié)果。
-
(12)kubelet 是在 Node 上面運(yùn)行的進(jìn)程,它也通過 List-Watch 的方式監(jiān)聽(Watch,通過https的6443端口)APIServer 發(fā)送的 Pod 更新的事件。kubelet 會(huì)嘗試在當(dāng)前節(jié)點(diǎn)上調(diào)用 Docker 啟動(dòng)容器,并將 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)聽呢?原因很簡(jiǎn)單,假設(shè)這個(gè)時(shí)候 kubectl 發(fā)命令,要擴(kuò)充 Pod 副本數(shù)量,那么上面的流程又會(huì)觸發(fā)一遍,kubelet 會(huì)根據(jù)最新的 Pod 的部署情況調(diào)整 Node 的資源。又或者 Pod 副本數(shù)量沒有發(fā)生變化,但是其中的鏡像文件升級(jí)了,kubelet 也會(huì)自動(dòng)獲取最新的鏡像文件并且加載。
3. Kubernetes 的調(diào)度過程
3.1 調(diào)度需要考慮的問題
??Scheduler 是 kubernetes 的調(diào)度器,主要的任務(wù)是把定義的 pod 分配到集群的節(jié)點(diǎn)上。其主要考慮的問題如下:
- 公平:如何保證每個(gè)節(jié)點(diǎn)都能被分配資源;
- 資源高效利用:集群所有資源最大化被使用;
- 效率:調(diào)度的性能要好,能夠盡快地對(duì)大批量的 pod 完成調(diào)度工作;
- 靈活:允許用戶根據(jù)自己的需求控制調(diào)度的邏輯。
??Sheduler 是作為單獨(dú)的程序運(yùn)行的,啟動(dòng)之后會(huì)一直監(jiān)聽 APIServer,獲取 spec.nodeName 為空的 pod,對(duì)每個(gè) pod 都會(huì)創(chuàng)建一個(gè) binding,表明該 pod 應(yīng)該放到哪個(gè)節(jié)點(diǎn)上。
3.2 具體調(diào)度過程
- 首先是過濾掉不滿足條件的節(jié)點(diǎn),這個(gè)過程稱為預(yù)算策略(predicate);
- 然后對(duì)通過的節(jié)點(diǎn)按照優(yōu)先級(jí)排序,這個(gè)是優(yōu)選策略(priorities);
- 最后從中選擇優(yōu)先級(jí)最高的節(jié)點(diǎn)。如果中間任何一步驟有錯(cuò)誤,就直接返回錯(cuò)誤。
- 如果在 predicate 過程中沒有合適的節(jié)點(diǎn),pod 會(huì)一直在 pending 狀態(tài),不斷重試調(diào)度,直到有節(jié)點(diǎn)滿足條件。 經(jīng)過這個(gè)步驟,如果有多個(gè)節(jié)點(diǎn)滿足條件,就繼續(xù) priorities 過程:按照優(yōu)先級(jí)大小對(duì)節(jié)點(diǎn)排序。
- 通過算法對(duì)所有的優(yōu)先級(jí)項(xiàng)目和權(quán)重進(jìn)行計(jì)算,得出最終的結(jié)果。
Predicate 常見的算法
算法 | 含義 |
---|---|
PodFitsResources | 節(jié)點(diǎn)上剩余的資源是否大于 pod 請(qǐng)求的資源。 |
PodFitsHost | 如果 pod 指定了 NodeName,檢查節(jié)點(diǎn)名稱是否和 NodeName 匹配。 |
PodFitsHostPorts | 節(jié)點(diǎn)上已經(jīng)使用的 port 是否和 pod 申請(qǐng)的 port 沖突。 |
PodSelectorMatches | 過濾掉和 pod 指定的 label 不匹配的節(jié)點(diǎn)。 |
NoDiskConflict | 已經(jīng) mount 的 volume 和 pod 指定的 volume 不沖突,除非它們都是只讀。 |
優(yōu)先級(jí)
??優(yōu)先級(jí)由一系列鍵值對(duì)組成,鍵是該優(yōu)先級(jí)項(xiàng)的名稱,值是它的權(quán)重(該項(xiàng)的重要性)。有一系列的常見的優(yōu)先級(jí)選項(xiàng)包括:
選項(xiàng) | 含義 |
---|---|
LeastRequestedPriority | 通過計(jì)算CPU和Memory的使用率來決定權(quán)重,使用率越低權(quán)重越高。也就是說,這個(gè)優(yōu)先級(jí)指標(biāo)傾向于資源使用比例更低的節(jié)點(diǎn)。 |
BalancedResourceAllocation | 節(jié)點(diǎn)上 CPU 和 Memory 使用率越接近,權(quán)重越高。這個(gè)一般和LeastRequestedPriority一起使用,不單獨(dú)使用。 比如 node01 的 CPU 和 Memory 使用率 20:60,node02 的 CPU 和 Memory 使用率 50:50,雖然 node01 的總使用率比 node02 低,但 node02 的 CPU 和 Memory 使用率更接近,從而調(diào)度時(shí)會(huì)優(yōu)選 node02。 |
ImageLocalityPriority | 傾向于已經(jīng)有要使用鏡像的節(jié)點(diǎn),鏡像總大小值越大,權(quán)重越高。 |
二、影響kubernetes調(diào)度的因素
1. nodeName
??指定調(diào)度節(jié)點(diǎn):pod.spec.nodeName
將 Pod 直接調(diào)度到指定的 Node 節(jié)點(diǎn)上,會(huì)跳過 Scheduler 的調(diào)度策略,該匹配規(guī)則是強(qiáng)制匹配。
vim myapp.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: myapp
spec:replicas: 3selector:matchLabels:app: myapptemplate:metadata:labels:app: myappspec:nodeName: node01containers:- name: myappimage: nginxports:- containerPort: 80
kubectl apply -f demo1.yamlkubectl get pods -o wide#查看詳細(xì)事件(發(fā)現(xiàn)未經(jīng)過 scheduler 調(diào)度分配)
kubectl describe pod myapp-6bc58d7775-6wlpp
2. nodeSelector
??pod.spec.nodeSelector
:通過 kubernetes 的 label-selector 機(jī)制選擇節(jié)點(diǎn),由調(diào)度器調(diào)度策略匹配 label,然后調(diào)度 Pod 到目標(biāo)節(jié)點(diǎn),該匹配規(guī)則屬于強(qiáng)制約束。
#獲取標(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]#需要獲取 node 上的 NAME 名稱
kubectl get node
NAME STATUS ROLES AGE VERSION
master01 Ready control-plane,master 54m v1.20.15
node01 Ready <none> 51m v1.20.15
node02 Ready <none> 51m v1.20.15
#給對(duì)應(yīng)的 node 設(shè)置標(biāo)簽分別為 class=a 和 class=b
kubectl label nodes node01 class=a
kubectl label nodes node02 class=b#查看標(biāo)簽
kubectl get nodes --show-labels
#修改成 nodeSelector 調(diào)度方式
vim myapp1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: myapp1
spec:replicas: 3selector:matchLabels:app: myapp1template:metadata:labels:app: myapp1spec:nodeSelector:class: acontainers:- name: myapp1image: nginxports:- containerPort: 80
kubectl apply -f myapp1.yaml kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp1-6f999d795-9vc8w 0/1 ContainerCreating 0 13s <none> node01 <none> <none>
myapp1-6f999d795-gpfsh 0/1 ContainerCreating 0 13s <none> node01 <none> <none>
myapp1-6f999d795-nhlg7 0/1 ContainerCreating 0 13s <none> node01 <none>#查看詳細(xì)事件(通過事件可以發(fā)現(xiàn)要先經(jīng)過 scheduler 調(diào)度分配)
kubectl describe pod myapp1-6f999d795-9vc8w
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 41s default-scheduler Successfully assigned default/myapp1-6f999d795-9vc8w to node01Normal Pulling 40s kubelet Pulling image "nginx"Normal Pulled 9s kubelet Successfully pulled image "nginx" in 30.729226268sNormal Created 9s kubelet Created container myapp1Normal Started 9s kubelet Started container myapp1
#修改一個(gè) label 的值,需要加上 --overwrite 參數(shù)
kubectl label nodes node02 class=a --overwrite#刪除一個(gè) label,只需在命令行最后指定 label 的 key 名并與一個(gè)減號(hào)相連即可:
kubectl label nodes node02 class-#指定標(biāo)簽查詢 node 節(jié)點(diǎn)
kubectl get node -l class=a
3. 親和性
??官網(wǎng):https://kubernetes.io/zh/docs/concepts/scheduling-eviction/assign-pod-node/
??節(jié)點(diǎn)親和 和 pod親和 如果同時(shí)存在且發(fā)生沖突,會(huì)報(bào)錯(cuò)!
??親和性調(diào)度是指通過配置的形式,實(shí)現(xiàn)優(yōu)先選擇滿足條件的Node進(jìn)行調(diào)度,如果沒有,也可以調(diào)度到不滿足條件的節(jié)點(diǎn)上,使調(diào)度更加靈活。
??親和性(Affinity)主要分為三類:
- 節(jié)點(diǎn)親和性(nodeAfinity): 以node為目標(biāo),解決pod可以調(diào)度到哪些node的問題。
- pod親和性(podAffinity):以pod為目標(biāo),解決pod可以和哪些已經(jīng)存在pod部署到同一個(gè)拓?fù)溆蛑械膯栴}。
- pod反親和性(podAntiAfinity): 以pod為目標(biāo),解決pod不能和哪些已存在的pod部署在統(tǒng)一個(gè)拓?fù)溆蛑械膯栴}。
#節(jié)點(diǎn)親和性
pod.spec.nodeAffinity
● preferredDuringSchedulingIgnoredDuringExecution:軟策略
● requiredDuringSchedulingIgnoredDuringExecution:硬策略#Pod 親和性
pod.spec.affinity.podAffinity/podAntiAffinity
● preferredDuringSchedulingIgnoredDuringExecution:軟策略
● requiredDuringSchedulingIgnoredDuringExecution:硬策略
3.1 三種親和性的區(qū)別
調(diào)度策略 | 匹配標(biāo)簽 | 操作符 | 拓?fù)溆蛑С?/th> | 調(diào)度目標(biāo) |
---|---|---|---|---|
nodeAffinity | 主機(jī) | In,NotIn,Exisits,DoesNotExist,Gt,Lt | 否 | 指定主機(jī) |
podAffinity | Pod | In,NotIn,Exisits,DoesNotExist | 是 | Pod與指定Pod同一拓?fù)溆?/td> |
podAntiAffinity | Pod | In,NotIn,Exisits,DoesNotExist | 是 | Pod與指定Pod不在同一拓?fù)溆?/td> |
3.2 鍵值運(yùn)算關(guān)系
In #label 的值在某個(gè)列表中
NotIn #label 的值不在某個(gè)列表中
Gt #label 的值大于某個(gè)值
Lt #label 的值小于某個(gè)值
Exists #某個(gè) label 存在
DoesNotExist #某個(gè) label 不存在
3.3 節(jié)點(diǎn)親和性
kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master01 Ready control-plane,master 110m v1.20.15 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master01,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=
node01 Ready <none> 107m v1.20.15 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> 107m v1.20.15 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node02,kubernetes.io/os=linux
硬策略
#requiredDuringSchedulingIgnoredDuringExecution:硬策略
mkdir /opt/affinity
cd /opt/affinityvim pod1.yaml
apiVersion: v1
kind: Pod
metadata:name: affinitylabels:app: node-affinity-pod
spec:containers:- name: with-node-affinityimage: nginxaffinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: kubernetes.io/hostname #指定node的標(biāo)簽operator: NotIn #設(shè)置Pod安裝到kubernetes.io/hostname的標(biāo)簽值不在values列表中的node上values:- node02
kubectl apply -f pod1.yamlkubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
affinity 1/1 Running 0 13s 10.244.1.30 node01 <none> <none>kubectl delete pod --all && kubectl apply -f pod1.yaml && kubectl get pods -o wide#如果硬策略不滿足條件,Pod 狀態(tài)一直會(huì)處于 Pending 狀態(tài)。
軟策略
#preferredDuringSchedulingIgnoredDuringExecution:軟策略
vim pod2.yaml
apiVersion: v1
kind: Pod
metadata:name: affinitylabels:app: node-affinity-pod
spec:containers:- name: with-node-affinityimage: nginxaffinity:nodeAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 1 #如果有多個(gè)軟策略選項(xiàng)的話,權(quán)重越大,優(yōu)先級(jí)越高preference:matchExpressions:- key: kubernetes.io/hostnameoperator: Invalues:- node02kubectl apply -f pod2.yamlkubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
affinity 1/1 Running 0 5s 10.244.2.35 node02 <none> <none>
#把values:的值改成node01,則會(huì)優(yōu)先在node01上創(chuàng)建Pod
kubectl delete pod --all
kubectl apply -f pod2.yaml
kubectl get pods -o wide
硬策略和軟策略結(jié)合使用
#如果把硬策略和軟策略合在一起使用,則要先滿足硬策略之后才會(huì)滿足軟策略
kubectl label nodes node01 class=a
kubectl label nodes node02 class=b
kubectl get nodes --show-labels
vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:name: affinitylabels:app: node-affinity-pod
spec:containers:- name: with-node-affinityimage: nginxaffinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution: #先滿足硬策略,排除有kubernetes.io/hostname=node02標(biāo)簽的節(jié)點(diǎn)nodeSelectorTerms:- matchExpressions:- key: kubernetes.io/hostnameoperator: NotInvalues:- node02preferredDuringSchedulingIgnoredDuringExecution: #再滿足軟策略,優(yōu)先選擇有class=a標(biāo)簽的節(jié)點(diǎn)- weight: 1preference:matchExpressions:- key: classoperator: Invalues:- akubectl apply -f pod3.yaml
kubectl get pods -o wide
3.4 Pod 親和性
#創(chuàng)建標(biāo)簽
kubectl label nodes node01 class=a
kubectl label nodes node02 class=a#創(chuàng)建一個(gè)標(biāo)簽為 app=myapp01 的 Pod
vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:name: myapp01labels:app: myapp01
spec:containers:- name: with-node-affinityimage: soscscs/myapp:v1kubectl apply -f pod3.yamlkubectl get pods --show-labels -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
myapp01 1/1 Running 0 37s 10.244.2.3 node01 <none> <none> app=myapp01
#使用 Pod 親和性調(diào)度,創(chuàng)建多個(gè) Pod 資源
vim pod4.yaml
apiVersion: v1
kind: Pod
metadata:name: myapp02labels:app: myapp02
spec:containers:- name: myapp02image: nginxaffinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: appoperator: Invalues:- myapp01topologyKey: class#僅當(dāng)節(jié)點(diǎn)和至少一個(gè)已運(yùn)行且有鍵為“app”且值為“myapp01”的標(biāo)簽 的 Pod 處于同一拓?fù)溆驎r(shí),才可以將該 Pod 調(diào)度到節(jié)點(diǎn)上。 (更確切的說,如果節(jié)點(diǎn) N 具有帶有鍵 class 和某個(gè)值 V 的標(biāo)簽,則 Pod 有資格在節(jié)點(diǎn) N 上運(yùn)行,以便集群中至少有一個(gè)具有鍵 class 和值為 V 的節(jié)點(diǎn)正在運(yùn)行具有鍵“app”和值 “myapp01”的標(biāo)簽的 pod。)
#topologyKey 是節(jié)點(diǎn)標(biāo)簽的鍵。如果兩個(gè)節(jié)點(diǎn)使用此鍵標(biāo)記并且具有相同的標(biāo)簽值,則調(diào)度器會(huì)將這兩個(gè)節(jié)點(diǎn)視為處于同一拓?fù)溆蛑小?調(diào)度器試圖在每個(gè)拓?fù)溆蛑蟹胖脭?shù)量均衡的 Pod。
#如果 class 對(duì)應(yīng)的值不一樣就是不同的拓?fù)溆?。比?Pod1 在 class=a 的 Node 上,Pod2 在 class=b 的 Node 上,Pod3 在 class=a 的 Node 上,則 Pod2 和 Pod1、Pod3 不在同一個(gè)拓?fù)溆?#xff0c;而Pod1 和 Pod3在同一個(gè)拓?fù)溆颉?/span>kubectl apply -f pod4.yamlkubectl get pods --show-labels -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
myapp01 1/1 Running 0 4m22s 10.244.1.15 node01 <none> <none> app=myapp01
myapp02 1/1 Running 0 39s 10.244.2.4 node02 <none> <none> app=myapp02
3.5 Pod 反親和性
vim pod5.yaml
apiVersion: v1
kind: Pod
metadata:name: myapp10labels:app: myapp10
spec:containers:- name: myapp10image: nginxaffinity:podAntiAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 100podAffinityTerm:labelSelector:matchExpressions:- key: appoperator: Invalues:- myapp01topologyKey: kubernetes.io/hostname#如果節(jié)點(diǎn)處于 Pod 所在的同一拓?fù)溆蚯揖哂墟I“app”和值“myapp01”的標(biāo)簽, 則該 pod 不應(yīng)將其調(diào)度到該節(jié)點(diǎn)上。 (如果 topologyKey 為 kubernetes.io/hostname,則意味著當(dāng)節(jié)點(diǎn)和具有鍵 “app”和值“myapp01”的 Pod 處于相同的拓?fù)溆?#xff0c;Pod 不能被調(diào)度到該節(jié)點(diǎn)上。)
kubectl apply -f pod5.yamlkubectl get pods --show-labels -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
myapp01 1/1 Running 0 8m12s 10.244.1.15 node01 <none> <none> app=myapp01
myapp02 1/1 Running 0 4m29s 10.244.2.4 node02 <none> <none> app=myapp02
myapp10 1/1 Running 0 22s 10.244.2.5 node02 <none> <none> app=myapp10
vim pod6.yaml
apiVersion: v1
kind: Pod
metadata:name: myapp20labels:app: myapp20
spec:containers:- name: myapp20image: nginxaffinity:podAntiAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: appoperator: Invalues:- myapp01topologyKey: class#由于指定 Pod 所在的 node01 節(jié)點(diǎn)上具有帶有鍵 class 和標(biāo)簽值 a 的標(biāo)簽,node02 也有這個(gè)kgc=a的標(biāo)簽,所以 node01 和 node02 是在一個(gè)拓?fù)溆蛑?#xff0c;反親和要求新 Pod 與指定 Pod 不在同一拓?fù)溆?#xff0c;所以新 Pod 沒有可用的 node 節(jié)點(diǎn),即為 Pending 狀態(tài)。
kubectl get pod --show-labels -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
myapp01 1/1 Running 0 43s 10.244.1.68 node01 <none> <none> app=myapp01
myapp20 0/1 Pending 0 4s <none> <none> <none> <none> app=myapp03kubectl label nodes node02 class=b --overwritekubectl get pod --show-labels -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
myapp01 1/1 Running 0 7m40s 10.244.1.68 node01 <none> <none> app=myapp01
myapp21 1/1 Running 0 7m1s 10.244.2.65 node02 <none> <none> app=myapp03
4. 污點(diǎn)和容忍
4.1 污點(diǎn)(Taint)
??節(jié)點(diǎn)親和性,是Pod的一種屬性(偏好或硬性要求),它使Pod被吸引到一類特定的節(jié)點(diǎn)。Taint 則相反,它使節(jié)點(diǎn)能夠排斥一類特定的 Pod。
??Taint 和 Toleration 相互配合,可以用來避免 Pod 被分配到不合適的節(jié)點(diǎn)上。每個(gè)節(jié)點(diǎn)上都可以應(yīng)用一個(gè)或多個(gè) taint ,這表示對(duì)于那些不能容忍這些 taint 的 Pod,是不會(huì)被該節(jié)點(diǎn)接受的。如果將 toleration 應(yīng)用于 Pod 上,則表示這些 Pod 可以(但不一定)被調(diào)度到具有匹配 taint 的節(jié)點(diǎn)上。
??使用 kubectl taint 命令可以給某個(gè) Node 節(jié)點(diǎn)設(shè)置污點(diǎn),Node 被設(shè)置上污點(diǎn)之后就和 Pod 之間存在了一種相斥的關(guān)系,可以讓 Node 拒絕 Pod 的調(diào)度執(zhí)行,甚至將 Node 已經(jīng)存在的 Pod 驅(qū)逐出去。
#污點(diǎn)的格式
key=value:effect#每個(gè)污點(diǎn)有一個(gè) key 和 value 作為污點(diǎn)的標(biāo)簽,其中 value 可以為空,effect 描述污點(diǎn)的作用。
??當(dāng)前 taint effect 支持如下三個(gè)選項(xiàng):
選項(xiàng) | 含義 |
---|---|
NoSchedule | 表示 k8s 將不會(huì)將 Pod 調(diào)度到具有該污點(diǎn)的 Node 上。 |
PreferNoSchedule | 表示 k8s 將盡量避免將 Pod 調(diào)度到具有該污點(diǎn)的 Node 上。 |
NoExecute | 表示 k8s 將不會(huì)將 Pod 調(diào)度到具有該污點(diǎn)的 Node 上,同時(shí)會(huì)將 Node 上已經(jīng)存在的 Pod 驅(qū)逐出去。 |
kubectl get nodes
NAME STATUS ROLES AGE VERSION
master01 Ready control-plane,master 7m54s v1.20.15
node01 Ready <none> 2m58s v1.20.15
node02 Ready <none> 2m57s v1.20.15#master 就是因?yàn)橛?NoSchedule 污點(diǎn),k8s 才不會(huì)將 Pod 調(diào)度到 master 節(jié)點(diǎn)上
kubectl describe node master
......
Taints: node-role.kubernetes.io/master:NoSchedule
#設(shè)置污點(diǎn)
kubectl taint node node01 key1=value1:NoSchedule#節(jié)點(diǎn)說明中,查找 Taints 字段
kubectl describe node node01 | grep Taints #去除污點(diǎn)
kubectl taint node node01 key1:NoSchedule-
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-5fb876b6f7-6dxmn 1/1 Running 0 97s 10.244.2.4 node02 <none> <none>
myapp-5fb876b6f7-9jgb2 1/1 Running 0 97s 10.244.2.2 node02 <none> <none>
myapp-5fb876b6f7-l9fzc 1/1 Running 0 97s 10.244.2.3 node02 <none> <none>kubectl taint node node02 check=mycheck:NoExecute#查看 Pod 狀態(tài),會(huì)發(fā)現(xiàn) node02 上的 Pod 已經(jīng)被全部驅(qū)逐(注:如果是 Deployment 或者 StatefulSet 資源類型,為了維持副本數(shù)量則會(huì)在別的 Node 上再創(chuàng)建新的 Pod)
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-5fb876b6f7-pf9dd 0/1 Pending 0 0s <none> node02 <none> <none>
4.2 容忍(Tolerations)
??設(shè)置了污點(diǎn)的 Node 將根據(jù) taint 的 effect:NoSchedule
、PreferNoSchedule
、NoExecute
和 Pod 之間產(chǎn)生互斥的關(guān)系,Pod 將在一定程度上不會(huì)被調(diào)度到 Node 上。但我們可以在 Pod 上設(shè)置容忍(Tolerations),意思是設(shè)置了容忍的 Pod 將可以容忍污點(diǎn)的存在,可以被調(diào)度到存在污點(diǎn)的 Node 上。
kubectl taint node node01 check=mycheck:NoExecutevim pod3.yaml
apiVersion: v1
kind: Pod
metadata:name: myapp01labels:app: myapp01
spec:containers:- name: with-node-affinityimage: nginxkubectl apply -f pod3.yaml
#在兩個(gè) Node 上都設(shè)置了污點(diǎn)后,此時(shí) Pod 將無法創(chuàng)建成功
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp01 0/1 Pending 0 17s <none> <none> <none> <none>vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:name: myapp01labels:app: myapp01
spec:containers:- name: with-node-affinityimage: nginxtolerations:- key: "check"operator: "Equal"value: "mycheck"effect: "NoExecute"tolerationSeconds: 3600#其中的 key、vaule、effect 都要與 Node 上設(shè)置的 taint 保持一致
#operator 的值為 Exists 將會(huì)忽略 value 值,即存在即可
#tolerationSeconds 用于描述當(dāng) Pod 需要被驅(qū)逐時(shí)可以在 Node 上繼續(xù)保留運(yùn)行的時(shí)間
kubectl apply -f pod3.yaml#在設(shè)置了容忍之后,Pod 創(chuàng)建成功
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp01 1/1 Running 0 10m 10.244.1.5 node01 <none> <none>
其它注意事項(xiàng)
#1.當(dāng)不指定 key 值時(shí),表示容忍所有的污點(diǎn) keytolerations:- operator: "Exists"#2.當(dāng)不指定 effect 值時(shí),表示容忍所有的污點(diǎn)作用tolerations:- key: "key"operator: "Exists"#3.有多個(gè) Master 存在時(shí),防止資源浪費(fèi),可以如下設(shè)置
kubectl taint node Master-Name node-role.kubernetes.io/master=:PreferNoSchedule#如果某個(gè) Node 更新升級(jí)系統(tǒng)組件,為了防止業(yè)務(wù)長時(shí)間中斷,可以先在該 Node 設(shè)置 NoExecute 污點(diǎn),把該 Node 上的 Pod 都驅(qū)逐出去
kubectl taint node node01 check=mycheck:NoExecute#此時(shí)如果別的 Node 資源不夠用,可臨時(shí)給 Master 設(shè)置 PreferNoSchedule 污點(diǎn),讓 Pod 可在 Master 上臨時(shí)創(chuàng)建
kubectl taint node master node-role.kubernetes.io/master=:PreferNoSchedule#待所有 Node 的更新操作都完成后,再去除污點(diǎn)
kubectl taint node node01 check=mycheck:NoExecute-
5. cordon 和 drain
##對(duì)節(jié)點(diǎn)執(zhí)行維護(hù)操作:
kubectl get nodes
??將 Node 標(biāo)記為不可調(diào)度的狀態(tài),這樣就不會(huì)讓新創(chuàng)建的 Pod 在此 Node 上運(yùn)行。
#命令格式
kubectl cordon <NODE_NAME> #該node將會(huì)變?yōu)镾chedulingDisabled狀態(tài)
??kubectl drain 可以讓 Node 節(jié)點(diǎn)開始釋放所有 pod,并且不接收新的 pod 進(jìn)程。drain 本意排水,意思是將出問題的 Node 下的 Pod 轉(zhuǎn)移到其它 Node 下運(yùn)行。
#命令格式
kubectl drain <NODE_NAME> --ignore-daemonsets --delete-emptydir-data --force--------------------------------------------------------------------------------
--ignore-daemonsets:無視 DaemonSet 管理下的 Pod。
--delete-emptydir-data:如果有 mount local volume 的 pod,會(huì)強(qiáng)制殺掉該 pod。
--force:強(qiáng)制釋放不是控制器管理的 Pod。
??注:執(zhí)行 drain 命令,會(huì)自動(dòng)做了兩件事情:
- 設(shè)定此 node 為不可調(diào)度狀態(tài)(cordon)
- evict(驅(qū)逐)了 Pod
#kubectl uncordon 將 Node 標(biāo)記為可調(diào)度的狀態(tài)
kubectl uncordon <NODE_NAME>
6. Pod啟動(dòng)階段(相位 phase)
??Pod 創(chuàng)建完之后,一直到持久運(yùn)行起來,中間有很多步驟,也就有很多出錯(cuò)的可能,因此會(huì)有很多不同的狀態(tài)。
??一般來說,pod 這個(gè)過程包含以下幾個(gè)步驟:
- 調(diào)度到某臺(tái) node 上。kubernetes 根據(jù)一定的優(yōu)先級(jí)算法選擇一臺(tái) node 節(jié)點(diǎn)將其作為 Pod 運(yùn)行的 node;
- 拉取鏡像;
- 掛載存儲(chǔ)配置等;
- 容器運(yùn)行起來。如果有健康檢查,會(huì)根據(jù)檢查的結(jié)果來設(shè)置其狀態(tài)。
7. phase 的可能狀態(tài)有
- Pending:表示APIServer創(chuàng)建了Pod資源對(duì)象并已經(jīng)存入了etcd中,但是它并未被調(diào)度完成(比如還沒有調(diào)度到某臺(tái)node上),或者仍然處于從倉庫下載鏡像的過程中。
- Running:Pod已經(jīng)被調(diào)度到某節(jié)點(diǎn)之上,并且Pod中所有容器都已經(jīng)被kubelet創(chuàng)建。至少有一個(gè)容器正在運(yùn)行,或者正處于啟動(dòng)或者重啟狀態(tài)(也就是說Running狀態(tài)下的Pod不一定能被正常訪問)。
- Succeeded:有些pod不是長久運(yùn)行的,比如job、cronjob,一段時(shí)間后Pod中的所有容器都被成功終止,并且不會(huì)再重啟。需要反饋任務(wù)執(zhí)行的結(jié)果。
- Failed:Pod中的所有容器都已終止了,并且至少有一個(gè)容器是因?yàn)槭〗K止。也就是說,容器以非0狀態(tài)退出或者被系統(tǒng)終止,比如 command 寫的有問題。
- Unknown:表示無法讀取 Pod 狀態(tài),通常是 kube-controller-manager 無法與 Pod 通信。Pod 所在的 Node 出了問題或失聯(lián),從而導(dǎo)致 Pod 的狀態(tài)為 Unknow。
8. 如何刪除 Unknown 狀態(tài)的 Pod ?
- 從集群中刪除有問題的 Node。使用公有云時(shí),kube-controller-manager 會(huì)在 VM 刪除后自動(dòng)刪除對(duì)應(yīng)的 Node。 而在物理機(jī)部署的集群中,需要管理員手動(dòng)刪除 Node(
kubectl delete node <node_name
>)。 - 被動(dòng)等待 Node 恢復(fù)正常,Kubelet 會(huì)重新跟 kube-apiserver 通信確認(rèn)這些 Pod 的期待狀態(tài),進(jìn)而再?zèng)Q定刪除或者繼續(xù)運(yùn)行這些 Pod。
- 主動(dòng)刪除 Pod,通過執(zhí)行
kubectl delete pod <pod_name> --grace-period=0 --force
強(qiáng)制刪除 Pod。但是這里需要注意的是,除非明確知道 Pod 的確處于停止?fàn)顟B(tài)(比如 Node 所在 VM 或物理機(jī)已經(jīng)關(guān)機(jī)),否則不建議使用該方法。特別是 StatefulSet 管理的 Pod,強(qiáng)制刪除容易導(dǎo)致腦裂或者數(shù)據(jù)丟失等問題。
9. 故障排除步驟
#查看Pod事件
kubectl describe TYPE NAME_PREFIX #查看Pod日志(Failed狀態(tài)下)
kubectl logs <POD_NAME> [-c Container_NAME]#進(jìn)入Pod(狀態(tài)為running,但是服務(wù)沒有提供)
kubectl exec –it <POD_NAME> bashkubectl debug -it <POD_NAME> --image=busybox:1.28 --target=${container_name} #在為 Pod 里的具體某個(gè)容器添加一個(gè)臨時(shí)容器(鏡像為 busybox),并進(jìn)行 debug。#查看集群信息
kubectl get nodes#發(fā)現(xiàn)集群狀態(tài)正常
kubectl cluster-info#查看kubelet日志發(fā)現(xiàn)
journalctl -xefu kubelet
總結(jié)
1. K8S 如何實(shí)現(xiàn)每個(gè)組件的協(xié)作
controller-manager、scheduler、kubelet 通過 List-Watch 機(jī)制監(jiān)聽 apiserver 發(fā)出的事件,apiserver 通過 List-Watch 機(jī)制監(jiān)聽 etcd 發(fā)出的事件
2. scheduler 的調(diào)度算法
預(yù)選策略/預(yù)算策略:通過調(diào)度算法過濾掉不滿足條件的Node節(jié)點(diǎn),如果沒有滿足條件的Node節(jié)點(diǎn),Pod會(huì)處于Pending狀態(tài),直到有符合條件的Node節(jié)點(diǎn)出現(xiàn)
PodFitsResources、PodFitsHost、PodFitsHostPorts、PodSelectorMatches、NoDiskConflict優(yōu)選策略:根據(jù)優(yōu)先級(jí)選項(xiàng)為滿足預(yù)選策略條件的Node節(jié)點(diǎn)進(jìn)行優(yōu)先級(jí)權(quán)重排序,最終選擇優(yōu)先級(jí)最高的Node節(jié)點(diǎn)來調(diào)度Pod
LeastRequestedPriority、BalancedResourceAllocation、ImageLocalityPriority
3. Pod 調(diào)度到指定的 node 節(jié)點(diǎn)
1)使用 nodeName 字段指定 Node 節(jié)點(diǎn)名稱
2)使用 node Selector 指定 Node 節(jié)點(diǎn)的標(biāo)簽
3)使用 節(jié)點(diǎn)/Pod 親和性
4)使用 污點(diǎn)+容忍
4. 標(biāo)簽的管理操作
kubectl label <資源類型> <資源名稱> 標(biāo)簽key=標(biāo)簽value #添加標(biāo)簽
kubectl label <資源類型> <資源名稱> 標(biāo)簽key=標(biāo)簽value --overwrite #將標(biāo)簽進(jìn)行重寫
kubectl label <資源類型> <資源名稱> 標(biāo)簽key- #刪除標(biāo)簽kubectl get <資源類型> <資源名稱> --show-labels #查看標(biāo)簽
kubectl label <資源類型> -l 標(biāo)簽key[] #過濾標(biāo)簽
5. 親和性
節(jié)點(diǎn)親和性(nodeAffinity):匹配指定的Node節(jié)點(diǎn)標(biāo)簽,將要部署的Pod調(diào)度到滿足條件的Node節(jié)點(diǎn)上Pod親和性(podAffinity):匹配指定的Pod標(biāo)簽,將要部署的Pod調(diào)度到與指定Pod所在的Node節(jié)點(diǎn)處于 同一個(gè)拓?fù)溆?的Node節(jié)點(diǎn)上Pod反親和性(podAntiAffinity):匹配指定的Pod標(biāo)簽,將要部署的Pod調(diào)度到與指定Pod所在的Node節(jié)點(diǎn)處于 不同的拓?fù)溆?的Node節(jié)點(diǎn)上#如何判斷是否處于同一個(gè)拓?fù)溆?/span>
看拓?fù)溆?span id="vxwlu0yf4" class="token keyword">key(topologKey),如果有其他Node節(jié)點(diǎn)擁有與指定Pod所在的Node節(jié)點(diǎn)相同的 拓?fù)溆?span id="vxwlu0yf4" class="token keyword">key的標(biāo)簽key和value,那么它們就在同一個(gè)拓?fù)溆颉?
6. 親和性的策略
硬策略(required....):要強(qiáng)制性的滿足指定條件,如果沒有滿足條件的Node節(jié)點(diǎn),Pod會(huì)處于Pending狀態(tài),直到有符合條件的Node節(jié)點(diǎn)出現(xiàn)軟策略(preferred....):非強(qiáng)制性的,會(huì)優(yōu)先選擇滿足條件的Node節(jié)點(diǎn)調(diào)度,即使沒有滿足條件的Node節(jié)點(diǎn),Pod依然會(huì)完成調(diào)度
7. 污點(diǎn)和容忍
#污點(diǎn) taint
kubectl taint node <node名稱> key=value:effectNoSchedule(一定不會(huì)被調(diào)度) PreferNoSchedule(盡量不被調(diào)度) NoExecute(不會(huì)被調(diào)度,并驅(qū)逐節(jié)點(diǎn)上的Pod)kubectl taint node <node名稱> key[=value:effect]-kubectl describe nodes <node名稱> | grep Taints#容忍 tolerations
spec:tolerations:- key: 污點(diǎn)鍵名operator: Equal|Existsvalue: 污點(diǎn)鍵值effect: NoSchedule|PreferNoSchedule|NoExecute
8. 不可調(diào)度方式
#不可調(diào)度
kubectl cordon <node名稱>kubectl uncordon <node名稱>#不可調(diào)度 + 驅(qū)逐
kubectl drain <node名稱> --ignore-daemonsets --delete-emptydir-data --force
9. Pod 的啟動(dòng)過程
1)通過 scheduler 根據(jù)調(diào)度算法選擇一條在最合適的 Node 節(jié)點(diǎn)運(yùn)行 Pod
2)拉取鏡像
3)掛載 存儲(chǔ)卷 等
4)創(chuàng)建并運(yùn)行容器
5)根據(jù)容器的探針探測(cè)結(jié)果設(shè)置 Pod 狀態(tài)
10. Pod 生命周期的 5 種狀態(tài)
pending pod已經(jīng)創(chuàng)建,但處于包括pod還未完成調(diào)度到node節(jié)點(diǎn)的過程或者處于鏡像拉取過程中、存儲(chǔ)卷掛載失敗等情況
running pod中至少有一個(gè)容器正在運(yùn)行
succeeded pod中的所有容器都已經(jīng)成功退出,且不再重啟(Complated)
failed pod中的所有容器都已終止,且至少有一個(gè)容器異常退出(Error)
unkunown Master 節(jié)點(diǎn)的 controller-manager 無法獲取pod的狀態(tài),通常是因?yàn)?Maste r節(jié)點(diǎn)與pod所在的 Node 節(jié)點(diǎn)通信失聯(lián)#Pod遵循預(yù)定義的生命周期,起始于Pending階段,如果至少其中有一個(gè)主要容器正常啟動(dòng),
#則進(jìn)入Running階段,之后取決于Pod中是否有容器以失敗狀態(tài)結(jié)束而進(jìn)入Succeeded或者Failed階段。
11. K8S 中的排障手段
kubectl get pods #查看Pod運(yùn)行狀態(tài)
kubectl desrcibe <資源類型|pods> <資源名稱> #查看資源的詳細(xì)信息和事件
kubectl logs <pod名稱> [-c <容器名>] [-p] #查看容器的進(jìn)程日志
kubectl exec -it <pod名稱> [-c <容器名>] sh|bash #進(jìn)入Pod容器,查看容器內(nèi)部相關(guān)狀態(tài)信息
kubectl debug -it <pod名稱> --image=<臨時(shí)容器的鏡像名> --target=<目標(biāo)容器> #在Pod中創(chuàng)建臨時(shí)容器進(jìn)入目標(biāo)容器進(jìn)行調(diào)試在Pod容器的宿主機(jī)使用nsenter轉(zhuǎn)換網(wǎng)絡(luò)等命名空間,直接在宿主機(jī)進(jìn)入目標(biāo)容器的命名空間進(jìn)行調(diào)試。kubectl get nodes #查看Node節(jié)點(diǎn)的運(yùn)行狀態(tài)
kubectl get cs #查看Master組件的狀態(tài)
kubectl cluster-info #查看集群信息journalctl -u kubelet -f #跟蹤查看Kubelet進(jìn)程日志