zoukankan      html  css  js  c++  java
  • Kubernetes使用节点亲缘性将POD调度到特定节点上

      节点污点可以用来让pod远离特定的节点,尽量在不修改已有pod信息的前提,通过在节点添加污点信息,来拒绝pod在某些节点上的部署。

      而现在介绍一种叫做节点亲缘性,通过明确的在pod中添加的信息,来决定一个pod可以或者不可以被调度到哪些节点上。

      对比节点亲缘性和节点选择器

      在早期版本的Kubernetes中,初始的节点亲缘性机制,就是pod描述中的nodeSelector字段。节点必须包含所有pod对应字段中的指定label,才能成为pod调度的目标节点。

      节点选择器实现简单,但是它不能满足你的所有需求。正因为如此,一种更强大的机制被引入。节点选择器最终会被弃用,所以现在了解新的节点亲缘性机制就变得重要起来。

      与节点选择器类似,每个pod可以定义自己的节点亲缘性规则。这些规则可以允许指定硬性限制或者偏好。如果指定一种偏好的话,你将告知Kubernetes对于某个特定的pod,它更倾向于调度到某些节点上,之后Kubernetes将尽量把这个pod调度到这些节点上面。如果没法实现的话,pod将被调度到其他某个节点上。

      检查默认的节点标签

      节点亲缘性根据节点的标签来进行选择,这点跟节点选择器是一致的。现在检查一个Google Kubernetes引擎集群(GKE)中节点的标签,来看一下它们默认的标签是什么,如以下代码所示。

    #代码16.7 GKE节点的默认标签
    $ kubectl describe node gke-kubia-default-pool-db274c5a-mjnf
    Name:     gke-kubia-default-pool-db274c5a-mjnf
    Role:
    Labels:   beta.kubernetes.io/arch=amd64
              beta.kubernetes.io/fluentd-ds-ready=true
              beta.kubernetes.io/instance-type=f1-micro
              beta.kubernetes.io/os=linux
              cloud.google.com/gke-nodepool=default-pool
              failure-domain.beta.kubernetes.io/region=europe-west1         #最后这三个标签对于节点亲缘性来说最为重要
              failure-domain.beta.kubernetes.io/zone=europe-west1-d         
              kubernetes.io/hostname=gke-kubia-default-pool-db274c5a-mjnf   

      这个节点有很多标签,但涉及节点亲缘性和pod亲缘性时,最后三个标签是最重要的。这三个标签的含义如下:

      • failure-domain.beta.kubernetes.io/region表示该节点所在的地理地域。
      • failure-domain.beta.kubernetes.io/zone表示该节点所在的可用性区域(availability zone)。
      • kubernetes.io/hostname很显然是该节点的主机名。

      这三个以及其他标签,将被用于pod亲缘性规则。在第三章中,你己经学会 如何给一个节点添加自定义标签,并且在pod的节点选择器中使用它。可以通过给 pod加上节点选择器的方式,将节点部署到含有这个自定义标签的节点上。现在, 你将学习到怎么用节点亲缘性规则实现同样的功能。

     

    1.指定强制性节点亲缘性规则

      有这么一个例子,使用了节点选择器使得需要GPU的pod只被调度到有GPU的节点上。包含了nodeSelector字段的pod描述如以下代码所示。

    #代码16.8 使用了节点选择器的pod : kubia-gpu-nodeselector.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: kubia-gpu
    spec:
      nodeSelector:
        gpu: "true"
      containers:
      - image: luksa/kubia        #这个pod会被调度到包含了gpu=true标签的节点上
        name: kubia

      nodeSelector字段表示,pod只能被部署在包含了gpu=true标签的节点上。 如果将节点选择器替换为节点亲缘性规则,pod定义将会如以下代码清单所示。

    #代码16.9 使用了节点亲缘性规则的pod : kubia-gpu-nodeaffinity.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: kubia-gpu
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: gpu
                operator: In
                values:
                - "true"
      containers:
      - image: luksa/kubia
        name: kubia

      首先会注意到的是,这种写法比简单的节点选择器要复杂得多,但这是因为它的表达能力更强,再详细看一下这个规则。

      较长的节点亲缘性属性名的意义

      正如你所看到的,这个pod的描述包含了affinity字段,该字段又包含了nodeAffinity字段,这个字段有一个极其长的名字,所以,先重点关注这个。

      把这个名字分成两部分,然后分别看下它们的含义:

      • requiredDuringScheduling...表明了该字段下定义的规则,为了让pod能调度到该节点上,明确指出了该节点必须包含的标签。
      • ...IgnoredDuringExecution表明了该字段下定义的规则,不会影响己经在节点上运行着的pod。

      目前,当你知道当前的亲缘性规则只会影响正在被调度的pod,并且不会导致己经在运行的pod被剔除时,情况可能会更简单一些。这就是为什么目前的规则都是以IgnoredDuringExecution结尾的。最终,Kubernetes也会支持RequiredDuringExecution,表示如果去除掉节点上的某个标签,那些需要节点包含该标签的pod将会被剔除,但是,Kubernetes目前还不支持次特性。所以,可以暂时不去关心这个长字段的第二部分。

      了解节点选择器条件

      记住上一节所解释的内容,将更容易理解nodeSelectorTerms和matchExpressions字段,这两个字段定义了节点的标签必须满足哪一种表达式,才能满足pod调度的条件。样例中的单个表达式比较容易理解,节点必须包含一个叫作gpu的标签,并且这个标签的值必须是true。

      因此,这个pod只会被调度到包含gpu=true的节点上。如图16.2所示。

      现在,更有趣的部分来了,节点亲缘性也可以在调度时指定节点的优先级,将在接下来的部分看到。

    2.调度pod时优先考虑某些节点

      最近介绍的节点亲缘性的最大好处就是,当调度某一个pod时,指定调度器可以优先考虑哪些节点,这个功能是通过preferredDuringSchedulingIgnoredDuringExecution字段来实现的。

      想象一下拥有一个跨越多个国家的多个数据中心,每一个数据中心代表了一个单独的可用性区域。在每个区域中,有一些特定的机器,只提供给你自己或者你的合作公司使用。现在,你想要部署一些pod,希望将pod优先部署在区域zone1,并且是为你公司部署预留的机器上。如果你的机器没有足够的空间用于这些pod,或者出于其他一些重要的原因不希望这些pod调度到上面,那么就会调度到其他区域的其他机器上面,这种情况你也是可以接受的。节点亲缘性就可以实现这样的功能。

      给节点加上标签

      首先,节点必须加上合适的标签。每个节点需要包含两个标签,一个用于表示所在的这个节点所归属的可用性区域,另一个用于表示这是一个独占的节点还是一个共享的节点。

      在接下来假设双节点的集群环境,将使用这个集群中的两个工作节点,当然也可以使用GKE或者其他多节点的集群。

      首先,给这些节点加上标签,如以下代码所示。

    #代码16.10 给节点加上标签
    $ kubectl label node node1.k8s availability-zone=zone1
    node "node1.k8s" labeled
    $ kubectl label node node1.k8s share-type=dedicated
    node "node1.k8s" labeled
    $ kubectl label node node2.k8s availability-zone=zone2
    node "node2.k8s" labeled
    $ kubectl label node node2.k8s share-type=shared
    node "node2.k8s" labeled
    $ kubectl get node -L availability-zone -L share-type
    NAME         STATUS    AGE       VERSION   AVAILABILITY-ZONE   SHARE-TYPE
    master.k8s   Ready     4d        v1.6.4    <none>              <none>
    node1.k8s    Ready     4d        v1.6.4    zone1               dedicated
    node2.k8s    Ready     4d        v1.6.4    zone2               shared

      指定优先级节点亲缘性规则

      当这些节点的标签设置好,现在可以创建一个Deployment,其中优先选择zone1中的dedicated节点。下面的代码清单显示了这个Deployment的描述。

    #代码16.11 含有优先级节点亲缘性规则的Deployment: preferred-deployment.yamI
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
      name: pref
    spec:
      replicas: 5
      template:
        metadata:
          labels:
            app: pref
        spec:
          affinity:
            nodeAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:  #指定优先级,这不是必需的
              - weight: 80                                #这块是节点有限调度到zone1,这是最重要的偏好
                preference:
                  matchExpressions:
                  - key: availability-zone
                    operator: In
                    values:
                    - zone1
              - weight: 20                                 #同时优先调度pod到独占节点,但是该优先级为zone1优先级的1/4
                preference:
                  matchExpressions:
                  - key: share-type
                    operator: In
                    values:
                    - dedicated
          containers:
          - args:
            - sleep
            - "99999"
            image: busybox
            name: main

      查看上面的代码清单,定义了一个节点亲缘性优先级,而不是强制要求(后面会讲解)。想要pod被调度到包含标签availability-zone=zone1以及share-type=dedicated的节点上。第一个优先级规则是相对重要的,因此将其weight设置为80,而第二个优先级规则就不那么重要(weight设置为20)。

      了解节点优先级是如何工作的

      如果集群包含多个节点,当调度上面的代码中的Deployment pod时,节点将会分成4个组,如图16.3所示。那些包含availability-zone以及share-type标签,并且匹配pod亲缘性的节点,将排在最前面。然后,由于pod的节点亲缘性规则配置的权重,接下来是zone1的shared节点,然后是其他区域的dedicated节点,优先级最低的是剩下的其他节点。

      在一个包含两个节点的集群中部署节点

      如果在一个包含两个节点的集群中创建该部署,看到的最多的应该是pod被部署在了node1上面。检查下面的代码清单看情况是否属实。

    #代码16.12 查看pod调度情况
    $ kubectl get po -o wide
    NAME                READY   STATUS    RESTARTS  AGE   IP          NODE
    pref-607515-1rnwv   1/1     Running   0         4m    10.47.0.1   node2.k8s
    pref-607515-27wp0   1/1     Running   0         4m    10.44.0.8   node1.k8s
    pref-607515-5xd0z   1/1     Running   0         4m    10.44.0.5   node1.k8s
    pref-607515-jx9wt   1/1     Running   0         4m    10.44.0.4   node1.k8s
    pref-607515-mlgqm   1/1     Running   0         4m    10.44.0.6   node1.k8s

      5个pod被创建,其中4个部署在了node1,1个部署在了node2。为什么会有1个pod会被调度到node2而不是node1?原因是除了节点亲缘性的优先级函数,调度器还是使用其他的优先级函数来决定节点被调度到哪。其中之一就是Selector SpreadPriority函数,这个函数确保了属于同一个ReplicaSet或者Service的pod,将分散部署在不同节点上,以避免单个节点失效导致这个服务也宕机。这就是有1个pod被调度到node2的最大可能。

      可以去试着扩容部署至20个实例或更多,将看到大多数的pod被调度到node1。如果没有设置任何节点亲缘性优先级,pod将会被均匀地分配在两个节点上面。

     

    3.更多的亲缘性匹配规则及语法

      上面也简单介绍了简单的原理以及过程,现在介绍更多的规则以及案例。

      节点亲和调度分成软策略(soft)和硬策略(hard),在软策略下,如果没有满足调度条件的节点,pod会忽略这条规则,继续完成调度。

    • requiredDuringSchedulingIgnoredDuringExecution

    表示pod必须部署到满足条件的节点上,如果没有满足条件的节点,就不停重试。其中IgnoreDuringExecution表示pod部署之后运行的时候,如果节点标签发生了变化,不再满足pod指定的条件,pod也会继续运行。

    • requiredDuringSchedulingRequiredDuringExecution

    表示pod必须部署到满足条件的节点上,如果没有满足条件的节点,就不停重试。其中RequiredDuringExecution表示pod部署之后运行的时候,如果节点标签发生了变化,不再满足pod指定的条件,则重新选择符合要求的节点。

    • preferredDuringSchedulingIgnoredDuringExecution

      表示优先部署到满足条件的节点上,如果没有满足条件的节点,就忽略这些条件,按照正常逻辑部署。

    • preferredDuringSchedulingRequiredDuringExecution

      表示优先部署到满足条件的节点上,如果没有满足条件的节点,就忽略这些条件,按照正常逻辑部署。其中RequiredDuringExecution表示如果后面节点标签发生了变化,满足了条件,则重新调度到满足条件的节点。

      再来一个官方示例

    apiVersion: v1
    kind: Pod
    metadata:
      name: with-node-affinity
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/e2e-az-name
                operator: In
                values:
                - e2e-az1
                - e2e-az2
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
              - key: another-node-label-key
                operator: In
                values:
                - another-node-label-value
      containers:
      - name: with-node-affinity
        image: gcr.io/google_containers/pause:2.0

      这个pod同时定义了requiredDuringSchedulingIgnoredDuringExecution和 preferredDuringSchedulingIgnoredDuringExecution 两种 nodeAffinity。第一个要求 pod 运行在特定AZ的节点上,第二个希望节点最好有对应的another-node-label-key:another-node-label-value 标签。

      这里的匹配逻辑是label在某个列表中,可选的操作符有:

      • In: label的值在某个列表中
      • NotIn:label的值不在某个列表中
      • Exists:某个label存在
      • DoesNotExist:某个label不存在
      • Gt:label的值大于某个值(字符串比较)
      • Lt:label的值小于某个值(字符串比较)

      如果nodeAffinity中nodeSelector有多个选项,节点满足任何一个条件即可;如果matchExpressions有多个选项,则节点必须同时满足这些选项才能运行pod 。

    作者:小家电维修

    相见有时,后会无期。

  • 相关阅读:
    398. Random Pick Index
    382. Linked List Random Node
    645. Set Mismatch
    174. Dungeon Game
    264. Ugly Number II
    115. Distinct Subsequences
    372. Super Pow
    LeetCode 242 有效的字母异位词
    LeetCode 78 子集
    LeetCode 404 左叶子之和
  • 原文地址:https://www.cnblogs.com/lizexiong/p/14823426.html
Copyright © 2011-2022 走看看