Kubernetes 亲和性调度

栏目: 数据库 · 发布时间: 5年前

内容简介:前一篇文章Kubernetes 支持限制 Pod 在指定的 Node 上运行,或者指定更倾向于在某些特定 Node 上运行。有几种方式可以实现这个功能:

一、概述

前一篇文章 Kubernetes 调度器浅析 ,大致讲述了调度器的工作原理及相关调度策略。这一章会继续深入调度器,介绍下“亲和性调度”。

Kubernetes 支持限制 Pod 在指定的 Node 上运行,或者指定更倾向于在某些特定 Node 上运行。

有几种方式可以实现这个功能:

NodeName
NodeSelector
NodeAffinity
PodAffinity

二、NodeName

nodeName 是 PodSpec 的一个字段,用于直接指定调度节点,并运行该 pod。调度器在工作时,实际选择的是 nodeName 为空的 pod 并进行调度然后再回填该 nodeName,所以直接指定 nodeName 实际是直接跳过了调度器。换句话说,指定 nodeName 的方式是优于其他节点选择方法。

方法很简单,直接来个官方示例:

apiVersion: v1

kind: Pod

metadata:

  name: nginx

spec:

  containers:

  - name: nginx

    image: nginx

  nodeName: kube-01

当然如果选择的节点不存在,或者资源不足,那该 pod 必然就会运行失败。

三、NodeSelector

nodeSelector 也是 PodSpec 中的一个字段,指定键—值对的映射。

如果想要将 pod 运行到对应的 node 上,需要先给这些 node 打上 label,然后在 podSpec.NodeSelector 指定对应 node labels 即可。

步骤如下:

- 设置标签到 node 上:

kubectl label nodes kubernetes-node type=gpu

- pod 配置添加 nodeSelector 字段:

apiVersion: v1

kind: Pod

metadata:

  name: nginx

spec:

  containers:

  - name: nginx

    image: nginx

  nodeSelector:

    type: gpu

内置 Node 标签

1. Kubernetes 内置了一些节点标签:

  • kubernetes.io/hostname
  • beta.kubernetes.io/instance-type
  • beta.kubernetes.io/os
  • beta.kubernetes.io/arch
  • failure-domain.beta.kubernetes.io/zone
  • failure-domain.beta.kubernetes.io/region

有些标签是对云提供商使用。

  1. 还有些表示 node role 的 labels(可以指定 master、lb 等):

    • kubernetes.io/role
    • node-role.kubernetes.io

四、NodeAffinity

nodeSelector 通过 k-v 的方式非常简单的支持了 pod 调度限制到具有特定标签的节点上。而 nodeAffinity 根据亲和力 & 反亲和力极大地扩展了能够表达的约束信息。

nodeAffinity 特性的设计初衷就是为了替代 nodeSelector。

nodeAffinity 当前支持的匹配符号包括:In、NotIn、Exists、DoesNotExists、Gt、Lt 。

nodeAffinity 当前支持两种调度模式:

  • requiredDuringSchedulingIgnoredDuringExecution : 一定要满足的条件,如果没有找到满足条件的节点,则 Pod 创建失败。所有也称为 hard 模式
  • preferredDuringSchedulingIgnoredDuringExecution : 优先选择满足条件的节点,如果没有找到满足条件的节点,则在其他节点中择优创建 Pod。所有也称为 soft 模式

两种模式的名字特长,这是 k8s 的命名风格。其中 IgnoredDuringExecution 的意义就跟 nodeSelector 的实现一样,即使 node label 发生变更,也不会影响之前已经部署且又不满足 affinity rules 的 pods,这些 pods 还会继续在该 node 上运行。换句话说,亲和性选择节点仅在调度 Pod 时起作用。

k8s 社区正在计划提供 requiredDuringSchedulingRequiredDuringExecution 模式,便于驱逐 node 上不满足 affinity rules 的 pods。

来个官方示例,看下怎么玩:

apiVersion: v1

kind: Pod

metadata:

  name: with-node-affinity

