Kubernetes in Action笔记 - (18) 高级调度:污点、容忍度和亲缘性
使用污点和容忍度阻止节点调度到特定节点
污点和容忍度
污点是在不修改已有pod信息的前提下,通过在节点上添加污点信息,来拒绝pod在某些节点上的部署。
只有当一个pod容忍某个节点的污点, 这个pod才能被调度到该节点。
可以通过kubectl describe node
查看节点的污点信息,在返回结果的Taints字段值中;可以通过kubectl describe po
查看pod的污点容忍度,在返回结果的Tolerations字段值中。
每一个污点都可以关联一个效果, 效果包含了以下三种:
- NoSchedule:表示如果pod没有容忍这些污点, pod则不能被调度到包含这些污点的节点上。
- PreferNoSchedule:它是NoSchedule的一个宽松的版本,表示尽量阻止pod被调度到这个节点上,但是如果没有其他节点可以调度, pod依然会被调度到这个节点上。
- NoExecute:不同于NoSchedule以及PreferNoSchedule,后两者只在调度期间起作用, 而NoExecute也会影响正在节点上运行着的pod。如果在一个节点上添加了NoExecute污点,那些在该节点上运行着的pod,如果没有容忍这个NoExecute污点,将会从这个节点去除。
添加污点与容忍度
在节点上添加自定义污点
假设有一个单独的 Kubernetes 集群,上面同时有生产环境和非生产环境的流量。其中最重要的一点是,非生产环境的pod不能运行在生产环境的节点上。可以通过在生产环境的节点上添加污点来满足这个要求:
# 这个命令添加了一个taint,其中key为node-type,value为production,效果为NoSchedule
kubectl taint node nodel.k8s node-type=production:NoSchedule
在pod上添加污点容忍度
apiVersion: extensions/vlbetal
kind: Deployment
metadata:
name: prod
spec:
replicas: 5
template:
spec:
tolerations:
# 此处的污点容忍度允许pod被调度到生产环境节点上
- key: node-type
operator: Equal
value: production
effect: NoSchedule
污点和污点容忍度的使用场景
节点可以拥有多个污点信息,而pod也可以有多个污点容忍度。
污点可以只有一个key和一个效果,而不必设置value。污点容忍度可以通过设置Equal操作符Equal操作符来指定匹配的value(默认情况下的操作符),或者也可以通过设置Exists操作符来匹配污点的key。
在调度时使用污点和容忍度
污点可以用来组织新pod的调度(使用 NoSchedule效果),或者定义非优先调度的节点(使用PreferNoSchedule效果), 甚至是将已有的pod从当前节点剔除。
可以用任何觉得合适的方式去设置污点和容忍度。例如,可以将一个集群分成多个部分,只允许开发团队将pod调度到他们特定的节点上。 当部分节点提供了某种特殊硬件, 并且只有部分pod需要使用到这些硬件的时候, 也可以通过设置污点和容忍度的方式来实现。
配置节点失效之后的pod重新调度最长等待时间
也可以配置一个容忍度, 用于当某个pod运行所在的节点变成unready或者unreachable状态时,k8s可以等待该pod被调度到其他节点的最长等待时间
$ kubectl get po prod-350605-lphSh -o yaml
...
tolerations:
- effect: NoExecute
key: node.alpha.kubernetes.io/notReady
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.alpha.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
这两个容忍度表示, 该pod将容忍所在节点处于notReady或者unreachable状态维持300秒。当k8s控制器检测到有节点处于notReady或者unreachable状态时, 将会等待300秒, 如果状态持续的话, 之后将把该pod重新调度到其他节点上。当没有定义这两个容忍度时, 他们会自动添加到pod上。如果觉得5分钟太长的话,可以在pod描述中显式地将这两个容忍度设置得更短一些。
注意:在作者写书的时候,这是一个alpha阶段的特性,在未来的k8s版本中可能会有所改变。基于污点信息的pod剔除也不是默认启的, 如果要启用这个特性, 需要在运行控制器管理器时使用--feature-gates=TaintBasedEvictions=true
选项。
使用节点亲缘性将pod调度到特定节点上
可以定义pod的节点亲缘性规则,指定硬性限制或者偏好。
如果指定一种偏好的话,k8s将尽量把这个pod 调度到某些特定的节点上面;如果没法实现的话, pod 将被调度到其他节点。
指定强制性节点亲缘性规则
使用了节点选择器使得需要GPU的pod只被调度到有GPU的节点上。
apiVersion: vl
kind : Pod
metadata:
name: kubia-gpu
spec:
nodeSelector:
gpu: "true"
...
将节点选择器替换为节点亲缘性规则
apiVersion: v1
kind: Pod
metadata:
name: kubia-gpu
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: gpu
operator: In
values: "true"
- requiredDuringScheduling… 表明了该字段下定义的规则,为了让pod能调度到该节点上,明确指出了该节点必须包含的标签。
- …IgnoredDuringExecution 表明了该字段下定义的规则, 不会影响已经在节点上运行着的pod
调度pod时优先考虑某些节点
这个功能是通过 preferredDuringSchedulingIgnoredDuringExecution 宇段来实现的
nodeAffinity:
preferredDuringSchedulingignoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: availability-zone
operator: In
values:
- zone1
- weight: 20
preference:
matchExpressions:
- key: share-type
operator: In
values:
- dedicated
使用pod亲缘性与非亲缘性对pod进行协同部署
上面主要涉及到pod和节点之间的亲缘性,这里介绍如何指定pod自身之间的亲缘性。
使用pod间亲缘性将多个pod部署在同一个节点上
举例来说,想象一下你一个前端pod和一个后端pod, 将这些节点部署得比较靠近,可以降低延时,提高应用的性能。可以使用节点亲缘性规则来确保这两个pod被调度到同一个节点、同一个机架、同一个数据中心。但是,之后还需要指定调度到具体哪个节点、哪个机架或者哪个数据中心。因此,这不是一个最佳的解决方案。更好的做法应该是,让k8s将你的pod部署在任何它觉得合适的地方,同时确保2个pod是靠近的。这种功能可以通过pod亲缘性来实现。
# 注意不是nodeAffinity
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernates.io/hostname
labelSelector:
matchLabels:
app: backend
注意:除了使用简单的matchLabels字段, 也可以使用表达能力更强的matchExpressions字段。
将pod部署在同一机柜、可用性区域或者地理地域
在同一个可用性区域中协同部署pod,需要将topologyKey属性设置为failure-domain.beta.kubernetes.io/zone
在同一个地域中协同部署pod,需要将topologyKey属性设置为failure-domain.beta.kubernetes.io/region
topologyKey是如何工作的
topologyKey的工作方式很简单,可以任意设置自定义的键,唯一的前置条件就是,在节点上加上对应的标签。
注意:在调度时,默认情况下,标签选择器只有匹配同一命名空间中的pod。但是,可以通过在label Selector同一级添加namespaces字段,实现从其他的命名空间选择pod的功能。
表达pod亲缘性优先级取代强制性要求
podAffinity与nodeAffinity,都可以指定硬性限制(仅允许调度到特定节点或者pod)或者偏好(如果无法满足,则可以调度到其他地方)。
利用pod的非亲缘性分开调度pod
有时候可能希望pod远离彼此,这种特性叫作pod非亲缘性。
它和pod亲缘性的表示方式一样, 只不过是将podAffinity字段换成podAntiAffinity, 这将导致调度器永远不会选择那些有包含podAntiAffinity匹配标签的pod所在的节点
下面的例子使用非亲缘性分散一个部署中的pod:
# 一个前端pod必须不能调度到有app=frontend标签的pod运行的节点
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernates.io/hostname
labelSelector:
matchLabels:
app: frontend
图书资料: