本文将详细介绍 kubernetes 中控制 pod 调度到指定节点的几种方法。包括通过 nodeName 调度到指定单个节点、通过 nodeSelector 调度到拥有指定标签的节点以及通过 nodeAffinity 实现更复杂的调度规则。
一、nodeName
nodeName 是 pod 规范(spec)中的一个字段,用于将 Pod 调度到特定的节点上。如果其值不为空,scheduler 将会忽略该 pod 不再参与其调度过程,Pod 将直接由指定节点的 kubelet 在其节点上启动。
使用 nodeName 来选择节点的一些局限性:
- 如果指定的节点不存在,则 pod 将一直处于 Pending 状态,并且在某些情况下可能会被自动删除。
- 由于使用 nodeName 时默认假设其指定的节点满足资源要求,但实际如果所指代的节点无法提供用来运行 Pod 所需的资源,Pod 会启动失败(报错cpu或内存资源不足)。
- 创建的pod将被固定到指定单个节点上,后续难以管理和维护。
所以一般出于测试验证某些功能等特殊情况下,需要确保 pod 被调度到指定节点上,才会使用此配置。我们可以直接获取集群节点列表,然后指定节点名称调度。
[root@imzcy ~]# kubectl get node
NAME STATUS ROLES AGE VERSION
10.168.31.13 Ready <none> 466d v1.22.5-tke.6
10.168.31.8 Ready <none> 463d v1.22.5-tke.6
[root@imzcy ~]#
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-scheduler-test
namespace: test
spec:
replicas: 1
selector:
matchLabels:
zapp: nginx
template:
metadata:
labels:
zapp: nginx
spec:
containers:
- image: nginx:1.21.6
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
protocol: TCP
nodeName: 10.168.31.8
使用上面的清单文件创建好资源后,我们使用 kubectl describe 命令查看 pod 的详细信息,可以看到没有 Scheduled 的信息。
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Pulled 22s kubelet Container image "nginx:1.21.6" already present on machine
Normal Created 22s kubelet Created container nginx
Normal Started 22s kubelet Started container nginx
二、nodeSelector
NodeSelector 是一个选择器,基于节点拥有的标签来选择Pod应该被调度到哪些节点上。必须满足匹配条件才能使 pod 正常被调度到节点,否则会一直处于 Pending 状态。
如下所示的清单文件中,必须有节点同时拥有值为 wan 的 mylabel_node_area 标签以及值为 gateway 的 mylabel_node_role 的标签,才能被正常调度。
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-strategy-v1
namespace: test
spec:
replicas: 1
selector:
matchLabels:
zapp: nginx
template:
metadata:
labels:
zapp: nginx
spec:
containers:
- image: nginx:1.21.6
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
protocol: TCP
nodeSelector:
mylabel_node_area: wan
mylabel_node_role: gateway
否则基于上面清单文件创建资源后查看 pod 状态就会一直处于 Pending 状态,查看 pod 事件信息可以看到如下内容
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 20s default-scheduler 0/2 nodes are available: 2 node(s) didn't match Pod's node affinity/selector.
为指定节点打上对应标签,pod 即可正常调度。
[root@imzcy ~]# kubectl label nodes 10.168.31.8 mylabel_node_area=wan
node/10.168.31.8 labeled
[root@VM-31-13-centos ~]# kubectl label nodes 10.168.31.8 mylabel_node_role=gateway
node/10.168.31.8 labeled
[root@imzcy ~]#
再次查看 pod 事件
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 11m default-scheduler 0/2 nodes are available: 2 node(s) didn't match Pod's node affinity/selector.
Warning FailedScheduling 10m default-scheduler 0/2 nodes are available: 2 node(s) didn't match Pod's node affinity/selector.
Normal Scheduled 27s default-scheduler Successfully assigned test/deploy-scheduler-test-654659d4f9-fkwvj to 10.168.31.8
Normal Pulled 27s kubelet Container image "nginx:1.21.6" already present on machine
Normal Created 27s kubelet Created container nginx
Normal Started 27s kubelet Started container nginx
我这里一般基于 nodeSelector 实现“专用节点”场景,例如专用于部署 ingress-nginx-controller 的节点。实现方法就是提前给相关节点打好污点和标签,然后为 ingress-nginx-controller 配置好对应容忍规则和 nodeSelector 。
三、nodeAffinity
节点亲和性概念上类似于 nodeSelector,根据节点上的标签或字段来控制 Pod 被调度到哪些节点上。节点亲和性有两种:
- requiredDuringSchedulingIgnoredDuringExecution:强制满足,如果没有节点满足条件则调度失败。此功能类似于 nodeSelector, 但其语法表达能力更强。
- preferredDuringSchedulingIgnoredDuringExecution:尽量满足,如果没有节点满足条件则随机调度到任意节点。
需要注意的是,如果节点标签在 Kubernetes 调度 Pod 后发生了变更,Pod 依然会继续运行。
requiredDuringSchedulingIgnoredDuringExecution
matchExpressions(匹配表达式) 允许你使用Kubernetes标签选择器的表达式来指定节点亲和性。具体来说,你可以使用一组由键值对和操作符组成的表达式,来选择节点标签中与这些表达式匹配的节点。比如你可以使用In(label值等于指定字符)、NotIn(label值不等于指定字符)、Exists(存在指定label)、DoesNotExist(不存在指定label) 以及 Gt 和 Lt (label值大于或小于指定值)操作符来定义匹配表达式。
如下配置指定了Pod应该调度到具有zone标签且值为zone1或zone2的节点上:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: zone
operator: In
values:
- zone1
- zone2
matchFields(匹配字段)是使用节点字段作为节点亲和性的一种方式。你可以使用与Pod字段相关的条件来定义节点亲和性。这些条件包括节点的名称、Pod资源需求和其他与Pod相关的字段。
如下配置指定了Pod应该调度到节点名称为 10.168.31.8 这个节点上。
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchFields:
- key: metadata.name
operator: In
values:
- 10.168.31.8
另需注意
- 如果同时指定了 nodeSelector 和 nodeAffinity,两者必须都要满足才能将 Pod 调度到候选节点上。
- 如果在与 nodeAffinity 类型关联的 nodeSelectorTerms 中指定多个条件, 只要其中一个 nodeSelectorTerms 满足(各个条件按逻辑或操作组合)的话,Pod 就可以被调度到节点上(如下图示例1中所示配置,上下两个条件中只要 mylabel_node_area 标签值为 wan 或 mylabel_node_role 标签值为 gateway 满足一个即可被调度)。
- 如果在与 nodeSelectorTerms 中的条件相关联的单个 matchExpressions 字段中指定多个表达式, 则只有当所有表达式都满足(各表达式按逻辑与操作组合)时,Pod 才能被调度到节点上(如下图示例2中所示,必须有节点同时拥有值为 wan 的 mylabel_node_area 标签以及值为 gateway 的 mylabel_node_role 的标签,才能被正常调度)。
preferredDuringSchedulingIgnoredDuringExecution
这种类型的节点亲和性规则可以指定一组优先级排序的条件,以决定Pod调度到哪些节点上。都具有一个 weight 字段,其取值范围是 1 到 100(值越大优先级越高)。
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: environment
operator: In
values:
- production
- weight: 50
preference:
matchExpressions:
- key: zone
operator: In
values:
- zone1
在上面的示例中,第一个节点亲和性规则的权重为100,匹配 environment 为 production 的节点。而第二个节点亲和性规则的权重为50,匹配 zone 为 zone1 的节点。如果同一个节点同时满足两条规则,因为第一个规则的权重更高,所以更可能被选择。
本文采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。
如果您的问题未解决,欢迎微信扫描右侧二维码与我联系。