Pod 指派运行的节点

nodeSelector,亲和性和反亲和性,污点容忍度

将 Pod 约束在特定的节点上可以使用 nodeSelect 对节点进行标签选择。
但是调度器通常会合理的调度 Pod 到合适的节点,所以除了使用 nodeSelect 进行强制约束外,还可以使用亲和性和反亲和性以及污点和容忍度进行指派 Pod。

nodeSelect

nodeSelect 依赖于节点标签,所以首先需要对节点添加上标签。

查看集群节点上所有的标签:

[vagrant@master01 ~]$ kubectl get nodes --show-labels
NAME                   STATUS   ROLES           AGE   VERSION   LABELS
master01.example.com   Ready    control-plane   41d   v1.29.1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master01.example.com,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=,role=ceph
worker01.example.com   Ready    edge            40d   v1.29.1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker01.example.com,kubernetes.io/os=linux,node-role.kubernetes.io/edge=,role=ceph
worker02.example.com   Ready    <none>          40d   v1.29.1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker02.example.com,kubernetes.io/os=linux,role=ceph

为特定的节点添加标签:

[vagrant@master01 ~]$ kubectl label nodes worker01.example.com env=qa
node/worker01.example.com labeled
[vagrant@master01 ~]$ kubectl get nodes -l env=qa
NAME                   STATUS   ROLES   AGE   VERSION
worker01.example.com   Ready    edge    40d   v1.29.1

移除 label:

[vagrant@master01 ~]$ kubectl label node worker01.example.com env-
node/worker01.example.com unlabeled
[vagrant@master01 ~]$ kubectl get nodes -l env=qa
No resources found

将 Pod 调度到指定的标签的节点上:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    env: qa

验证 Pod 已经调度到了 worker01 :

[vagrant@master01 ~]$ kubectl get pods/nginx -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP              NODE                   NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          59s   10.244.95.132   worker01.example.com   <none>           <none>

[vagrant@master01 ~]$ kubectl get nodes -l env=qa
NAME                   STATUS   ROLES   AGE   VERSION
worker01.example.com   Ready    edge    40d   v1.29.1

亲和性和反亲和性

亲和性和反亲和性分为两种:节点亲和性 Pod 之间的亲和性和反亲和性。

节点亲和性

nodeSelector 只能选择拥有所指定标签的节点,可以标明规则是偏好或者软需求,在调度器无法找到匹配的节点时,仍然能够调度该 Pod(nodeSelector 就不行)。

节点亲和性有两种:

  • requiredDuringSchedulingIgnoredDuringExecution:调度器只有满足规则时才能执行调度。
  • preferredDuringSchedulingIgnoredDuringExecution:调度器首先寻找满足规则的节点,如果找不到,调度器仍然会执行调度。

IgnoredDuringExecution 代表 Pod 调度到节点后,节点的标签发生了变化,Pod 依旧运行在该节点上。

首先为节点添加标签:

[vagrant@master01 ~]$ kubectl label node worker02.example.com node-role.kubernetes.io/worker=
node/worker02.example.com labeled
[vagrant@master01 ~]$ kubectl get nodes
NAME                   STATUS   ROLES           AGE   VERSION
master01.example.com   Ready    control-plane   41d   v1.29.1
worker01.example.com   Ready    edge            40d   v1.29.1
worker02.example.com   Ready    worker          40d   v1.29.1

部署 Pod,并使用亲和性约束:

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: node-role.kubernetes.io/worker
            operator: Exists
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: env
            operator: In
            values:
            - prod
  containers:
  - name: with-node-affinity
    image: registry.k8s.io/pause:2.0

验证:

[vagrant@master01 ~]$ kubectl get pods -o wide | grep with-node-affinity
with-node-affinity   1/1     Running   0          32s   10.244.45.238   worker02.example.com   <none>           <none>

虽然 worker02 上没有 env=prod 标签,但还是调度在该节点上了:

[vagrant@master01 ~]$ kubectl get nodes -l node-role.kubernetes.io/worker
NAME                   STATUS   ROLES    AGE   VERSION
worker02.example.com   Ready    worker   40d   v1.29.1
[vagrant@master01 ~]$ kubectl get nodes -l env=prod
No resources found

operator 除了使用 Exists 来验证是否存在的逻辑之外,还可以使用 InNotInDoesNotExistGtLt 之一作为逻辑操作符。
如果使用非 Exist ,需要提供 values 字段来提供逻辑判断的值,例如:

preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value

节点亲和性可以通过 weight 来定义权重,这个适用于多个规则进行筛选时,满足规则且权重高的节点将被调度。

Pod 间亲和性和反亲和性

调度器在调度 Pod 时,除了有节点的亲和性,还有 Pod 间亲和性和反亲和性。

节点亲和性是基于节点的标签,Pod 间的亲和性和反亲和性是基于当前运行在节点上的 Pod 的标签。

Pod 的亲和性和反亲和性也有两种类型:

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution

在 Pod 亲和性中可以使用 requiredDuringSchedulingIgnoredDuringExecution 来约束两个服务的 Pod 应该放到同一位置,可能是因为它们通信非常频繁。
在 Pod 反亲和性中可以使用 preferredDuringSchedulingIgnoredDuringExecution 来阻止同一个服务的 Pod 放置在同一个位置,避免出现单点故障。

