k8s調(diào)度原理和策略詳解
k8s調(diào)度器Scheduler
Scheduler工作原理
請求及Scheduler調(diào)度步驟:
- 節(jié)點預(yù)選(Predicate):排除完全不滿足條件的節(jié)點,如內(nèi)存大小,端口等條件不滿足。
- 節(jié)點優(yōu)先級排序(Priority):根據(jù)優(yōu)先級選出最佳節(jié)點
- 節(jié)點擇優(yōu)(Select):根據(jù)優(yōu)先級選定節(jié)點

1.首先用戶通過 Kubernetes 客戶端 Kubectl 提交創(chuàng)建 Pod 的 Yaml 的文件,向Kubernetes 系統(tǒng)發(fā)起資源請求,該資源請求被提交到Kubernetes 系統(tǒng)中,用戶通過命令行工具 Kubectl 向 Kubernetes 集群即 APIServer 用 的方式發(fā)送“POST”請求,即創(chuàng)建 Pod 的請求。
2.APIServer 接收到請求后把創(chuàng)建 Pod 的信息存儲到 Etcd 中,從集群運行那一刻起,資源調(diào)度系統(tǒng) Scheduler 就會定時去監(jiān)控 APIServer
3.通過 APIServer 得到創(chuàng)建 Pod 的信息,Scheduler 采用 watch 機制,一旦 Etcd 存儲 Pod 信息成功便會立即通知APIServer,APIServer會立即把Pod創(chuàng)建的消息通知Scheduler,Scheduler發(fā)現(xiàn) Pod 的屬性中 Dest Node 為空時(Dest Node=””)便會立即觸發(fā)調(diào)度流程進行調(diào)度。而這一個創(chuàng)建Pod對象,在調(diào)度的過程當(dāng)中有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,當(dāng)這類節(jié)點多于1個時,則進行隨機選擇
k8s的調(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的支持。
常用預(yù)選策略

常用優(yōu)先函數(shù)

節(jié)點親和性調(diào)度
節(jié)點親和性規(guī)則:硬親和性 required 、軟親和性 preferred。
Affinity 翻譯成中文是“親和性”,它對應(yīng)的是 Anti-Affinity,我們翻譯成“互斥”。這兩個詞比較形象,可以把 pod 選擇 node 的過程類比成磁鐵的吸引和互斥,不同的是除了簡單的正負極之外,pod 和 node 的吸引和互斥是可以靈活配置的。
Affinity的優(yōu)點:
- 匹配有更多的邏輯組合,不只是字符串的完全相等
- 調(diào)度分成軟策略(soft)和硬策略(hard),在軟策略下,如果沒有滿足調(diào)度條件的節(jié)點,pod會忽略這條規(guī)則,繼續(xù)完成調(diào)度。
目前主要的node affinity:
requiredDuringSchedulingIgnoredDuringExecution
表示pod必須部署到滿足條件的節(jié)點上,如果沒有滿足條件的節(jié)點,就不停重試。其中IgnoreDuringExecution表示pod部署之后運行的時候,如果節(jié)點標簽發(fā)生了變化,不再滿足pod指定的條件,pod也會繼續(xù)運行。
requiredDuringSchedulingRequiredDuringExecution
表示pod必須部署到滿足條件的節(jié)點上,如果沒有滿足條件的節(jié)點,就不停重試。其中RequiredDuringExecution表示pod部署之后運行的時候,如果節(jié)點標簽發(fā)生了變化,不再滿足pod指定的條件,則重新選擇符合要求的節(jié)點。
preferredDuringSchedulingIgnoredDuringExecution
表示優(yōu)先部署到滿足條件的節(jié)點上,如果沒有滿足條件的節(jié)點,就忽略這些條件,按照正常邏輯部署。
preferredDuringSchedulingRequiredDuringExecution
表示優(yōu)先部署到滿足條件的節(jié)點上,如果沒有滿足條件的節(jié)點,就忽略這些條件,按照正常邏輯部署。其中RequiredDuringExecution表示如果后面節(jié)點標簽發(fā)生了變化,滿足了條件,則重新調(diào)度到滿足條件的節(jié)點。
軟策略和硬策略的區(qū)分是有用處的,硬策略適用于 pod 必須運行在某種節(jié)點,否則會出現(xiàn)問題的情況,比如集群中節(jié)點的架構(gòu)不同,而運行的服務(wù)必須依賴某種架構(gòu)提供的功能;軟策略不同,它適用于滿不滿足條件都能工作,但是滿足條件更好的情況,比如服務(wù)最好運行在某個區(qū)域,減少網(wǎng)絡(luò)傳輸?shù)?。這種區(qū)分是用戶的具體需求決定的,并沒有絕對的技術(shù)依賴。
下面是一個官方的示例:
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/e2e-az-name
operator: In
values:
- e2e-az1
- e2e-az2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
containers:
- name: with-node-affinity
image: gcr.io/google_containers/pause:2.0
這個 pod 同時定義了 requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution 兩種 nodeAffinity。第一個要求 pod 運行在特定 AZ 的節(jié)點上,第二個希望節(jié)點最好有對應(yīng)的 another-node-label-key:another-node-label-value 標簽。
這里的匹配邏輯是label在某個列表中,可選的操作符有:
- In: label的值在某個列表中
- NotIn:label的值不在某個列表中
- Exists:某個label存在
- DoesNotExist:某個label不存在
- Gt:label的值大于某個值(字符串比較)
- Lt:label的值小于某個值(字符串比較)
如果nodeAffinity中nodeSelector有多個選項,節(jié)點滿足任何一個條件即可;如果matchExpressions有多個選項,則節(jié)點必須同時滿足這些選項才能運行pod 。
需要說明的是,node并沒有anti-affinity這種東西,因為NotIn和DoesNotExist能提供類似的功能。
節(jié)點軟親和性的權(quán)重
preferredDuringSchedulingIgnoredDuringExecution
柔性控制邏輯,當(dāng)條件不滿足時,能接受被編排于其他不符合條件的節(jié)點之上
權(quán)重 weight 定義優(yōu)先級,1-100 值越大優(yōu)先級越高
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy-with-node-affinity
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
name: myapp-pod
labels:
app: myapp
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution: #節(jié)點軟親和性
- weight: 60
preference:
matchExpressions:
- {key: zone, operator: In, values: ["foo"]}
- weight: 30
preference:
matchExpressions:
- {key: ssd, operator: Exists, values: []}
containers:
- name: myapp
image: ikubernetes/myapp:v1

Pod資源親和調(diào)度
Pod對象間親和性,將一些Pod對象組織在相近的位置(同一節(jié)點、機架、區(qū)域、地區(qū))
Pod對象間反親和性,將一些Pod在運行位置上隔開
調(diào)度器將第一個Pod放置于任何位置,然后與其有親和或反親和關(guān)系的Pod據(jù)此動態(tài)完成位置編排
基于MatchInterPodAffinity預(yù)選策略完成節(jié)點預(yù)選,基于InterPodAffinityPriority優(yōu)選函數(shù)進行各節(jié)點的優(yōu)選級評估
位置拓撲,定義"同一位置"
Pod硬親和調(diào)度
requiredDuringSchedulingIgnoredDuringExecution
Pod親和性描述一個Pod與具有某特征的現(xiàn)存Pod運行位置的依賴關(guān)系;即需要事先存在被依賴的Pod對象
# 被依賴Pod
kubectl run tomcat -l app=tomcat --image tomcat:alpine
kubectl explain pod.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution.topologyKey
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬親和調(diào)度
- labelSelector:
matchExpressions: #集合選擇器
- {key: app, operator: In, values: ["tomcat"]} # 選擇被依賴Pod
# 上面意思是,當(dāng)前pod要跟標簽為app值為tomcat的pod在一起
topologyKey: kubernetes.io/hostname # 根據(jù)挑選出的Pod所有節(jié)點的hostname作為同一位置的判定
containers:
- name: myapp
image: ikubernetes/myapp:v1
Pod軟親和調(diào)度
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-with-preferred-pod-affinity
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
name: myapp
labels:
app: myapp
spec:
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
podAffinityTerm:
labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["cache"]}
topologyKey: zone
- weight: 20
podAffinityTerm:
labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["db"]}
topologyKey: zone
containers:
- name: myapp
image: ikubernetes/myapp:v1
Pod反親和調(diào)度
Pod反親和調(diào)度用于分散同一類應(yīng)用,調(diào)度至不同的區(qū)域、機架或節(jié)點等
將 spec.affinity.podAffinity替換為 spec.affinity.podAntiAffinity
反親和調(diào)度也分為柔性約束和強制約束
apiVersion: v1
kind: Pod
metadata:
name: pod-first
labels:
app: myapp
tier: fronted
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
name: pod-second
labels:
app: backend
tier: db
spec:
containers:
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c", "sleep 3600"]
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["myapp"]}
topologyKey: zone
污點和容忍度
污點 taints 是定義在節(jié)點上的鍵值型屬性數(shù)據(jù),用于讓節(jié)點拒絕將Pod調(diào)度運行于其上,除非Pod有接納節(jié)點污點的容忍度容忍度 tolerations 是定義在Pod上的鍵值屬性數(shù)據(jù),用于配置可容忍的污點,且調(diào)度器將Pod調(diào)度至其能容忍該節(jié)點污點的節(jié)點上或沒有污點的節(jié)點上
使用PodToleratesNodeTaints預(yù)選策略和TaintTolerationPriority優(yōu)選函數(shù)完成該機制
- 節(jié)點親和性使得Pod對象被吸引到一類特定的節(jié)點 (nodeSelector和affinity)
- 污點提供讓節(jié)點排斥特定Pod對象的能力
定義污點和容忍度
- 污點定義于nodes.spec.taints容忍度定義于pods.spec.tolerations
- 語法: key=value:effect
effect定義排斥等級:
- NoSchedule,不能容忍,但僅影響調(diào)度過程,已調(diào)度上去的pod不受影響,僅對新增加的pod生效。
- PreferNoSchedule,柔性約束,節(jié)點現(xiàn)存Pod不受影響,如果實在是沒有符合的節(jié)點,也可以調(diào)度上來
- NoExecute,不能容忍,當(dāng)污點變動時,Pod對象會被驅(qū)逐
在Pod上定義容忍度時:
- 等值比較 容忍度與污點在key、value、effect三者完全匹配
- 存在性判斷 key、effect完全匹配,value使用空值
- 一個節(jié)點可配置多個污點,一個Pod也可有多個容忍度
管理節(jié)點的污點
- 同一個鍵值數(shù)據(jù),effect不同,也屬于不同的污點
給節(jié)點添加污點:
kubectl taint node <node-name> <key>=<value>:<effect> kubectl taint node node2 node-type=production:NoShedule #舉例
查看節(jié)點污點:
kubectl get nodes <nodename> -o go-template={{.spec.taints}}
刪除節(jié)點污點:
kubectl taint node <node-name> <key>[:<effect>]-
kubectl patch nodes <node-name> -p '{"spec":{"taints":[]}}'
kubectl taint node kube-node1 node-type=production:NoSchedule
kubectl get nodes kube-node1 -o go-template={{.spec.taints}}
#刪除key為node-type,effect為NoSchedule的污點
kubectl taint node kube-node1 node-type:NoSchedule-
刪除key為node-type的所有污點
kubectl taint node kube-node1 node-type-
刪除所有污點
kubectl patch nodes kube-node1 -p '{"spec":{"taints":[]}}'
給Pod對象容忍度
- spec.tolerations字段添加
- tolerationSeconds用于定義延遲驅(qū)逐Pod的時長
等值判斷 tolerations: - key: "key1" operator: "Equal" #判斷條件為Equal value: "value1" effect: "NoExecute" tolerationSeconds: 3600 存在性判斷 tolerations: - key: "key1" operator: "Exists" #存在性判斷,只要污點鍵存在,就可以匹配 effect: "NoExecute" tolerationSeconds: 3600
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
domainNames: ""
exposeType: HostNetwork
io.daocloud/dce.ingress.metrics-port: "12955"
lbType: nginx
taintNodes: "false"
tpsLevel: "20000"
watchNamespace: ""
creationTimestamp: "2021-09-15T06:36:59Z"
generation: 1
labels:
ingress.loadbalancer.dce.daocloud.io/ingress-type: nginx
io.daocloud.dce.ingress.controller.name: ""
loadbalancer.dce.daocloud.io/adapter: ingress
loadbalancer.dce.daocloud.io/instance: lb01
resource.ingress.loadbalancer.dce.daocloud.io: lb01
name: lb01-ingress1
namespace: kube-system
resourceVersion: "28248"
selfLink: /apis/apps/v1/namespaces/kube-system/deployments/lb01-ingress1
uid: 760b31d3-66b0-4547-8438-fb7f972344a5
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name.ingress.loadbalancer.dce.daocloud.io/lb01: enabled
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
annotations:
prometheus.io/port: "12955"
prometheus.io/scrape: "true"
creationTimestamp: null
labels:
ingress.loadbalancer.dce.daocloud.io/ingress-type: nginx
name.ingress.loadbalancer.dce.daocloud.io/lb01: enabled
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: beta.kubernetes.io/os
operator: In
values:
- linux
- key: kubernetes.io/hostname
operator: In
values:
- dce-172-16-17-21
- dce-172-16-17-22
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: name.ingress.loadbalancer.dce.daocloud.io/lb01
operator: In
values:
- enabled
topologyKey: kubernetes.io/hostname
containers:
- args:
- /nginx-ingress-controller
- --healthz-port=12955
- --status-port=12989
- --https-port=12973
- --default-server-port=12956
- --stream-port=12943
- --profiler-port=12963
- --http-port=12987
- --configmap=kube-system/lb01-ingress
- --default-ssl-certificate=kube-system/lb01-ingress
- --tcp-services-configmap=kube-system/lb01-tcp-services
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
image: 172.16.17.250/kube-system/dce-ingress-controller:v0.46.0-1
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 12955
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
name: nginx-ingress-controller
ports:
- containerPort: 12955
hostPort: 12955
name: healthz
protocol: TCP
- containerPort: 12989
hostPort: 12989
name: status
protocol: TCP
- containerPort: 12973
hostPort: 12973
name: https
protocol: TCP
- containerPort: 12956
hostPort: 12956
name: default-server
protocol: TCP
- containerPort: 12943
hostPort: 12943
name: stream
protocol: TCP
- containerPort: 12963
hostPort: 12963
name: profiler
protocol: TCP
- containerPort: 12987
hostPort: 12987
name: http
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 12955
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
resources:
limits:
cpu: "4"
memory: 2Gi
requests:
cpu: "1"
memory: 512Mi
securityContext:
privileged: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/log
name: log-volume
dnsPolicy: ClusterFirst
hostNetwork: true
initContainers:
- args:
- -c
- ' mkdir -p /var/log/nginx; chown -hR 101:101 /var/log/nginx; mkdir -p /var/log/dce-ingress; chown
-hR 101:101 /var/log/dce-ingress; echo init-done;'
command:
- /bin/sh
image: 172.16.17.250/kube-system/dce-busybox:1.30.1
imagePullPolicy: IfNotPresent
name: init
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/log/
name: log-volume
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: lb01
serviceAccountName: lb01
terminationGracePeriodSeconds: 60
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/master
- effect: NoSchedule
key: node-role.kubernetes.io/load-balance
- key: CriticalAddonsOnly
operator: Exists
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
- effect: NoSchedule
key: node.kubernetes.io/disk-pressure
operator: Exists
- effect: NoSchedule
key: node.kubernetes.io/memory-pressure
operator: Exists
volumes:
- hostPath:
path: /var/log/
type: Directory
name: log-volume
問題節(jié)點標識
自動為節(jié)點添加污點信息,使用NoExecute效用標識,會驅(qū)逐現(xiàn)有Pod
K8s核心組件通常都容忍此類污點
- node.kubernetes.io/not-ready 節(jié)點進入NotReady狀態(tài)時自動添加
- node.alpha.kubernetes.io/unreachable 節(jié)點進入NotReachable狀態(tài)時自動添加
- node.kubernetes.io/out-of-disk 節(jié)點進入OutOfDisk狀態(tài)時自動添加
- node.kubernetes.io/memory-pressure 節(jié)點內(nèi)存資源面臨壓力
- node.kubernetes.io/disk-pressure 節(jié)點磁盤面臨壓力
- node.kubernetes.io/network-unavailable 節(jié)點網(wǎng)絡(luò)不可用
- node.cloudprovider.kubernetes.io/uninitialized kubelet由外部云環(huán)境程序啟動時,自動添加,待到去控制器初始化此節(jié)點時再將其刪除
Pod優(yōu)選級和搶占式調(diào)度
優(yōu)選級,Pod對象的重要程度
優(yōu)選級會影響節(jié)點上Pod的調(diào)度順序和驅(qū)逐次序
一個Pod對象無法被調(diào)度時,調(diào)度器會嘗試搶占(驅(qū)逐)較低優(yōu)先級的Pod對象,以便可以調(diào)度當(dāng)前Pod
Pod優(yōu)選級和搶占機制默認處于禁用狀態(tài)
- 啟用:同時為kube-apiserver、kube-scheduler、kubelet程序的 --feature-gates 添加 PodPriority=true
- 使用:事先創(chuàng)建優(yōu)先級類別,并在創(chuàng)建Pod資源時通過 priorityClassName屬性指定所屬的優(yōu)選級類別
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
StatefulSet的每個Pod單獨創(chuàng)建Service實踐
本文介紹了如何為StatefulSet的每個Pod創(chuàng)建獨立的Service,并提供了一個示例腳本,以批量生成Service定義,通過這種方式,可以實現(xiàn)從集群外部訪問每個Pod的功能,并且可以根據(jù)需要配置不同的Service類型(如NodePort、LoadBalancer、ClusterIP+Ingress等)2026-01-01
關(guān)于CentOS7日志文件及journalctl日志查看方法
這篇文章主要介紹了關(guān)于CentOS7日志文件及journalctl日志查看方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03
k8s?Service?實現(xiàn)服務(wù)發(fā)現(xiàn)和負載均衡
這篇文章主要為大家介紹了k8s?Service?實現(xiàn)服務(wù)發(fā)現(xiàn)和負載均衡的工作原理及使用方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-04-04
Rainbond調(diào)用Vue?React項目的后端接口
這篇文章主要為大家介紹了Rainbond調(diào)用Vue?React項目的后端接口問題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-04-04
ES業(yè)務(wù)數(shù)據(jù)遷移遇到的精度問題BUG
這篇文章主要為大家介紹了ES業(yè)務(wù)數(shù)據(jù)遷移遇到的BUG精度問題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06

