目录

    1. Kubernetes 中的调度器

    kube-scheduler 是 Kubernetes 中决定 Pending 状态的 Pod 运行在哪个 Node 的组件,被称之为调度器。

    Kubernetes 中内置了大量的调度策略,也提供了一些高级调度策略(nodeAffinity、podAffinity 等),以供用户使用,基本能够满足绝大部分的业务需求。

    前面的文档 Kubernetes 之 Labels、Selectors 中提到, Labels、Selectors 是 Kubernetes 中非常重要的功能。Labels 关联了 Pod 、Deployment 、Service ,也用于调度策略,下面我们就来看看怎么使用 Labels 定制调度策略。

    2. nodeSelector

    首先查看,Node 有哪些 Labels :

    kubectl get nodes --show-labels
    
    NAME    STATUS   ROLES           AGE   VERSION    LABELS
    node1   Ready    master,worker   17h   v1.15.12   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux,node-role.kubernetes.io/master=
    node2   Ready    worker          17h   v1.15.12   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux,node-role.kubernetes.io/worker=
    node3   Ready    worker          17h   v1.15.12   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node3,kubernetes.io/os=linux,node-role.kubernetes.io/worker=
    

    回顾一下 Labels 的基本操作:

    • 增加标签
    kubectl label node node1 node-role.kubernetes.io/worker=ci
    
    • 修改标签
    kubectl label --overwrite node1 node-role.kubernetes.io/worker=
    
    • 删除标签
    kubectl label node node1 node-role.kubernetes.io/worker-
    

    在使用 nodeSelector 时,在 Pod 的 Spec 字段中增加 nodeSelector ,说明 Node 需要同时满足的全部 Label 条件即可。下面这个例子,将 Pod 调度到具有 kubernetes.io/hostname=node1 Label 的 Node 上。

    spec:
      containers:
      - ...
      nodeSelector:
        kubernetes.io/hostname: node1
    

    在 1.2 版本之后,Kubernetes 引入了 nodeAffinity ,功能上类似 nodeSelector ,nodeSelector 在后续版本中将被废除。

    3. nodeAffinity

    nodeAffinity 主要用于控制 Pod 应该运行在哪个 Node 上。亲和性调度有两种方式:

    • 软策略,尽量满足
    • 硬策略,必须满足

    这些策略是通过 Label 匹配进行判断的,Kubernetes 提供了几种操作符:

    • In, Label 在某个列表中
    • NotIn, Label 不在某个列表中
    • Gt, Label 大于某个值
    • Lt, Label 小于某个值
    • Exists, Label 存在
    • DoesNotExist,Label 不存在

    通过这些操作符和 Label ,我们就可以定制自己的调度策略。下面是一个官方的示例:

    spec:
      containers:
      - ...
      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
    

    在 Pod 的 Spec 字段中,新增 nodeAffinity 字段进行描述。

    如果同时指定多个 nodeSelectorTerms ,那么 Node 只要满足其中一个条件即可调度。如果指定多个 matchExpressions ,那么 Node 必须满足所有条件才可以调度。

    4. podAffinity

    podAffinity 与 nodeAffinity 类似,只不过 nodeAffinity 描述的是 Pod 对 Node 的选择,而 podAffinity 描述的是 Pod 对 Pod 的选择。

    podAffinity 多了一个 topologyKey (拓扑域),这相当于给 Pod 的调度策略增加了一个选择 Node 的维度。首先 Node 的 Label 需要满足 topologyKey 的要求,再考察运行中的 Pod 带的 Label 是否满足亲和性的要求。

    下面这个例子要求 Pod 调度需要满足:

    • Node 的 Label 必须有 failure-domain.beta.kubernetes.io/zone
    • Node 上运行的 Pod Label 必须有 security=S1
    • 尽量不要调度到 Label 有 kubernetes.io/hostname ,并且 Pod Label 有 security=S2 的 Node 上
    spec:
      containers:
      - ...
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution: #硬策略,强制满足
          - labelSelector:
              matchExpressions:
              - key: security
                operator: In
                values:
                - S1
            topologyKey: failure-domain.beta.kubernetes.io/zone
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution: #软策略,尽量满足
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: security
                  operator: In
                  values:
                  - S2
              topologyKey: kubernetes.io/hostname
    

    5. taints、tolerations

    taints 针对的是 Node , tolerations 针对的是 Pod。如果一个 Node 被标记为 taint ,那么这个 Node 将不被调度,除非 Pod 被设置 tolerations 容忍这个 taint。taints、tolerations 通常用在一些特殊的 Node 调度上,比如 master 、具有 GPU 的 Node 、SSD 硬盘的 Node 、内存很大的 Node 等。

    taint 的格式为:<key>=<value>:<effect>

    其中 key、value(均可为空) 用于 tolerations 匹配,而 effect 有三个值:

    - PreferNoSchedule ,尽量不要调度
    - NoSchedule ,不能调度
    - NoExecute ,不能调度,同时驱逐已有 Pod
    
    • 给 Node 增加一个 taint
    kubectl taint nodes node1 key1=value1:NoSchedule
    
    • 查看 Node 的 taint
    kubectl describe nodes node1
    
    • 给 Node 去掉 taint
    kubectl taint nodes node1 key1:NoSchedule-
    
    • Pod 容忍 taint

    在 Pod 的 Spec 字段,新增 tolerations 描述容忍的 taint 。下面的例子,正好可以容忍上面打的 taint :

    spec:
      containers:
      - ...
      tolerations:
      - key: "key1"
        operator: "Equal"
        value: "value1"
        effect: "NoSchedule"
    

    6. 参考