一、Scheduler
當Scheduler通過API server 的watch接口監聽到新建Pod副本的信息後,它會檢查所有符合該Pod要求的Node列表,開始執行Pod調度邏輯。調度成功後將Pod綁定到目標節點上。Scheduler在整個系統中承擔了承上啓下的作用,承上是負責接收創建的新Pod,爲安排一個落腳的地(Node),啓下是安置工作完成後,目標Node上的kubelet服務進程接管後繼工作,負責Pod生命週期的後半生。具體來說,Scheduler的作用是將待調度的Pod安裝特定的調度算法和調度策略綁定到集羣中的某個合適的Node上,並將綁定信息傳給API server 寫入etcd中。整個調度過程中涉及三個對象,分別是:待調度的Pod列表,可以的Node列表,以及調度算法和策略。
Kubernetes Scheduler 提供的調度流程分三步:
1)預選策略(predicate) 遍歷nodelist,選擇出符合要求的候選節點,Kubernetes內置了多種預選規則供用戶選擇;
2)優選策略(priority) 在選擇出符合要求的候選節點中,採用優選規則計算出每個節點的積分,最後選擇得分最高的;
3)選定(select) 如果最高得分有好幾個節點,select就會從中隨機選擇一個節點。
1.常用的預選策略
- CheckNodeConditionPred 檢查節點是否正常
- GeneralPred HostName(如果pod定義hostname屬性,會檢查節點是否匹配。pod.spec.hostname)、PodFitsHostPorts(檢查pod要暴露的hostpors是否被佔用。pod.spec.containers.ports.hostPort)
- MatchNodeSelector pod.spec.nodeSelector 看節點標籤能否適配pod定義的nodeSelector
- PodFitsResources 判斷節點的資源能夠滿足Pod的定義(如果一個pod定義最少需要2C4G node上的低於此資源的將不被調度。用kubectl describe node NODE名稱 可以查看資源使用情況)
- NoDiskConflict 判斷pod定義的存儲是否在node節點上使用。(默認沒有啓用)
- PodToleratesNodeTaints 檢查pod上Tolerates的能否容忍污點(pod.spec.tolerations)
- CheckNodeLabelPresence 檢查節點上的標誌是否存在 (默認沒有啓動)
- CheckServiceAffinity 根據pod所屬的service。將相同service上的pod儘量放到同一個節點(默認沒有啓動)
- CheckVolumeBinding 檢查是否可以綁定(默認沒有啓動)
- NoVolumeZoneConflict 檢查是否在一起區域(默認沒有啓動)
- CheckNodeMemoryPressure 檢查內存是否存在壓力
- CheckNodeDiskPressure 檢查磁盤IO壓力是否過大
- CheckNodePIDPressure 檢查pid資源是否過大
2.常用的優選策略
- least_requested 選擇消耗最小的節點(根據空閒比率評估 cpu(總容量-sum(已使用)*10/總容量) )
- balanced_resource_allocation 從節點列表中選出各項資源使用率最均衡的節點(CPU和內存)
- node_prefer_avoid_pods 節點傾向
- taint_toleration 將pod對象的spec.toleration與節點的taints列表項進行匹配度檢查,匹配的條目越多,得分越低。
- selector_spreading 與services上其他pod儘量不在同一個節點上,節點上通一個service的pod越少得分越高。
- interpod_affinity 遍歷node上的親和性條目,匹配項越多的得分越高
- most_requested 選擇消耗最大的節點上(儘量將一個節點上的資源用完)
- node_label 根據節點標籤得分,存在標籤既得分,沒有標籤沒得分。標籤越多 得分越高。
- image_locality 節點上有所需要的鏡像既得分,所需鏡像越多得分越高。(根據已有鏡像體積大小之和)
四、高級調度方式
- 節點選擇器: nodeSelector、nodeName
- 節點親和性調度: nodeAffinity
- Pod親和性調度:PodAffinity
- Pod反親和性調度:podAntiAffinity
1.nodeSelector
我們定義一個pod,讓其選擇帶有node=ssd這個標籤的節點
# cat > test.yaml<<EOF
apiVersion: v1
kind: Pod
metadata:
name: pod-1
labels:
name: myapp
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
nodeSelector:
node: ssd
EOF
# kubectl create -f test.yaml
查看信息
//get一下pod 一直處於Pending狀態
# kubectl get pod
NAME READY STATUS RESTARTS AGE
pod-1 0/1 Pending 0 7s
//查看詳細信息,是沒有可用的selector
# kubectl describe pod pod-1
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 9s (x14 over 36s) default-scheduler 0/4 nodes are available: 4 node(s) didn't match node selector.
//我們給node2打上這個標籤
# kubectl label node k8s-node02 node=ssd
node/k8s-node02 labeled
//Pod正常啓動
# kubectl describe pod pod-1
....
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 2m (x122 over 8m) default-scheduler 0/4 nodes are available: 4 node(s) didn't match node selector.
Normal Pulled 7s kubelet, k8s-node02 Container image "ikubernetes/myapp:v1" already present on machine
Normal Created 7s kubelet, k8s-node02 Created container
Normal Started 7s kubelet, k8s-node02 Started container
2.nodeAffinity
kubectl explain pod.spec.affinity.nodeAffinity
1)requiredDuringSchedulingIgnoredDuringExecution 硬親和性 必須滿足親和性。
- matchExpressions 匹配表達式,這個標籤可以指定一段,例如pod中定義的key爲zone,operator爲In(包含那些),values爲 foo和bar。就是在node節點中包含foo和bar的標籤中調度
- matchFields 匹配字段 和上面的意思 不過他可以不定義標籤值,可以定義
2)preferredDuringSchedulingIgnoredDuringExecution 軟親和性 能滿足最好,不滿足也沒關係。
- preference 優先級
- weight 權重1-100範圍內,對於滿足所有調度要求的每個節點,調度程序將通過迭代此字段的元素計算總和,並在節點與對應的節點匹配時將“權重”添加到總和。
運算符包含:In,NotIn,Exists,DoesNotExist,Gt,Lt。可以使用NotIn和DoesNotExist實現節點反關聯行爲。
硬親和性:
# cat > pod-affinity-demo.yaml<<EOF
apiVersion: v1
kind: Pod
metadata:
name: node-affinity-pod
labels:
name: myapp
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: zone
operator: In
values:
- foo
- bar
EOF
# kubectl apply -f pod-affinity-demo.yaml
# kubectl describe pod node-affinity-pod
.....
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 33s (x25 over 1m) default-scheduler 0/4 nodes are available: 4 node(s) didn't match node selector.
//給其中一個node打上foo的標籤
# kubectl label node k8s-node03 zone=foo
# kubectl get pods
NAME READY STATUS RESTARTS AGE
node-affinity-pod 1/1 Running 0 8m
軟親和性:
與requiredDuringSchedulingIgnoredDuringExecution比較,這裏需要注意的是preferredDuringSchedulingIgnoredDuringExecution是個列表項,而preference不是一個列表項了。
# cat > pod-affinity-demo.yaml<<EOF
apiVersion: v1
kind: Pod
metadata:
name: node-affinity-pod-2
labels:
name: myapp
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 50
preference:
matchExpressions:
- key: zone
operator: In
values:
- foo
- bar
EOF
# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
node-affinity-pod 1/1 Running 0 3h 10.244.3.2 k8s-node03
node-affinity-pod-2 1/1 Running 0 1m 10.244.3.3 k8s-node03
3.PodAffinity
Pod親和性場景,我們的k8s集羣的節點分佈在不同的區域或者不同的機房,當服務A和服務B要求部署在同一個區域或者同一機房的時候,我們就需要親和性調度了。
kubectl explain pod.spec.affinity.podAffinity 和NodeAffinity是一樣的,都是有硬親和性和軟親和性
硬親和性:
- labelSelector 選擇跟那組Pod親和
- namespaces 選擇哪個命名空間
- topologyKey 指定節點上的哪個鍵
apiVersion: v1
kind: Pod
metadata:
name: node-affinity-pod1
labels:
name: podaffinity-myapp
tier: service
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
name: node-affinity-pod2
labels:
name: podaffinity-myapp
tier: front
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: name
operator: In
values:
- podaffinity-myapp
topologyKey: kubernetes.io/hostname
查看信息
# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
node-affinity-pod1 1/1 Running 0 12s 10.244.2.6 k8s-node02
node-affinity-pod2 1/1 Running 0 12s 10.244.2.5 k8s-node02
4.podAntiAffinity
Pod反親和性場景,當應用服務A和數據庫服務B要求儘量不要在同一臺節點上的時候。
kubectl explain pod.spec.affinity.podAntiAffinity 也分爲硬反親和性和軟反親和性調度(和podAffinity一樣的配置)
示例:
//首先把兩個node打上同一個標籤。
# kubectl label node k8s-node02 zone=foo
# kubectl label node k8s-node03 zone=foo
//反硬親和調度
apiVersion: v1
kind: Pod
metadata:
name: node-affinity-pod1
labels:
name: podaffinity-myapp
tier: service
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
name: node-affinity-pod2
labels:
name: podaffinity-myapp
tier: front
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: name
operator: In
values:
- podaffinity-myapp
topologyKey: zone
查看一下(因爲zone這個key在每個node都有會,所以第二個Pod沒有辦法調度,所以一直Pending狀態)
# kubectl get pod
NAME READY STATUS RESTARTS AGE
node-affinity-pod1 1/1 Running 0 11s
node-affinity-pod2 0/1 Pending 0 11s
五、污點容忍調度(Taint和Toleration)
前兩種方式都是pod選擇那個pod,污點調度是node選擇的pod,污點就是定義在節點上的鍵值屬性數據。舉要作用是讓節點拒絕pod,拒絕不合法node規則的pod。Taint(污點)和 Toleration(容忍)是相互配合的,可以用來避免 pod 被分配到不合適的節點上,每個節點上都可以應用一個或多個 taint ,這表示對於那些不能容忍這些 taint 的 pod,是不會被該節點接受的。
1.Taint
Taint是節點上屬性,我們看一下Taints如何定義
kubectl explain node.spec.taints(對象列表)
- key 定義一個key
- value 定義一個值
- effect pod不能容忍這個污點時,他的行爲是什麼,行爲分爲三種:NoSchedule 僅影響調度過程,對現存的pod不影響。PreferNoSchedule 系統將盡量避免放置不容忍節點上污點的pod,但這不是必需的。就是軟版的NoSchedule NoExecute 既影響調度過程,也影響現存的pod,不滿足的pod將被驅逐。
node 做taint標記:
//語法:kubectl taint NODE NAME KEY_1=VAL_1:TAINT_EFFECT_1 ... KEY_N=VAL_N:TAINT_EFFECT_N [options]
//增加taint
# kubectl taint node k8s-node02 node-type=prod:NoSchedule
//刪除taint
# kubectl taint node k8s-node02 node-type:NoSchedule-
2.Tolerations
- key 被容忍的key
- tolerationSeconds 被驅逐的寬限時間,默認是0 就是立即被驅逐
- value 被容忍key的值
- operator Exists只要key在就可以調度,Equal(等值比較)必須是值要相同
- effect 節點調度後的操作
示例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myapp
release: dev
template:
metadata:
labels:
app: myapp
release: dev
spec:
containers:
- name: myapp-containers
image: ikubernetes/myapp:v2
ports:
- name: http
containerPort: 80
tolerations:
- key: "node-type"
operator: "Equal"
value: "prod"
effect: "NoSchedule"