介绍
在默认情况下,一个 pod 在哪个 node 节点上运行,是由 Scheduler 组件采用相应的算法算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不满足需求,因为在很多情况下,我们想控制某些 pod 到达某些节点上,那么应该怎么做呢?这就涉及到 kubernetes 对 pod 的调度规则,kubernetes 提供了四大类调度方式
- 自动调度:运行在哪个节点上完全由 Scheduler 经过一系列的算法计算得出
- 定向调度:有两种,根据节点名称(NodeName)或者节点选择器(NodeSelector)
- 亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
- 污点(容忍)调度:Taints、Toleration
默认就是自动调度,所以下面来看其他的几种调度方式
定向调度
定向调度,指的是利用在 pod 上声明 nodeName 或者 nodeSelector,以此将 pod 调度到期望的 node 节点上。注意,这里的调度是强制的,这就意味着即使要调度的目标 node 不存在,也会向上面进行调度,只不过 pod 运行失败而已
NodeName
NodeName 用于强制将 pod 调度到指定的 name 的 node 节点上,这种方式,其实是直接跳过 Scheduler 的调度逻辑,直接将 pod 调度到指定名称的节点
接下来,实验一下:创建一个 pod-nodename.yaml 文件
apiVersion: v1 kind: Pod metadata: name: pod-nodename namespace: zouzou spec: containers: - name: nginx image: nginx:1.14 nodeName: dce-10-6-215-190 # 指定调度到 dce-10-6-215-190 节点上
如果你不知道 nodeName 的话,使用下面命令查看
kubectl get node
创建 pod,查看 pod
# 创建 pod kubectl apply -f pod-nodename.yaml
查看 pod 的信息,可以看到 pod 已经分配给了 dce-10-6-215-190 的节点上
# 可以看到 pod 分配到了 dce-10-6-215-190 的节点上,有人说,可能是随机分配的,那你也可以写一个不存在的 node 地址试下 [root@dce-10-6-215-215 tmp]# kubectl get pod pod-nodename -n zouzou -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-nodename 1/1 Running 0 89s 172.29.190.141 dce-10-6-215-190 <none> <none>
NodeSelector
NodeSelector 用于将 pod 调度到添加了指定标签的 node 节点上,它是通过 kubernetes 的 label-selector 机制实现的,也就是说,在 pod 创建之前,会由 scheduler 使用 MatchNodeSelector 调度策略进行 label 匹配,找出目标 node,然后将 pod 调度到目标节点,该匹配规则是强制约束。
接下来,实验一下:首先查看下节点的 labels
[root@dce-10-6-215-215 tmp]# kubectl get node --show-labels NAME STATUS ROLES AGE VERSION LABELS dce-10-6-215-190 Ready <none> 7d3h v1.18.20 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=dce-10-6-215-190,kubernetes.io/os=linux dce-10-6-215-200 Ready <none> 7d1h v1.18.20 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=dce-10-6-215-200,kubernetes.io/os=linux dce-10-6-215-215 Ready master,registry 8d v1.18.20 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=dce-10-6-215-215,kubernetes.io/os=linux,node-role.kubernetes.io/master=,node-role.kubernetes.io/registry=
上面有很多的标签,为了测试方便,在此基础上给两个 node 节点添加两个简单的标签
kubectl label node dce-10-6-215-190 nodeenv=pro kubectl label node dce-10-6-215-200 nodeenv=test
在来查看下对应的标签
这样我们就给两个节点打上了标签
创建一个 pod-nodeselector.yaml 文件,并使用它创建 Pod
apiVersion: v1 kind: Pod metadata: name: pod-nodeselector namespace: zouzou spec: containers: - name: nginx image: nginx:1.14 nodeSelector: nodeenv: pro # 指定调度到具有 nodeenv=pro 标签的节点上
创建 pod 查看详情,可以看到,pod 分配给了 nodeenv=pro 的标签节点上
# 创建 pod [root@dce-10-6-215-215 tmp]# kubectl apply -f pod-nodeselector.yaml pod/pod-nodeselector created # 查看 pod 在 dce-10-6-215-190 上了,你也可以写个不存在的标签测试是不是随机分配的 [root@dce-10-6-215-215 tmp]# kubectl get pod pod-nodeselector -n zouzou -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-nodeselector 1/1 Running 0 48s 172.29.190.142 dce-10-6-215-190 <none> <none>
亲和性调度
上面,介绍了两种定向调度的方式,使用起来非常方便,但是也有一定的问题,那就是如果没有满足条件的 node,那么 pod 将不会被运行,即使在集群中还有可用 node 列表也不行,这就限制了它的使用场景
基于上面的问题,kubernetes 还提供了一种亲和度(Affinity)调度。它在 NodeSelector 的基础上进行了扩展,可以通过配置的形式,实现优先选择满足条件的 Node 进行调度,如果没有,也可以调度到不满足条件的节点上,使调度更加灵活
Affinity 主要分为三类
- nodeAffinity(node 亲和性):以 node 为目标,解决 pod 可以调度到哪些 node 上的问题
- podAffinity(pod 亲和性):以 pod 为目标,解决 pod 可以和哪些已存在的 pod 部署在同一个拓扑域中的问题
- podAntiAffinity(pod 反亲和性):以 pod 为目标,解决 pod 不能和哪些已存在 pod 部署在同一个拓扑域中的问题
关于亲和性和反亲和性使用场景的说明:
亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用尽可能的靠近,这样可以减少因网络通信而带来的性能损耗,比如 web 应用和 mysql,要频繁的从数据库里查询和新增数据,所以应该让他们尽可能的在一起
反亲和性:当应用采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个 node 上,这样可以提高服务的高可用性,当一个服务器挂掉之后,其他的 pod 还可以提供服务
NodeAffinity
首先来看一下 NodeAffinity
的可配置项:
pod.spec.affinity.nodeAffinity requiredDuringSchedulingIgnoredDuringExecution # Node节点必须满足指定的所有规则才可以,相当于硬限制 nodeSelectorTerms # 节点选择列表 matchFields # 按节点字段列出的节点选择器要求列表 matchExpressions # 按节点标签列出的节点选择器要求列表(推荐) key # 键 values # 值 operat or # 关系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt preferredDuringSchedulingIgnoredDuringExecution # 优先调度到满足指定的规则的 Node,相当于软限制 (倾向) preference # 一个节点选择器项,与相应的权重相关联 matchFields # 按节点字段列出的节点选择器要求列表 matchExpressions # 按节点标签列出的节点选择器要求列表(推荐) key # 键 values # 值 operator # 关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt weight # 倾向权重,在范围1-100。
关系符的说明
- matchExpressions: - key: nodeenv # 匹配存在标签的 key 为 nodeenv 的节点 operator: Exists - key: nodeenv # 匹配标签的 key 为 nodeenv,且 value 是 "xxx" 或 "yyy" 的节点 operator: In values: ["xxx","yyy"] - key: nodeenv # 匹配标签的 key 为 nodeenv,且 value 大于 "xxx" 的节点 operator: Gt values: "xxx"
接下来首先演示一下 requiredDuringSchedulingIgnoredDuringExecution
创建 pod-nodeaffinity-required.yaml,内容如下
apiVersion: v1 kind: Pod metadata: name: pod-nodeaffinity-required namespace: zouzou spec: containers: - name: nginx image: nginx:1.14 affinity: # 亲和性设置 nodeAffinity: # 设置 node 亲和性 requiredDuringSchedulingIgnoredDuringExecution: # 硬限制,没有满足就会失败 nodeSelectorTerms: # node 选择器,可以有多个 - matchExpressions: # 匹配标签 - key: nodeenv # 标签的 key 要为 nodeenv operator: In # 操作符,在里面 values: ["xxx","yyy"] # 值在["xxx","yyy"]中的标签
创建 pod
# 创建 pod kubectl create -f pod-nodeaffinity-required.yaml
查看 pod 和 pod 的 event
# 查看 pod ,发现 pod 没有调度到节点上,因为 nodeenv 的值没有 xxx 或者 yyy,而且是硬限制 [root@dce-10-6-215-215 tmp]# kubectl get pod pod-nodeaffinity-required -n zouzou -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-nodeaffinity-required 0/1 Pending 0 81s <none> <none> <none> <none> # 从 event 可以看到,调度失败,有三个 node 节点,但是没有满足标签选择器的 node [root@dce-10-6-215-215 tmp]# kubectl describe pod pod-nodeaffinity-required -n zouzou Name: pod-nodeaffinity-required Namespace: zouzou Priority: 0 Node: <none> Labels: <none> ...... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 104s default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector. Warning FailedScheduling 30s default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
从上面我们可以看出,硬限制当没有匹配的标签时,会调度失败。因为我们两个 node 节点,只有 nodeenv=pro 和 nodeenv=test 的节点,没有 nodeenv=xxx 或者 nodeenv=yyy 的节点
接下来,我们删除 pod,把 yyy 改为 pro,因为 nodeenv=pro 是存在的
# 删除 pod kubectl delete -f pod-nodeaffinity-required.yaml
修改后在创建 pod
# 创建 pod kubectl create -f pod-nodeaffinity-required.yaml
查看 pod
# 可以看到,pod 分配给了 dce-10-6-215-190 的节点上,并成功运行了 [root@dce-10-6-215-215 tmp]# kubectl get pod pod-nodeaffinity-required -n zouzou -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-nodeaffinity-required 1/1 Running 0 17s 172.29.190.143 dce-10-6-215-190 <none> <none> # 从 event 里,也可以看出来,是成功调度的 [root@dce-10-6-215-215 tmp]# kubectl describe pod pod-nodeaffinity-required -n zouzou Name: pod-nodeaffinity-required Namespace: zouzou...... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 24s default-scheduler Successfully assigned zouzou/pod-nodeaffinity-required to dce-10-6-215-190 Normal Pulled 21s kubelet Container image "nginx:1.14" already present on machine Normal Created 21s kubelet Created container nginx Normal Started 20s kubelet Started container nginx
接下来再演示一下 requiredDuringSchedulingIgnoredDuringExecution
创建 pod-nodeaffinity-preferred.yaml,内容如下
apiVersion: v1 kind: Pod metadata: name: pod-nodeaffinity-preferred namespace: zouzou spec: containers: - name: nginx image: nginx:1.14 affinity: # 亲和性设置 nodeAffinity: # 设置 node 亲和性 preferredDuringSchedulingIgnoredDuringExecution: # 软限制,没有匹配的标签也会分配给某个 node - weight: 1 # 权重 preference: # 一个节点选择器项,与相应的权重相关联 matchExpressions: # 匹配标签 - key: nodeenv # 标签的 key 要为 nodeenv operator: In # 操作符,在里面 values: ["xxx","yyy"] # 匹配 env 的值在["xxx","yyy"]中的标签(当前环境没有)
创建 pod
kubectl create -f pod-nodeaffinity-preferred.yaml
查看 pod 和 pod event
# 可以看到,成功分配给了 dce-10-6-215-200 的节点上 [root@dce-10-6-215-215 tmp]# kubectl get pod pod-nodeaffinity-preferred -n zouzou -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-nodeaffinity-preferred 1/1 Running 0 53s 172.29.34.238 dce-10-6-215-200 <none> <none> # 从 event 中也可以看出,pod 是调度成功的 [root@dce-10-6-215-215 tmp]# kubectl describe pod pod-nodeaffinity-preferred -n zouzou Name: pod-nodeaffinity-preferred Namespace: zouzou Priority: 0 Node: dce-10-6-215-200/10.6.215.200 Start Time: Sat, 16 Apr 2022 11:45:15 +0800 ...... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 66s default-scheduler Successfully assigned zouzou/pod-nodeaffinity-preferred to dce-10-6-215-200 Normal Pulled 63s kubelet Container image "nginx:1.14" already present on machine Normal Created 63s kubelet Created container nginx Normal Started 62s kubelet Started container ngin
从上面的结果可以看出,软限制,当没有匹配到对应的标签时,也不会调度失败,会根据算法分配给合适的节点
NodeAffinity规则设置的注意事项:
- 如果同时定义了 nodeSelector 和 nodeAffinity,那么必须两个条件都得到满足,Pod 才能运行在指定的 Node 上
- 如果 nodeAffinity 指定了多个 nodeSelectorTerms,那么只需要其中一个能够匹配成功即可
- 如果一个 nodeSelectorTerms 中有多个 matchExpressions ,则一个节点必须满足所有的才能匹配成功
- 如果一个 pod 所在的 Node 在 Pod 运行期间其标签发生了改变,不再符合该 Pod 的节点亲和性需求,则系统将忽略此变化