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 。

    作者:小家电维修

    相见有时,后会无期。

  • 相关阅读:
    ASP.NET 2.0服务器控件开发之基本概念篇
    ASP.NET 2.0的URL映射的实现方法
    Asp.net中防止用户多次登录的方法
    SSRS:使用SQL2008教程学习Reporting Services之数据库AdventureWorks2008问题_学习笔记1
    ASP.NET服务器控件编程之热身运动
    ASP.NET2.0服务器控件开发之实现事件
    datalist 或者repeater分页
    .Net 上传图片加水印
    datalist 全选
    深入理解JavaScript中的函数
  • 原文地址:https://www.cnblogs.com/lizexiong/p/14823426.html
Copyright © 2011-2022 走看看