使用 taint 和 toleration 阻止节点调度到特定节点 #
taint 和 toleration #
taint,是在不修改已有 Pod 的前提下,通过在节点上添加污点信息,拒绝 Pod 的部署。 只有当一个 Pod 容忍某个节点的 taint 时,才能被调度到此节点上。
显示节点 taint 信息
kubectl describe node master.k8s
...
Name: master.k8s
Role:
Labels:
beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/hostname=master.k8s
Annotations:
node.alpha.kubernetes.io/ttl=0
Taints:
node-role.kubernetes.io/master:NoSchedule
...
主节点上包含一个污点,污点包含一个 key 和 value,以及一个 effect,格式为
显示 Pod tolerations
kubectl describe Pod kube-proxy-as92 -n kube-system
...
Tolerations:
node-role.kubernetes.io/master:=NoSchedule
node.alpha.kubernetes.io/notReady=:Exists:NoExecute
node.alpha.kubernetes.io/unreachable=:Exists:NoExecute
...
第一个 toleration 匹配了主节点的 taint,表示允许这个 Pod 调度到主节点上。
了解污点效果
另外 2 个在 kube-proxy Pod 上容忍定义了当前节点状态是没有 ready 或者是 unreachable 时, 该 Pod 允许运行在该节点上多长时间。污点可以包含以下三种效果:
- NoSchedule,表示如果 pod 没有容忍此污点,将无法调度
- PreferNoSchedule,是上面的宽松版本,如果没有其他节点可调度,依旧可以调度到本节点
- NoExecute,上 2 个在调度期间起作用,而此设定也会影响正在运行中的 Pod。 如果节点上的 Pod 没有容忍此污点,会被驱逐。
在节点上定义污点 #
kubectl taint node node1.k8s node-type=production:NoSchedule
此命令给节点添加污点,key 为 node-type,value 为 production,effect 为 NoSchedule。 如果现在部署一个常规 Pod 多个副本,没有一个 Pod 会调度到此节点上。
在 Pod 上添加容忍 #
apiVersion: apps/v1
kind: Deployment
metadata:
name: prod
spec:
replicas: 5
template:
spec:
...
tolertations:
- key: node-type
operator: Equals
value: production
effect: NoSchedule
部署此 deployment,Pod 即可调度到 node1.k8s 节点上。
了解污点和容忍的使用场景 #
调度时使用污点和容忍
污点可以组织新 Pod 的调度,或者定义非有限调度节点,甚至是将已有 Pod 驱逐。 例如,一个集群分成多个部分,只允许开发团队将 Pod 部署到特定节点上;或者某些特殊 Pod 依赖硬件配置。
配置节点失效后的 pod 重新调度最长等待时间
容忍程度也可以配置,当某个 Pod 所在节点 NotReady 或者 Unreachable 是, k8s 可以等待该 Pod 重新调度之前的最长等待时间。
...
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
...
上面 2 个容忍表示,该 Pod 容忍所在节点处于 notReady 和 unreachable 状态维持 300s。超时后,再重新调度。
使用节点亲和将 Pod 调度到指定节点 #
污点可以拒绝调度,亲和允许调度。早期的 k8s 版本,节点亲和,就是 Pod 中的 nodeSelector 字段。 与节点选择器类似,每个节点都可以配置亲和性规则,可以是硬性标注,也可以是偏好。
检查节点默认标签
kubectl describe node gke-kubia-default-adj12knzf-2dascjb
Name: gke-kubia-default-adj12knzf-2dascjb
Role:
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
failure-domain.beta.kubernetes.io/region=europe-west1
failure-domain.beta.kubernetes.io/zone=europe-west1-d
kubernetes.io/hostname=gke-kubia-default-adj12knzf-2dascjb
这是一个 gke 的节点,最后三个标签涉及到亲和性,分别表示所在地理地域,可用性区域,和主机名。
指定强制节点亲和性规则 #
下面展示了只能被部署到含有 gpu=true 标签的节点上的 Pod:
apiVersion: v1
kind: Pod
metadata:
name: kubia-gpu
spec:
affinity:
nodeAffinity:
requiredDuringSchdulingIgnoredDuringExecution:
lableSelectorTerms:
- matchExpressions:
- key: gpu
operator: In
values:
- "true"
较长的节点亲和性属性名的含义
- requireDuringScheduling… 表示该字段下定义的规则,为了让 Pod 调度到该节点上,必须满足的标签。
- …IgnoredDuringExecution 表明该字段下的规则,不会影响已经在节点上的 Pod。
了解节点选择器的条件
lableSelectorTerms 和 matchExpressions 定义了节点的标签必须满足哪一种表达方式。
调度 Pod 时优先考虑某些节点 #
节点亲和性和节点选择器相比,最大的好处就是, 当调度某一个 Pod,指定可以优先考某些节点,如果节点均无法满足,调度结果也是可以接受的。 这是由 preferdDuringSchdulingIgnoredDuringExecution 字段实现的。
添加标签
kubectl label node node1.k8s availability-zone=zone1
kubectl label node node1.k8s share-type=dedicated
kubectl label node node2.k8s availability-zone=zone2
kubectl label node node2.k8s share-type=shared
指定优先级节点亲和性
apiVersion: apps/v1
kind: Deployment
metadata:
name: pref
spec:
template:
...
spec:
affinity:
nodeAffinity:
prefereddDuringSchdulingIgnoredDuringExecution:
- weight: 80
preference:
- matchExpressions:
- key: availability-zone
operator: In
values:
- zone1
- weight: 20
preference:
- matchExpressions:
- key: share-type
operator: In
values:
- dedicated
上面描述一个节点亲和优先级,并不是强制要求。想要 Pod 调度到含有 availability-zone=zone1 和 share-type=dedicated 的节点上。第一个优先级相对重要,weight=80,第二个相对不重要,weight=20。
了解优先级是如何工作的
如果集群中包含很多节点,上面的 deployment,将会把节点分成四种。2 个标签都包含的,优秀级最高; 只包含 weight=80 的标签节点次之;然后只是包含 weight=20 的节点;剩下的节点排在最后。
在一个包含 2 个节点的集群中部署
如果在只有 node1 和 node2 的集群中部署一个 replica=5 的 Deployment,Pod 更多部署到 node1, 有极少数调度到 node2 上。这是因为 kube-schduler 除了节点亲和的优先级函数, 还有其他函数来决定 Pod 调度到哪里。其中之一就是 Selector SpreadPriority 函数, 此函数保证同一个 ReplicaSet 或者 Service 的 Pod,将分散到不同节点上。 避免单个节点失效而出现整个服务挂掉。
使用亲和/反亲和部署 Pod #
刚才描述了 Pod 和节点之间的亲和关系,也有 Pod 与 Pod 之间的亲和关系, 例如前端 Pod 和后端 Pod,尽可能部署较近,减少网络时延。
使用 Pod 亲和将多个 Pod 部署到同一个节点上 #
部署 1 个后端 Pod 和 5 个前端 Pod。先部署后端:
kubectl run backend -l app=backend --image busybox --sleep 9999
这里没什么区别,只是给 Pod 加了个标签,app=backend。
在 Pod 中指定亲和性
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 5
template:
...
spec:
affinity:
podAffinity:
requireddDuringSchdulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
app: backend
这里强制要求,frontend 的 Pod 被调度到和其他包含 app=backend 标签的 Pod 所在的相同节点上。 这是通过 topologyKey 指定的。
了解调度器如何使用 pod 亲和规则
上面的应用部署后,5 个前端 Pod 和 1 个后端 Pod 都在 node2 上。 假设后端 Pod 被误删,重新调度后,依旧在 node2 上。 虽然后端 Pod 本身没有定义任何亲和性规则,如果调度到其他节点,即会打破已有的亲和规则。
将 Pod 部署到同一个机柜、可用性区域或者地理地域 #
同一个可用性区域协调部署
如果想要 Pod 在同一个 available zone 部署应用, 可以将 topologyKey 设置成 failure-domain.beta.kubernetes.io/zone。
同一个地域协调部署 如果想要 Pod 在同一个 available zone 部署应用, 可以将 topologyKey 设置成 failure-domain.beta.kubernetes.io/region。
了解 topologyKey 如何工作 topologyKey 的工作方式很简单,上面提到的三个属性并没有什么特别。可以设置成任意你想要的值。
当调度器决定决定 Pod 调度到哪里时,首先检查 podAffinity 配置,选择满足条件的 Pod, 接着查询这些 Pod 运行在那些节点上。特别寻找能匹配 podAffinity 配置的 topologyKey 的节点。 接着,会优先选择所有标签匹配的 Pod 的值的节点。
Pod 优先级亲和性 #
与节点亲和一样,pod 之间也可以用 prefereddDuringSchdulingIgnoredDuringExecution。
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 5
template:
...
spec:
affinity:
podAffinity:
prefereddDuringSchdulingIgnoredDuringExecution:
- weight: 80
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
app: backend
和 nodeAffinity 一样,设置了权重。也需要设置 topologyKey 和 labelSelector。
Pod 反亲和分开调度 #
同理,有 nodeAntiAffinity,也有 podAntiAffinity。 这将导致调度器永远不会选择包含 podAntiAffinity 的 Pod 所在的节点。
使用反亲和分散 Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 5
template:
metadata:
labels:
app: frontend
spec:
affinity:
podAntiAffinity:
requiredDuringSchdulingIgnoredDuringExecution:
topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
app: frontend
上面的 Deployment 创建后,5 个实例应当分布在 5 个不同节点上。
理解 Pod 反亲和优先级
在这种情况下,可能使用软反亲和策略(prefereddDuringSchdulingIgnoredDuringExecution)。 毕竟 2 个前端 Pod 在同一个节点上也不是什么问题。如果运行在一个节点上出现问题, 那么使用 requiredDuringSchdulingIgnoredDuringExecution 就比较合适了。 与 Pod 亲和一样,topologyKey 决定了 Pod 不能被调度的范围。