Kubernetes Scheduling
Kubernetes作為一個(gè)容器編排調(diào)度引擎般码,資源調(diào)度是它的最基本也是最重要的功能束倍。當(dāng)我們部署一個(gè)應(yīng)用時(shí)它運(yùn)行在哪個(gè)節(jié)點(diǎn)被丧?這個(gè)節(jié)點(diǎn)滿不滿足我們的運(yùn)行要求?Kubernetes又是如何進(jìn)行資源調(diào)度的呢绪妹?今天將通過本文帶你了解到以下信息:
- 了解資源請(qǐng)求及限制對(duì)pod調(diào)度的影響
- 查看調(diào)度事件events
- 了解label選擇器對(duì)pod調(diào)度的影響
- 了解節(jié)點(diǎn)親和性和Pod親和性對(duì)調(diào)度的影響
- 不使用調(diào)度器甥桂, 手動(dòng)調(diào)度一個(gè)pod
- 了解Daemonset的角色
- 了解如何配置kubernetes scheduler
在Kubernetes中有一個(gè)kube-scheduler組件,該組件運(yùn)行在master節(jié)點(diǎn)上邮旷,它主要負(fù)責(zé)pod的調(diào)度黄选。kube-scheduler監(jiān)聽kube-apiserver中是否有還未調(diào)度到node上的pod(即Spec.NodeName為空的Pod),再通過特定的算法為pod指定分派node運(yùn)行婶肩。如果分配失敗办陷,則將該pod放置調(diào)度隊(duì)列尾部以重新調(diào)度。調(diào)度主要分為幾個(gè)部分:首先是預(yù)選過程律歼,過濾不滿足Pod要求的節(jié)點(diǎn)民镜。然后是優(yōu)選過程,對(duì)通過要求的節(jié)點(diǎn)進(jìn)行優(yōu)先級(jí)排序苗膝,最后選擇優(yōu)先級(jí)最高的節(jié)點(diǎn)分配殃恒。其中涉及到的兩個(gè)關(guān)鍵點(diǎn)是過濾和優(yōu)先級(jí)評(píng)定的算法。調(diào)度器使用一組規(guī)則過濾不符合要求的節(jié)點(diǎn)辱揭,其中包括設(shè)置了資源的request和指定了Nodename或者其他親和性設(shè)置等等离唐。優(yōu)先級(jí)評(píng)定將過濾得到的節(jié)點(diǎn)列表進(jìn)行打分,調(diào)度器考慮一些整體的優(yōu)化策略问窃,比如將Deployment控制的多個(gè)副本集分配到不同節(jié)點(diǎn)上等等亥鬓。
資源請(qǐng)求及限制對(duì)pod調(diào)度的影響
在部署應(yīng)用時(shí),我們會(huì)考慮到使這個(gè)應(yīng)用運(yùn)行起來需要多少的內(nèi)存和CPU資源的使用量域庇,這樣才能判斷應(yīng)將他運(yùn)行在哪個(gè)節(jié)點(diǎn)上丸冕。在部署文件resource屬性中添加requests字段用于說明運(yùn)行該容器所需的最少資源,當(dāng)調(diào)度器開始調(diào)度該P(yáng)od時(shí)莲蜘,調(diào)度程序確保對(duì)于每種資源類型静檬,計(jì)劃容器的資源請(qǐng)求總和必須小于節(jié)點(diǎn)的容量才能分配該節(jié)點(diǎn)運(yùn)行Pod,resource屬性中添加limits字段用于限制容器運(yùn)行時(shí)所獲得的最大資源尉姨。如果該容器超出其內(nèi)存限制庵朝,則可能被終止。 如果該容器可以重新啟動(dòng),kubelet會(huì)將它從新啟動(dòng)九府。如果調(diào)度器找不到合適的節(jié)點(diǎn)運(yùn)行Pod時(shí)椎瘟,就會(huì)產(chǎn)生調(diào)度失敗事件,調(diào)度器會(huì)將Pod放置調(diào)度隊(duì)列以循環(huán)調(diào)度侄旬,直到調(diào)度完成肺蔚。
在下面例子中,運(yùn)行一個(gè)nginx Pod儡羔,資源請(qǐng)求了256Mi的內(nèi)存和100m的CPU宣羊,調(diào)度器將判斷哪個(gè)節(jié)點(diǎn)還剩余這么多的資源,尋找到了之后就會(huì)將這個(gè)Pod調(diào)度上去笔链。同時(shí)也設(shè)置了512Mi的內(nèi)存和300m的CPU的使用限制段只,如果該P(yáng)od運(yùn)行之后超出了這一限制就將被重啟甚至被驅(qū)逐。
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "300m"
參考文檔:
查看調(diào)度事件events
在部署應(yīng)用后鉴扫,可以使用kubectl describe
命令進(jìn)行查看Pod的調(diào)度事件赞枕,下面是一個(gè)coredns被成功調(diào)度到node3運(yùn)行的事件記錄。
$ kubectl describe po coredns-5679d9cd77-d6jp6 -n kube-system
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 29s default-scheduler Successfully assigned kube-system/coredns-5679d9cd77-d6jp6 to node3
Normal Pulled 28s kubelet, node3 Container image "grc.io/kubernetes/coredns:1.2.2" already present on machine
Normal Created 28s kubelet, node3 Created container
Normal Started 28s kubelet, node3 Started container
下面是一個(gè)coredns被調(diào)度失敗的事件記錄坪创,根據(jù)記錄顯示不可調(diào)度的原因是沒有節(jié)點(diǎn)滿足該P(yáng)od的內(nèi)存請(qǐng)求炕婶。
$ kubectl describe po coredns-8447874846-5hpmz -n kube-system
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 22s (x3 over 24s) default-scheduler 0/3 nodes are available: 3 Insufficient memory.
label選擇器對(duì)pod調(diào)度的影響
例如我們需要部署一個(gè)ES集群,由于ES對(duì)磁盤有較高的要求莱预,而集群中只有一部分節(jié)點(diǎn)有SSD磁盤柠掂,那么我們就需要將標(biāo)記一下帶有SSD磁盤的節(jié)點(diǎn)即給這些節(jié)點(diǎn)打上Lable,讓ES的pod只能運(yùn)行在帶這些標(biāo)記的節(jié)點(diǎn)上依沮。
Lable是附著在K8S對(duì)象(如Pod涯贞、Service等)上的鍵值對(duì)。它可以在創(chuàng)建對(duì)象的時(shí)候指定危喉,也可以在對(duì)象創(chuàng)建后隨時(shí)指定宋渔。Kubernetes最終將對(duì)labels最終索引和反向索引用來優(yōu)化查詢和watch,在UI和命令行中會(huì)對(duì)它們排序辜限。通俗的說皇拣,就是為K8S對(duì)象打上各種標(biāo)簽,方便選擇和調(diào)度薄嫡。
- 查看節(jié)點(diǎn)信息
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
node1 Ready etcd,master 128m v1.12.4
node2 Ready etcd,lb,master 126m v1.12.4
node3 Ready etcd,lb,worker 126m v1.12.4
- 選擇出有SSD磁盤的節(jié)點(diǎn)氧急, 并給這個(gè)節(jié)點(diǎn)打上標(biāo)記(label)
$ kubectl label nodes <your-node-name> disktype=ssd
node/<your-node-name> labeled
- 驗(yàn)證節(jié)點(diǎn)上是否有成功打上對(duì)應(yīng)label
$ kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
node1 Ready etcd,master 139m v1.12.4 ...disktype=ssd,kubernetes.io/hostname=node1...
node2 Ready etcd,lb,master 137m v1.12.4 ...kubernetes.io/hostname=node2...
node3 Ready etcd,lb,worker 137m v1.12.4 ...kubernetes.io/hostname=node3...
- 創(chuàng)建一個(gè)ES的pod, 調(diào)度到有SSD磁盤標(biāo)記的節(jié)點(diǎn)上
在pod的配置里毫深, 要指定nodeSelector屬性值為disktype:ssd吩坝。 這意味著pod啟動(dòng)后會(huì)調(diào)度到打上了disktype=ssd標(biāo)簽的node上
apiVersion: v1
kind: Pod
metadata:
name: es
spec:
containers:
- name: es
image: es
nodeSelector:
disktype: ssd
- 驗(yàn)證pod啟動(dòng)后是否調(diào)度到指定節(jié)點(diǎn)上
$ kubectl get pods -o wide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
default es-5679d9cd77-sbmcx 1/1 Running 0 134m 10.244.2.3 node1 <none>
- 參考文檔 Assign Pods to Nodes
節(jié)點(diǎn)親和性和Pod親和性對(duì)調(diào)度的影響
上小節(jié)講述的nodeSelector提供了一種非常簡(jiǎn)單的方法,可以將pod限制為具有特定標(biāo)簽的節(jié)點(diǎn)哑蔫。而更為強(qiáng)大的表達(dá)約束類型則可以由Affinity和Anti-affinity來配置钾恢。即親和性與反親和性的設(shè)置手素。親和性和反親和性包括兩種類型:節(jié)點(diǎn)(反)親和性與Pod(反)親和性。Node affinity與NodeSelector很相似瘩蚪,它允許你根據(jù)節(jié)點(diǎn)上的標(biāo)簽限制你的pod可以在哪些節(jié)點(diǎn)上進(jìn)行調(diào)度。目前有兩種類型的節(jié)點(diǎn)關(guān)聯(lián)稿黍,稱為requiredDuringSchedulingIgnoredDuringExecution和 preferredDuringSchedulingIgnoredDuringExecution疹瘦。可以將它們分別視為“硬規(guī)則”和“軟規(guī)則”巡球,前者指定了要將 pod調(diào)度到節(jié)點(diǎn)上必須滿足的規(guī)則言沐,而后者指定調(diào)度程序?qū)L試強(qiáng)制但不保證的首選項(xiàng)。名稱中的“IgnoredDuringExecution”部分意味著酣栈,類似于nodeSelector工作方式险胰,如果節(jié)點(diǎn)上的標(biāo)簽在運(yùn)行時(shí)更改,不再滿足pod上的關(guān)聯(lián)性規(guī)則矿筝,pod仍將繼續(xù)在該節(jié)點(diǎn)上運(yùn)行起便。Pod affinity強(qiáng)調(diào)的是同一個(gè)節(jié)點(diǎn)中Pod之間的親和力〗盐可以根據(jù)已在節(jié)點(diǎn)上運(yùn)行的pod上的標(biāo)簽來約束pod可以調(diào)度哪些節(jié)點(diǎn)上榆综。比如希望運(yùn)行該P(yáng)od到某個(gè)已經(jīng)運(yùn)行了Pod標(biāo)簽為app=webserver的節(jié)點(diǎn)上,就可以使用Pod affinity來表達(dá)這一需求铸史。目前有兩種類型Pod親和力和反親和力鼻疮,稱為requiredDuringSchedulingIgnoredDuringExecution以及 preferredDuringSchedulingIgnoredDuringExecution,其中表示“硬規(guī)則”與“軟規(guī)則”的要求琳轿。類似于Node affinity判沟,IgnoredDuringExecution部分表示如果在Pod運(yùn)行期間改變了Pod標(biāo)簽導(dǎo)致親和性不滿足以上規(guī)則,則pod仍將繼續(xù)在該節(jié)點(diǎn)上運(yùn)行崭篡。無論是Selector還是Affinity挪哄,都是基于Pod或者Node的標(biāo)簽來表達(dá)約束類型。從而讓調(diào)度器按照約束規(guī)則來調(diào)度Pod運(yùn)行在合理的節(jié)點(diǎn)上媚送。
節(jié)點(diǎn)親和性如下所示中燥,其中親和性定義為:該pod只能放置在一個(gè)含有鍵為kubernetes.io/hostname并且值為node1或者node2標(biāo)簽的節(jié)點(diǎn)上。此外塘偎,在滿足該標(biāo)準(zhǔn)的節(jié)點(diǎn)中疗涉,具有其鍵為app且值為webserver的標(biāo)簽的節(jié)點(diǎn)應(yīng)該是優(yōu)選的。
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node1
- node2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: app
operator: In
values:
- webserver
containers:
- name: with-node-affinity
image: k8s.gcr.io/pause:2.0
Pod反親和性如下所示吟秩,其中反親和性定義為:在此拓?fù)溆颍ㄏ喈?dāng)于以topologyKey的值進(jìn)行的節(jié)點(diǎn)分組)中咱扣,命名空間為default下有標(biāo)簽鍵為app,標(biāo)簽值為redis的Pod時(shí)不在此Node上運(yùn)行涵防。
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
namespaces:
- default
topologyKey: kubernetes.io/hostname
containers:
- name: with-node-affinity
image: k8s.gcr.io/pause:2.0
不使用調(diào)度器闹伪, 手動(dòng)調(diào)度一個(gè)pod
Scheduling過程的本質(zhì)其實(shí)就是給Pod賦予nodeName屬性合適的值。那么在我們進(jìn)行Pod部署時(shí)就直接指定這個(gè)值是否可行呢?答案是肯定的偏瓤。如下配置杀怠,將nginx直接分配到node1上運(yùn)行。
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginx
nodeName: node1
還有一種指定節(jié)點(diǎn)的部署方式——static pod厅克,就像它名稱一樣赔退,他是一個(gè)“靜態(tài)”的Pod,它不通過apiserver证舟,直接由kubelet進(jìn)行托管硕旗。在kubelet的啟動(dòng)參數(shù)中--pod-manifest-path=DIR
,這里的DIR
就是放置static pod的編排文件的目錄女责。把static pod的編排文件放到此目錄下漆枚,kubelet就可以監(jiān)聽到變化,并根據(jù)編排文件創(chuàng)建pod抵知。還有一個(gè)啟動(dòng)參數(shù)--manifest-url=URL
墙基,kubelet會(huì)從這個(gè)URL下載編排文件,并創(chuàng)建pod辛藻。static pod有一個(gè)特性是我們使用docker或kubectl刪除static pod后碘橘, static pod還能被kubelet進(jìn)程拉起。通過這種方式保證了應(yīng)用的可用性吱肌。有點(diǎn)相當(dāng)于systemd的功能痘拆, 但比systemd好的一點(diǎn)是, static pod的鏡像信息會(huì)在apiserver中注冊(cè)氮墨。 這樣的話纺蛆, 我們就可以統(tǒng)一對(duì)部署信息進(jìn)行可視化管理。 此外static pod是容器规揪, 無需拷貝二進(jìn)制文件到主機(jī)上桥氏, 應(yīng)用封裝在鏡像里也保證了環(huán)境的一致性, 無論是應(yīng)用的編排文件還是應(yīng)用的鏡像都方便進(jìn)行版本管理和分發(fā)猛铅。
在使用kubeadm部署kubernetes集群時(shí)字支, static pod得到了大量的應(yīng)用,比如etcd奸忽、 kube-scheduler, kube-controller-manager, kube-apiserver等都是使用 static pod的方式運(yùn)行的堕伪。
使用static pod部署出來的pod名稱與其他pod有很大的不同點(diǎn),名稱中沒有“亂碼”栗菜,只是簡(jiǎn)單的將pod的name屬性值與它運(yùn)行在的node的name屬性值相連接而成欠雌。如下所示,coredns是通過Deployment部署出來的名稱中就有部分“亂碼”疙筹,而etcd富俄,kube-apiserver這種Pod就是static pod禁炒。
$ kubectl get po --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-5679d9cd77-d6jp6 1/1 Running 0 6m59s
kube-system etcd-node1 1/1 Running 0 6m58s
kube-system etcd-node2 1/1 Running 0 6m58s
kube-system etcd-node3 1/1 Running 0 6m54s
kube-system kube-proxy-nxj5d 1/1 Running 0 6m52s
kube-system kube-proxy-tz264 1/1 Running 0 6m56s
kube-system kube-proxy-zxgxc 1/1 Running 0 6m53s
了解Daemonset角色
DaemonSet是一種控制器,它確保在一些或全部Node上都運(yùn)行一個(gè)指定的Pod霍比。這些Pod就相當(dāng)于守護(hù)進(jìn)程一樣不期望被終止幕袱。當(dāng)有Node加入集群時(shí),也會(huì)為他們新增一個(gè)Pod桂塞。當(dāng)有Node從集群移除時(shí)凹蜂,對(duì)應(yīng)的Pod也會(huì)被回收。當(dāng)刪除DaemonSet時(shí)將會(huì)刪除它創(chuàng)建的所有Pod阁危。一般情況下,Pod運(yùn)行在哪個(gè)節(jié)點(diǎn)上是由Kubernates調(diào)度器選擇的汰瘫。但是在Kubernates 1.11版本之前由DaemonSet Controller創(chuàng)建的Pod在創(chuàng)建時(shí)已經(jīng)確定了在哪個(gè)節(jié)點(diǎn)上運(yùn)行(pod在創(chuàng)建的時(shí)候.spec.nodeName字段就指定了狂打, 因此會(huì)被scheduler忽略),所以即使調(diào)度器沒有啟動(dòng)DaemonSet Controller創(chuàng)建的Pod仍然也可以被分配node混弥。直到Kubernates 1.11版本趴乡,DaemonSet的pod由scheduler調(diào)度才作為alpha特性引入。在上小節(jié)中kube-proxy
就是以DaemonSet的方式進(jìn)行運(yùn)行的蝗拿。
配置Kubernetes scheduler
如果需要配置一些高級(jí)的調(diào)度策略以滿足我們的需要晾捏,可以修改默認(rèn)調(diào)度程序的配置文件。kube-scheduler在啟動(dòng)的時(shí)候可以通過--policy-config-file參數(shù)來指定調(diào)度策略文件哀托,我們可以根據(jù)我們自己的需要來組裝Predicates和Priority函數(shù)惦辛。選擇不同的過濾函數(shù)和優(yōu)先級(jí)函數(shù)。調(diào)整控制優(yōu)先級(jí)函數(shù)的權(quán)重和過濾函數(shù)的順序都會(huì)影響調(diào)度結(jié)果仓手。
官方的Policy文件如下:
kind: Policy
apiVersion: v1
predicates:
- {name: PodFitsHostPorts}
- {name: PodFitsResources}
- {name: NoDiskConflict}
- {name: NoVolumeZoneConflict}
- {name: MatchNodeSelector}
- {name: HostName}
priorities:
- {name: LeastRequestedPriority, weight: 1}
- {name: BalancedResourceAllocation, weight: 1}
- {name: ServiceSpreadingPriority, weight: 1}
- {name: EqualPriority, weight: 1}
其中predicates區(qū)域是調(diào)度的預(yù)選階段所需要的過濾算法胖齐。priorities區(qū)域是優(yōu)選階段的評(píng)分算法。
總結(jié):
我們?cè)賮砘仡櫼幌抡{(diào)度的主要構(gòu)成部分:首先是預(yù)選過程嗽冒,過濾掉不滿足Pod要求的節(jié)點(diǎn)呀伙,然后是優(yōu)選過程,對(duì)通過要求的節(jié)點(diǎn)進(jìn)行優(yōu)先級(jí)排序添坊,最后選擇優(yōu)先級(jí)最高的節(jié)點(diǎn)進(jìn)行分配剿另。當(dāng)調(diào)度器不工作時(shí)或有臨時(shí)需求可以手動(dòng)指定nodeName屬性的值,讓其不通過調(diào)度器進(jìn)行調(diào)度直接運(yùn)行在指定的節(jié)點(diǎn)上贬蛙。