Pod 調度
當我們創建一個deployment或者RC後,kuernetes會自動根據我們的要求將一個或多個Pod副本自動調度到合適的節點上,這個過程kube-scheduler經過一系列算法自動完成,用戶無法幹預。在某些場景,我們也可以使用Kubernetes提供的其他調度策略來滿足我們的特殊需求。這些調度策略包括:
- NodeSelector
- NodeAffinity
- PodAffinity
- Pod驅逐
- Taints和Tolerations
參考鏈接
NodeSelector 定向調度
NodeSelector非常簡單,就是將pod調度到我們指定的Node節點上,這裏分為兩個步驟:
(1) 對Node節點打上特定的label
(2) 創建pod時指定此label.
下面是一個簡單示例:
1、對node添加標簽,並驗證:
kubectl label nodes <node-name> <label-key>=<label-value> eg: [root@node-1 ~]# kubectl get node NAME STATUS ROLES AGE VERSION 10.0.0.2 Ready <none> 9d v1.10.4 10.0.0.3 Ready <none> 9d v1.10.4 [root@node-1 ~]# kubectl label nodes 10.0.0.2 disk=ssd node "10.0.0.2" labeled [root@node-1 ~]# kubectl get nodes --show-labels NAME STATUS ROLES AGE VERSION LABELS 10.0.0.2 Ready <none> 9d v1.10.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disk=ssd,kubernetes.io/hostname=10.0.0.2 10.0.0.3 Ready <none> 9d v1.10.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10.0.0.3
- 創建一個pod,指定Label:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent # 節點上沒有nginx鏡像時才執行pull操作
nodeSelector:
disk: ssd
提示:如果指定的label在所有node上都無法匹配,則創建Pod失敗,會提示無法調度:
Warning FailedScheduling 7s (x6 over 22s) default-scheduler 0/2 nodes are available: 2 node(s) didn‘t match node selector.
調度成功後會顯示:
# kubectl describe pod nginx
...
Node-Selectors: disk=ssd
Tolerations: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 4m default-scheduler Successfully assigned nginx to 10.0.0.2
Normal SuccessfulMountVolume 4m kubelet, 10.0.0.2 MountVolume.SetUp succeeded for volume "default-token-hmvnc"
Normal Pulling 4m kubelet, 10.0.0.2 pulling image "nginx"
Normal Pulled 3m kubelet, 10.0.0.2 Successfully pulled image "nginx"
Normal Created 3m kubelet, 10.0.0.2 Created container
Normal Started 3m kubelet, 10.0.0.2 Started container
默認情況下,kubernetes也自帶了一些標簽:
# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
10.0.0.2 Ready <none> 9d v1.10.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disk=ssd,kubernetes.io/hostname=10.0.0.2
10.0.0.3 Ready <none> 9d v1.10.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10.0.0.3
NodeAffinity Node親和性調度
Node親和性調度是用於替換NodeSelector的全新調度策略,有兩種節點親和性表示:
- requiredDuringSchedulingIgnoredDuringExecution:必須滿足指定的規則才可以調度Pod到Node上(功能與nodeSelector很像,語法不同),相當於硬限制。
- preferredDuringSchedulingIgnoredDuringExecution:強調優先級,可以設置權重,但不是強制的,相當於軟限制。
這裏使用如下示例:
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: k8s.gcr.io/pause:2.0
此節親和性規則指出,只能將pod放置在具有標簽的節點上,標簽的關鍵字為kubernetes.io/e2e-az-name,其值必須為e2e-az1或e2e-az2。 另外,在滿足該標準的節點中,應該優選具有其標簽為another-node-label-key其值是another-node-label-value的節點。
上面的示例使用了in 操作符,NodeAffinity語法支持的操作符包括in, NotIn, Exists, DoesNotExit, Gt, Lt。使用NotIn和DoesNotExist就可以實現排斥功能了。
- 如果同時定義了nodeSelector和nodeAffinity,那麽必須兩個條件都得到滿足,Pod才能最終運行在指定的Node上。
- 如果nodeAffinity指定了多個nodeSelectorTerms,那麽只需要其中一個能夠匹配成功即可。
- 如果nodeSelectorTerms中有多個matchExpressions,則一個節點必須滿足所有matchExpressions才能運行該Pod。
- 如果一個Pod所在的節點在Pod運行期間標簽發生了變化,不再符合該Pod的親和性需求,Pod不會進行重新調度,繼續在該節點上運行。
PodAffinity: Pod親和性與互斥調度策略
Pod親和性的調度策略將節點上的Pod也納入了考慮範圍,這種規則可以描述為:
如果在具有標簽X的Node節點上運行了一個或者多個符合條件Y的Pod,那麽Pod應該(拒絕/允許)運行在這個Node上。
這裏的X指的時集群中的節點,機架,區域等概念,通過內置的節點標簽topologyKey來實現。
條件Y表達的是pod對應的一個或者全部命名空間中的一個Label Selector
Pod的親和與互斥的條件設置也是requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution
如下示例,podAffinity 和 podAntiAffinity都被定義在affinity區域中:
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: failure-domain.beta.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: kubernetes.io/hostname
containers:
- name: with-pod-affinity
image: k8s.gcr.io/pause:2.0
這個示例表示了pod在調度時,需要滿足以下幾個要求:
- 被調度的node節點與Pod標簽的key為‘security’,值為‘S1’的節點屬於同一個zone
-
最好不要調度到運行label key 為 ‘security’,值為‘S1’ pod的節點上。
pod affinity和 anti-affinity的邏輯操作運算有:
In
,NotIn
,Exists
,DoseNotExit
.
原則上,topologyKey可以使用任何合法的標簽Key賦值,但是出於性能和安全方面的考慮,對topologyKey有如下限制:- 在Pod親和性和requiredDuringSchedulingIgnoredDuringExecution的互斥性定義中,不允許使用空的topologyKey。
- 如果Admisson controller 包含了LimitPodHardAntiAffinityTopology,那麽針對requiredDuringSchedulingIgnoredDuringExecution的Pod互斥性定義就被限制為kubernetes.io/hostname,要使用自定義的topologyKey,就要改寫或禁用該控制器。
- 在preferredDuringSchedulingIgnoredDuringExecution 類型的pod互斥性定義中,空的topologyKey會被解釋為kubernetes.io/hostname, failure-domain.beta.kubernetes.io/zone 和 failure-domain.beta.kubernetes.io/region的組合。
- 如果不是上述情況,就可以采用任意合法的topologyKey 了。
附加說明:
- 除了設置LabelSelector和topologyKey,用戶還可以指定namespace列表來進行限制,同樣,使用Label Selector對namespace進行選擇。namespace的定義和Label Selector及topologyKey同級。 省略namespace的設置,表示使用定義了affinity/anti-affinity的Pod所在的namespace。 如果namespace設置為空值(“”),則表示所有namespace。
- 在所有關聯requiredDuringSchedulingIgnoredDuringExecution的matchExpressions 全部滿足之後,系統才能將Pod調度到某個Node上。
Taints和Tolerations 汙點和容忍
參考鏈接
Taint前面的節點親和性作用相反,使用Taints規則,將拒絕Pod在Node上運行。
Taint需要和Tolerations配合使用,讓Pod避開那些不合適的Node。在Node上設置一個或多個Taint之後,除非Pod明確聲明能夠容忍這些“汙點”,否則無法在這些Node上運行。Toleration是Pod的屬性,讓Pod能夠運行在標註了Taint的Node節點上。
1、使用kubectl taint
命令為Node設置Taint信息:
kubectl taint nodes 10.0.0.3 key=value:NoSchedule
為node節點的10.0.0.3加上一個Taint,Taint的鍵為Key,值為value, Taint的效果是NoSchedule。這裏表示的含義是任何Pod都不能調度到這個節點,除非設置了toleration.
刪除節點上的taint:
kubectl taint nodes 10.0.0.3 key:NoSchedule-
2、在Pod中定義容忍(Tolerate)具有該Taint的Node,使得Pod可以被調度到這個節點:
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
或者
tolerations:
- key: "key"
operator: "Exists"
effect: "NoSchedule"
Pod的Toleration聲明中的Key和effect需要與Taint的設置保持一致,並且滿足以下條件之一:
- operator的值是Exists(無需指定value)
- operator的值是Equal並且value相等
如果不指定operator,則默認值是Equal,還有如下兩個特例:
- 空的key配合Exists操作符能夠匹配所有的鍵和值
- 空的effect匹配所有的effect.
effect取值可以是NoSchedule,還可以取值為PreferNoSchedule,這個值的意思是優先,也可以算作NoSchedule的軟限制版本。
系統允許在同一個Node上設置多個Taint,也可以在Pod上設置多個Toleration,匹配上的就會按照規則執行,而剩下的會對Pod產生效果:
- 如果剩余的Taint中存在effect=NoSchedule,則調度器不會把該Pod調度到這一節點上。
- 如果剩余的Taint中沒有NoSchedule效果,但是有PreferNoSchedule效果,則調度器會嘗試不把這個Pod指派給這個節點。
- 如果剩余的Taint的效果有NoExecute,並且這個Pod已經在該節點上運行,則會被驅逐;如果沒有在該節點上運行,也不會再被調度到該節點上。
- 如果pod已經在節點上正常運行,此時添加一個新的Taint,使得effect=NoSchedule,Pod還會繼續在此節點上運行。
- 如果給Node加上effect=NoSchedule的Taint,那麽該節點Node上正在運行的所有無對應Toleration的Pod都會被立刻驅逐,而具有相應Toleration的Pod則永遠不會被逐出,可以加入一個可選的
tolerationSeconds
,當taint添加後,還可以繼續運行多長時間,單位為秒:
tolerations:
- key: "node.alpha.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 6000
如果在寬限時間(這裏示例是6000s)內Taint被移除,則不會觸發驅逐事件。
使用汙點和容忍策略一般可以運用於以下場景:
- 獨占節點:如要拿出一部分節點給特定的應用使用,結合Admisson Controller實現。
- 具有特殊硬件設備的節點
- 定義Pod驅逐行為,以應對節點故障(Alpha)
DaemonSet 在每個Node上調度一個Pod
參考鏈接
DaemonSet用於管理集群中每個節點僅運行一份Pod的副本實例。這種用法一般適合如下應用:
- 在每個Node上運行一個GlusterFS存儲或者Ceph存儲的Daemon進程。
- 在每個Node上運行一個日誌采集程序,例如Fluentd或者Logstash。
- 在每個Node上運行一個性能監控程序,采集Node的性能數據,如Prometheus Node Exporter, collectd, Datadog agent, New Relic agent, 或 Ganglia gmond。
如下示例,會在每個節點創建一個DaemonSet:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: k8s.gcr.io/fluentd-elasticsearch:1.20
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
由於上面的示例中,使用的kube-system的namespace示例,在查詢daemonset的時候需要指定namespace
DaemonSet的Pod調度策略與RC類似,除了使用系統內置的算法在每臺Node進行調度,也可以使用NodeSelector或NodeAffinity來指定滿足條件的Node範圍進行調度。
Job 批處理調度
批處理任務通常並行或串行啟動多個計算進程去處理一批工作項,處理完成之後,整個批處理任務結束。批處理任務分為如下幾種類型:
1、Non-parallel Jobs
- 通常一個Job只能啟動一個Pod,除非Pod異常,才會重啟該Pod.
- 一旦Pod正常結束,Job將結束。
2、Parallel Jobs with a fixed completion count
- 並行Job會啟動多個Pod,此時需要設定Job的.spec.completions參數為一個正數
- 當正常結束的Pod數量達至此參數的設定的值後,Job結束。
3、並行Job需要一個獨立的Queue,Work item都在一個Queue中存放,不能設置Job的.spec.completions參數,此時Job有以下特性:
- 每個Pod能獨立判斷和決定是否還有任務項需要處理。
- 如果某個Pod正常結束,則Job不會再啟動新的Pod.
- 如果一個Pod成功結束,此時應該不存在其他Pod還在幹活的情況,他們應該都處於即將結束,退出的狀態。
- 如果所有Pod都結束了,其至少有一個Pod成功結束,則整個Job算成功結束。
Cronjob 定時任務
Kubernetes的定時任務和Linux Cron定時任務語法類似,這裏有如下示例:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
可以發現每隔一分鐘執行了一次:
# kubectl get cronjob hello -o wide
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE CONTAINERS IMAGES SELECTOR
hello */1 * * * * False 0 33s 7m hello busybox <none>
# kubectl get jobs --watch
NAME DESIRED SUCCESSFUL AGE
hello-1529412060 1 1 3m
hello-1529412120 1 1 2m
hello-1529412180 1 1 1m
hello-1529412240 1 0 6s
查看日誌信息:
# kubectl get pods --selector=job-name=hello-1529412600
NAME READY STATUS RESTARTS AGE
hello-1529412600-slcps 0/1 Completed 0 20s
# kubectl get pod hello-1529412600-slcps
NAME READY STATUS RESTARTS AGE
hello-1529412600-slcps 0/1 Completed 0 51s
# kubectl logs hello-1529412600-slcps
Tue Jun 19 12:50:17 UTC 2018
Hello from the Kubernetes cluster
刪除cronjob:
# kubectl delete cronjob hello
自定義調度器
除了上面所述的相關調度策略外,kubernetes還支持自定義調度器,可以使用任何語言開發我們需要的調度規則,將自定義的調度器使用kubectl proxy來運行,指定的文件格式如下:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
schedulerName: my-scheduler
containers:
- name: nginx
image: nginx
Pod 調度