spec:

  affinity:

    nodeAffinity:

      # 必须选择 node label key 为 kubernetes.io/e2e-az-name,

      # value 为 e2e-az1 或 e2e-az2.

      requiredDuringSchedulingIgnoredDuringExecution:

        nodeSelectorTerms:

        - matchExpressions:

          - key: kubernetes.io/e2e-az-name

            operator: In

            values:

            - e2e-az1

            - e2e-az2

      # 过滤掉上面的必选项后,再优先选择 node label key 为 another-node-label-key

      # value 为 another-node-label-value.

      preferredDuringSchedulingIgnoredDuringExecution:

      # 如果满足节点亲和,积分加权重(优选算法,会对 nodes 打分)

      # weight: 0 - 100

      - weight: 1

        preference:

          matchExpressions:

          - key: another-node-label-key

            operator: In

            values:

            - another-node-label-value

  containers:

  - name: with-node-affinity

    image: k8s.gcr.io/pause:2.0

简单看下 NodeAffinity 的结构体,下面介绍注意事项时会涉及:

type NodeAffinity struct {

RequiredDuringSchedulingIgnoredDuringExecution *NodeSelector

PreferredDuringSchedulingIgnoredDuringExecution []PreferredSchedulingTerm

}



type NodeSelector struct {

NodeSelectorTerms []NodeSelectorTerm

}



type NodeSelectorTerm struct {

MatchExpressions []NodeSelectorRequirement

MatchFields []NodeSelectorRequirement

}

配置相关的注意点:

  • 如果 nodeSelectornodeAffinity 两者都指定,那 node 需要两个条件都满足,pod 才能调度。
  • 如果指定了多个 NodeSelectorTerms ,那 node 只要满足其中一个条件,pod 就可以进行调度。
  • 如果指定了多个 MatchExpressions ,那必须要满足所有条件,才能将 pod 调度到该 node。

五、PodAffinity

nodeSelector & nodeAffinity 都是基于 node label 进行调度。而有时候我们希望调度的时候能考虑 pod 之间的关系,而不只是 pod 和 node 的关系。

举个例子,会有需求希望服务 A 和 B 部署在同一个机房、机架或机器上,因为这些服务可能会对网路延迟比较敏感,需要低延时;再比如,希望服务 C 和 D 又希望尽量分开部署,即使一台主机甚至一个机房出了问题,也不会导致两个服务一起挂而影响服务可用性,提升故障容灾的能力。

podAffinity 会基于节点上已经运行的 pod label 来约束新 pod 的调度。

其规则就是“如果 X 已经运行了一个或者多个符合规则 Y 的 Pod,那么这个 Pod 应该(如果是反亲和性,则是不应该)调度到 X 上”。

这里的 Y 是关联 namespace 的 labelSelector,当然 namespace 也可以是 all。和 node 不同,pod 是隶属于 namespace 下的资源,所以基于 pod labelSelector 必须指定具体的 namespace;而 X 则可以理解为一个拓扑域,类似于 node、rack、zone、cloud region 等等,就是前面提到的 内置 Node 标签 ,当然也可以自定义。

看下 pod affinity 涉及的结构体,便于进行功能介绍:

type Affinity struct {

// NodeAffinity 前面介绍了

NodeAffinity *NodeAffinity

// pod 亲和性

PodAffinity *PodAffinity

// pod 反亲和性

PodAntiAffinity *PodAntiAffinity

}



type PodAffinity struct {

// hard 模式, 必选项

RequiredDuringSchedulingIgnoredDuringExecution []PodAffinityTerm

// soft 模式, 进行 node 优先

PreferredDuringSchedulingIgnoredDuringExecution []WeightedPodAffinityTerm

}



type PodAffinityTerm struct {

LabelSelector *metav1.LabelSelector

Namespaces []string

TopologyKey string

}



type WeightedPodAffinityTerm struct {

Weight int32

PodAffinityTerm PodAffinityTerm

}

podAffinity 和 nodeAffinity 有相似的地方,使用了 labelSelector 进行匹配,支持的匹配符号包括:In、NotIn、Exists、DoesNotExists;

也支持两种调度模式 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution , 功能和 nodeAffinity 一样,这里就不在累述。

podAffinity 和 nodeAffinity 也有较大的差异,前面讲了 pod 是 namespace 资源,所以必然会需要配置 namespaces,支持配置多个 namespace。如果省略的话,默认为待调度 pod 所属的 namespace;如果定义了但是值为空,则表示使用 “all” namespaces。

还有一个较大的差别 TopologyKey , 便于理解进行单独介绍。

TopologyKey

TopologyKey 用于定义 in the same place ,前面也介绍了是 拓扑域 的概念。

看下面的图,这两个 pod 到底该如何算在一个拓扑域?

Kubernetes 亲和性调度

如果我们使用 k8s.io/hostnamein the same place 则意味着在同一个 node,那下图的 pods 就不在一个 place:

Kubernetes 亲和性调度

如果我们使用 failure-domain.k8s.io/zone 来表示一个 place,那下图的 pods 就表示在一个 zone:

Kubernetes 亲和性调度

当然我们也可以自定义 node labels 作为 TopologyKey。比如我们可以给一组 node 打上 rack 标签,那下图的 pods 表示在同一个 place:

Kubernetes 亲和性调度

原则上,topologyKey 可以是任何合法的 label key。但是出于性能和安全考虑,topologyKey 存在一些限制:

  • 对于亲和性和反亲和性的 requiredDuringSchedulingIgnoredDuringExecution 模式,topologyKey 不能为空
  • pod 反亲和性 requiredDuringSchedulingIgnoredDuringExecution 模式下, LimitPodHardAntiAffinityTopology 权限控制器会限制 topologyKey 只能设置为 kubernetes.io/hostname 。当然如果你想要使用自定义 topology,那可以简单禁用即可。
  • pod 反亲和性 preferredDuringSchedulingIgnoredDuringExecution 模式下,topologyKey 为空则表示所有的拓扑域。截止 v1.12 版本,所有的拓扑域还只能是 kubernetes.io/hostname , failure-domain.beta.kubernetes.io/zonefailure-domain.beta.kubernetes.io/region 的组合。
  • 除此之外,topologyKey 可以是任何合法的 label key。

示例

来个官方示例,有三节点集群,需要分别部署 3 份 web 和 redis 服务。希望 web 与 redis 服务共存,但需要保证各个服务的副本分散部署。

先创建 redis 集群:

apiVersion: apps/v1

kind: Deployment

metadata:

name: redis-cache

spec:

selector:

matchLabels:

  app: store

replicas: 3

template:

metadata:

  labels:

    app: store

spec:

  affinity:

    // pod 反亲和性, 打散 redis 各个副本

    podAntiAffinity:

      requiredDuringSchedulingIgnoredDuringExecution:

      - labelSelector:

          matchExpressions:

          - key: app

            operator: In

            values:

            - store

        topologyKey: "kubernetes.io/hostname"

  containers:

  - name: redis-server

    image: redis:3.2-alpine

再部署 web 服务,需要打散并且与 redis 服务共存,配置如下:

apiVersion: apps/v1

kind: Deployment

metadata:

name: web-server

spec:

selector:

matchLabels:

  app: web-store

replicas: 3

template:

metadata:

  labels:

    app: web-store

spec:

  affinity:

    podAntiAffinity:

      requiredDuringSchedulingIgnoredDuringExecution:

      - labelSelector:

          matchExpressions:

          - key: app

            operator: In

            values:

            - web-store

        topologyKey: "kubernetes.io/hostname"

    podAffinity:

      requiredDuringSchedulingIgnoredDuringExecution:

      - labelSelector:

          matchExpressions:

          - key: app

            operator: In

            values:

            - store

        topologyKey: "kubernetes.io/hostname"

  containers:

  - name: web-app

    image: nginx:1.12-alpine

注意1: pod affinity 需要进行大量处理,所以会明显减慢大型集群的调度时间,不建议在大于几百个节点的集群中使用该功能。

注意2: pod antiAffinity 要求对节点进行一致标志,即集群中的所有节点都必须具有适当的标签用于配置给 topologyKey,如果节点缺少指定的 topologyKey 指定的标签,则可能会导致意外行为。

六、参考资料

官方

- Assigning Pods to Nodes - Kubernetes

- node affinity

- pod affinity

- 性能优化: Improve performance of affinity/anti-affinity predicate by 20x in large clusters by bsalamat

blog

- Scheduling in Kubernetes, Part 2: Pod Affinity – Koki – Medium


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

深入浅出Rust

深入浅出Rust

范长春 / 机械工业出版社 / 2018-8-21 / 89.00元

本书详细描述了Rust语言的基本语法,穿插讲解一部分高级使用技巧,并以更容易理解的方式解释其背后的设计思想。全书总共分五个部分。 第一部分介绍Rust基本语法,因为对任何程序设计语言来说,语法都是基础,学习这部分是理解其他部分的前提。 第二部分介绍属于Rust独一无二的内存管理方式。它设计了一组全新的机制,既保证了安全性,又保持了强大的内存布局控制力,而且没有额外性能损失。这部分是本书......一起来看看 《深入浅出Rust》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具