Pod 间亲和性和反亲和性的规则格式为:如果 X 上已经运行了一个或多个满足规则 Y 的 Pod,则这个 Pod 应该运行在 X 上(反亲和性的话就是不应该)。
X 可以是节点、机架、云提供商可用区等类似的拓扑愈,Y 是 Kubernetes 尝试满足的规则。

Pod 亲和性使用 .affinity.podAffinity 字段,Pod 反亲和性使用 .affinity.podAntiAffinity 字段。

参考示例:

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: topology.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: topology.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: registry.k8s.io/pause:2.0

上述例子中,调度器首先会找到有 zone 标签的节点,因为没有指定 zone 的值,所以只判断是否存在该标签,如果该节点上运行了一个带有 security=S1 的标签的 Pod,则会调度到该节点,如果该节点上运行了一个带有 security=s2 的标签,则不会调度到该节点。

测试一下,首先需要在 node 上打上标签:

[vagrant@master01 ~]$ kubectl label node worker01.example.com zone=cn-1
node/worker01.example.com labeled
[vagrant@master01 ~]$ kubectl label node worker02.example.com zone=cn-2
node/worker02.example.com labeled
[vagrant@master01 ~]$ kubectl get nodes -l zone
NAME                   STATUS   ROLES    AGE   VERSION
worker01.example.com   Ready    edge     41d   v1.29.1
worker02.example.com   Ready    worker   41d   v1.29.1

接下来运行一个带有 security=S1 标签的 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: nginxsecuritys1
  labels:
    security: S1
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent

检查调度到的节点:

[vagrant@master01 ~]$ kubectl get pod -o wide | grep nginxsecuritys1
nginxsecuritys1      1/1     Running   0          22s   10.244.45.215   worker02.example.com   <none>           <none>

使用 Pod 亲和性将第二个 Pod 调度到跟 nginxsecuritys1 相同的节点上:

apiVersion: v1
kind: Pod
metadata:
  name: nginxsecuritys2
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchExpressions:
              - key: security
                operator: In
                values:
                  - S1
          topologyKey: zone
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent

验证:

[vagrant@master01 ~]$ kubectl get pods -o wide | grep security
nginxsecuritys1      1/1     Running   0          37m     10.244.45.215   worker02.example.com   <none>           <none>
nginxsecuritys2      1/1     Running   0          10m     10.244.45.219   worker02.example.com   <none>           <none>

定义 Pod 反亲和性规则:

apiVersion: v1
kind: Pod
metadata:
  name: nginxsecuritys3
spec:
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 100
          podAffinityTerm:
            labelSelector:
              matchExpressions:
                - key: security
                  operator: In
                  values:
                    - S1
            topologyKey: zone
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent

验证:

[vagrant@master01 ~]$ kubectl get pods -o wide | grep security
nginxsecuritys1      1/1     Running   0          37m     10.244.45.215   worker02.example.com   <none>           <none>
nginxsecuritys2      1/1     Running   0          10m     10.244.45.219   worker02.example.com   <none>           <none>
nginxsecuritys3      1/1     Running   0          3m41s   10.244.95.133   worker01.example.com   <none>           <none>

nodeName

使用 nodeName 优先级将高于 nodeSelector 和亲和性规则。
示例:

apiVersion: v1
kind: Pod
metadata:
  name: nginxnodenameonworker1
spec:
  nodeName: worker01.example.com
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent

验证:

[vagrant@master01 ~]$ kubectl get pod/nginxnodenameonworker1 -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP              NODE                   NOMINATED NODE   READINESS GATES
nginxnodenameonworker1   1/1     Running   0          12s   10.244.95.156   worker01.example.com   <none>           <none>

污点和容忍度

节点亲和性使 Pod 被吸引到一类特定的节点。污点(Taint) 相反,污点应用于节点上使节点排斥一类特定的 Pod 。

容忍度(Toleration)是应用于 Pod 上的。容忍度允许调度器调度带有对应污点的 Pod。容忍度允许调度但不保证调度。

污点和容忍度的相互配合可以避免 Pod 被分配到不合适的节点上。
每个节点上可以应用多个污点,不能容忍这些污点的 Pod 是不会调度到该节点上。

使用 kubectl taint 给节点添加一个污点:

kubectl taint nodes worker01 key1=value1:NoSchedule

删除污点:

kubectl taint nodes worker01 key1=value1:NoSchedule-

上述的 NoSchedule 对应的是效果,表示只拥有和这个污点想匹配的容忍度的 Pod 才能后被调度到该节点。除此之外 effect 有以下类型:

  • NoExecute:会影响已在节点上运行的 Pod,如果 Pod 不能容忍这类污点,会马上驱逐。
  • NoSchedule:需要具有匹配的容忍度 Pod 才会被调度,当前正在节点上运行的 Pod 不会被驱逐。
  • PreferNoSchedule:软性的 NoSchedule,尝试避免将不能容忍的 Pod 调度到节点上,但不保证完全避免。 在 Pod 上使用容忍度的例子:
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "example-key"
    operator: "Exists"
    effect: "NoSchedule"

Pod 容忍度也有 operator 参数:

  • Equal:如果键、值和效果参数匹配,则容忍度和污点匹配,这是默认的行为
  • Exists:如果键和效果参数匹配,则容忍度和污点匹配,必须将值参数留空

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理

滚动至顶部