目录
1 节点的调度策略
1.1 节点选择器nodeSelector
这是一种较常用也是较简单的方式,将pod调度到指定label的node上,可以满足一般情况下的调度需求。
第一步,为节点打labal:
// 查看节点的label
$ kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
worker0 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker0
worker1 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker1
worker2 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker2
// 为节点打一个label
kubectl label node worker0 disktype=ssd
// 查看
$ kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
worker0 Ready <none> 1d v1.13.0 ...,disktype=ssd,kubernetes.io/hostname=worker0
worker1 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker1
worker2 Ready <none> 1d v1.13.0 ...,kubernetes.io/hostname=worker2
第二步,创建pod并定义nodeSelector:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
nodeSelector:
disktype: ssd
这样pod就会被调度到有对应标签的节点上了。
如果找不到任何一个node有nodeSelector定义的标签,pod是会一直Pending的~
1.2 节点亲和性affinity
是比nodeSelector更灵活但也更复杂的方式, 它可以进行一些简单的逻辑组合,而不只是简单的相等匹配,以应对更加复杂的节点调度需求。
分为软策略、硬策略两种:
- requiredDuringSchedulingIgnoredDuringExecution:硬策略,如果没有满足条件的节点,就不断重试直至满足条件
- preferredDuringSchedulingIgnoredDuringExecution:软策略,如果没有满足条件的节点,Pod会忽略这条规则,继续完成调度过程
spec:
containers:
- name: nginx
image: nginx
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- 192.168.136.131
- 192.168.136.132
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
- sas
上述亲和性配置可以描述为:
-
Pod 不能运行在131和132两个节点上
-
如果有节点满足 disktype=ssd或sas,优先调度到这类节点上
-
operator:操作符,有In/NotIn/Gt/Lt/Exist/DoesNotExist
-
nodeSelectorTerms:下面的条件是or关系,满足一个即可
-
matchExpressions:下面的条件是and关系,必须同时满足
-
weight:权重,取值[1,100],会基于此计算评分,也就是当两个节点都满足时,调度器会将weight值添加到节点评分上
1.3 污点与容忍
污点(Taints): node设置了taints后,因为有了污点,不会将pod调度到这个node上了
容忍(Tolerations):pod可以容忍node上的污点,就可以调度到node上
使用场景:
-
私有云服务中,某业务使用 GPU 进行大规模并行计算。为保证性能,希望确保该业务对服务器的专属性,避免将普通业务调度到部署 GPU 的服务器。
-
用户希望把 Master 节点保留给 Kubernetes 系统组件使用,或者把一组具有特殊资源预留给某些 Pod,则污点就很有用了,Pod 不会再被调度到 taint 标记过的节点。
1.3.1 设置污点
kubectl taint node [node_name] key=value:[effect]
其中 [effect] 取值:
- NoSchedule:一定不能被调度
- PreferNoSchedule:尽量不要调度
- NoExecute:不仅不会调度,还会驱逐Node上已有的Pod
// 设置污点
# kubectl taint node master smoke=true:NoSchedule
node/master tainted
// 查看污点
# kubectl describe node master
Taints: node.kubernetes.io/disk-pressure:NoSchedule
smoke=true:NoSchedule
// 移除污点
# kubectl taint nodes master smoke-
node/master untainted
1.3.2 设置容忍
spec:
containers:
- name: nginx
image: nginx
tolerations:
- key: smoke
operator: Equal
value: true
effect: NoSchedule
- key: drunk
operator: Exists
上述容忍可以描述为:
-
pod可以容忍以下node(满足其中之一即可):
-
taints有smoke=true:NoSchedule
-
存在key为drunk的taints
-
-
operator:操作符,不填默认是Equal,当是Exists时value可以省略
-
tolerations:下面的数组是or关系,满足其中之一即可
1.3.3 污点和亲和性冲突时会怎样
-
设置了master节点的taints为`smoke=true:NoSchedule`
-
同时设置了pod的nodeSelector为 `smoke:true`
# kubectl describe node master
Taints: smoke=true:NoSchedule
// yaml
spec:
containers:
- image: containous/nginx
name:nginx
nodeSelector:
smoke: "true"
查看pod状态:
# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-855f99dc9d-b54w2 0/1 Pending 0 4m50s
# kubectl describe pod nginx-855f99dc9d-b54w2
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 26s default-scheduler 0/1 nodes are available: 1 node(s) had taint {smoke: true}, that the pod didn't tolerate.
所以这种情况下,k8s是会阻止pod调度的。也就是说,即使 nodeSelector 匹配了,污点的 NoSchedule 效果会优先阻止调度,导致 pod 处于 Pending 状态。
1.4 禁止节点调度,cordon与drain
1.4.1 cordon
// 停止调度
# kubectl cordon master
// 查看
# kubectl describe node master
Taints: node.kubernetes.io/unschedulable:NoSchedule
smoke=true: NoSchedule
Unschedulable: true
// 恢复调度
# kubectl uncordon master
// 查看
# kubectl describe node master
Taints: smoke=true:NoSchedule
Unschedulable: false
cordon本质上也是使用了污点机制,cordon实际上做了以下事情:
-
为 node 打上 `node.kubernetes.io/unschedulable:NoSchedule` 的taints
-
将 Unschedulable 置为true
1.4.2 drain
// 停止调度并驱逐
$ kubectl drain master --force --ignore-daemonsets --delete-local-data
// 恢复调度
kubectl uncordon master
drain 方式是安全驱逐 pod,会等到 pod 容器应用程序优雅停止后再删除该 pod。
驱逐流程:
先在 node 节点删除 pod,然后再在其他 node 节点创建该 pod。所以为了确保 drain 驱逐过程中不中断服务(即做到无感知地平滑驱逐),必须保证要驱逐的 pod 副本数大于1,并且采用"反亲和策略”将这些 pod 调度到不同的 node上,也就是说,在"多个pod副本+反亲和策略"的场景下,驱逐过程对容器服务是没有影响的。
1.5 Pod的驱逐
驱逐Eviction:当节点 NotReady或资源不足时,把 pod 驱逐至其它节点。
本质上也是利用污点与容忍实现的。
k8s会从两方面去做检查:
(1) Kube-controller-manager: 周期性检查所有节点状态,当节点处于 NotReady 状态超过一段时间后,驱逐该节点上所有 pod。
若节点若失联或异常,k8s会自动为node打上污点,同时为pod添加如下容忍设置:
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
其中tolerationSeconds是一个宽限期,即当node异常导致pod要被驱逐时,给node一个5min的宽限时间,等待异常被恢复,避免因网络波动导致pod被立即驱逐。
(2) Kubelet: 周期性检查本节点资源,当资源不足时,按照优先级驱逐部分 pod
2 Pod的调度策略
2.1 Pod的亲和性与反亲和性
spec:
containers:
- name: nginx
image: nginx
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: topology.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: topology.kubernetes.io/zone
上述配置可以描述为:
- 对亲和性:只有节点属于特定的区域(zone),且该区域中的其他 Pod 已打上 security=S1 标签时,才可以 Pod 调度到此节点上。 比如,一个区域由带有 topology.kubernetes.io/zone=V 标签的节点组成,那么只要 Zone V 内已经至少有一个 Pod 打了 security=S1 标签, 就可以将此 Pod 调度到 Zone V 内的任何节点。
- 对反亲和性: 如果节点属于特定的区域(zone),且该区域中的其他 Pod 已打上 security=S2 标签,那么调度器会尽量避免将 Pod 调度到此节点上。比如,一个区域由带有 topology.kubernetes.io/zone=R 标签的节点组成,那么只要 Zone R 内已经至少有一个 Pod 打了 security=S2 标签, 调度器就会避免将 Pod 分配给 Zone R 内的任何节点。
pod的亲和性与反亲和性较为复杂,且性能不高,所以一般很少使用。
来自官方:Pod 间亲和性和反亲和性都需要相当的计算量,因此会在大规模集群中显著降低调度速度,因此 不建议在包含数百个节点的集群中使用这类设置。