k8s 中控制 pod 调度节点的几种方法

- Kubernetes

本文将详细介绍 kubernetes 中控制 pod 调度到指定节点的几种方法。包括通过 nodeName 调度到指定单个节点、通过 nodeSelector 调度到拥有指定标签的节点以及通过 nodeAffinity 实现更复杂的调度规则。
2024-05-15T15:14:25.png


一、nodeName

nodeName 是 pod 规范(spec)中的一个字段,用于将 Pod 调度到特定的节点上。如果其值不为空,scheduler 将会忽略该 pod 不再参与其调度过程,Pod 将直接由指定节点的 kubelet 在其节点上启动。

使用 nodeName 来选择节点的一些局限性:

所以一般出于测试验证某些功能等特殊情况下,需要确保 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 被调度到哪些节点上。节点亲和性有两种:

需要注意的是,如果节点标签在 Kubernetes 调度 Pod 后发生了变更,Pod 依然会继续运行。

requiredDuringSchedulingIgnoredDuringExecution
2024-05-15T15:11:40.png
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

另需注意

2024-05-15T15:13:14.png

preferredDuringSchedulingIgnoredDuringExecution
2024-05-15T15:13:33.png
这种类型的节点亲和性规则可以指定一组优先级排序的条件,以决定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 的节点。如果同一个节点同时满足两条规则,因为第一个规则的权重更高,所以更可能被选择。