Kumu's wiki
  • SUMMARY
  • Docker
    • 理论概述
    • 安装入门
    • 配置说明
    • 基础命令
    • 镜像构建
    • 镜像存储
      • OverlayFS 存储驱动
      • Habor 安装和升级标注
    • Compose
      • Compose 概览
      • Compose 安装
      • Compose 入门
      • Compose 环境变量
      • Compose 服务扩展
      • Compose 网络
      • Compose 生产实践
      • Compose 启动顺序控制
  • Kubernetes
    • 架构概览
    • 基础术语
    • 集群构建
    • 工作负载
      • Deployments
      • StatefulSets
      • Volumes
      • Persistent Volumes
    • 集群调度
      • 亲和性和反亲和性
      • 污点和容忍机制
    • 集群组件
      • Kubelet
    • 网络方案
      • 网络策略
      • Calico BGP 网络(v2.6.x)
      • Kubelet CNI 源码解析
    • client-go
      • client-go 背后机制
    • Helm
      • Helm 架构
      • Helm 快速上手
      • Helm 使用
      • Helm 命令
    • Google 大规模集群管理器 Borg
由 GitBook 提供支持
在本页
  • nodeSelector
  • 亲和性和反亲和性(Affinity and anti-affinity)
  • Node affinity
  • Pod affinity and anti-affinity
  • 实践案例
  • 始终调度在同一个节点

这有帮助吗?

  1. Kubernetes
  2. 集群调度

亲和性和反亲和性

上一页集群调度下一页污点和容忍机制

最后更新于5年前

这有帮助吗?

通过 Kubernetes 你可以将一个 pod 限制或倾向于在某些特定节点运行。有几种方式可以达到这个目的,它们都通过 进行选择。通常情况下,这样的约束是不必要的,因为调度程序会自动进行合理的调度(如通过一系列的评分机制将 pods 合理分配到最优节点上,而不会将 pod 分配在没有足够资源的节点上等)。但是在某些情况下,可能需要更多的策略控制,例如,将 pod 调度到 SSD 的计算节点上,或者将两个通信比较频繁的不同服务 pod 调度到同一个可用域。

labels 在 K8s 中是一个很重要的概念,作为一个标识,Service、Deployments 和 Pods 之间的关联都是通过 label 来实现的。而每个节点也都拥有 label,通过设置 label 相关的策略可以使得 pods 关联到对应 label 的节点上。

nodeSelector

nodeSelector 是最简单的约束方式。nodeSelector 是 PodSpec 的一个字段。

通过 --show-labels 可以查看当前 nodes 的 labels

$ kubectl get nodes --show-labels
NAME       STATUS    ROLES     AGE       VERSION   LABELS
minikube   Ready     <none>    1m        v1.10.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/
hostname=minikube

如果没有额外添加 nodes labels,那么看到的如上所示的默认标签。我们可以通过 kubectl label node 命令给指定 node 添加 labels:

$ kubectl label node minikube disktype=ssd
node/minikube labeled
$ kubectl get nodes --show-labels
NAME       STATUS    ROLES     AGE       VERSION   LABELS
minikube   Ready     <none>    5m        v1.10.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=minikube

当然,你也可以通过 kubectl label node 删除指定的 labels(标签 key 接 - 号即可)

$ kubectl label node minikube disktype-
node/minikube labeled
$ kubectl get node --show-labels
NAME       STATUS    ROLES     AGE       VERSION   LABELS
minikube   Ready     <none>    23m       v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=minikube

创建测试 pod 并指定 nodeSelector 选项绑定节点:

$ cat nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd
$ kubectl create -f nginx.yaml
pod/nginx created

查看 pod 调度的节点,即我们指定有 disktype=ssd label 的 minikube 节点:

$ kubectl get pods -o wide
NAME      READY     STATUS    RESTARTS   AGE       IP           NODE
nginx     1/1       Running   0          1m        172.18.0.4   minikube

nodeSelector 可以很方便的解决以上比较简单的需求,但是它还不够灵活。比如我想以机架为单位,部署的服务可以很好的分散在不同机架的服务器上,此时 nodeSelector 就并不是那么管用了。因此,Kubernetes 引入了亲和性和反亲和性概念。

亲和性和反亲和性(Affinity and anti-affinity)

affinity/anti-affinity 特性还处于 beta 状态,相比 nodeSelector 来说有几点优势:

  • 1、提供更多的表示式(不仅仅是 and 匹配)

  • 2、可以指定 “软” 规则限制而不仅仅是硬限制,因此即使调度器不能满足它的规则,也可以正常调度 pod

  • 3、可以针对 node 上运行的 pods 指定 labels,而不仅仅局限于 node 本身

affinity 特性拥有两种类型,一种是 node affinity,一种是 pod affinity/anti-affinity。node affinity 类似 nodeSelector,但同时拥有上文提到的 1、2 两点优势,pod affinity/anti-affinity 针对 pods 指定 labels,同时拥有以上三点优势。

Node affinity

K8s 在 1.2 的时候以 alpha 的特性引入 node affinity。node affinity 通过 node labels 约束 pod 调度节点。Node affinity 有两种类型:

  • requiredDuringSchedulingIgnoredDuringExecution (硬限制,同 nodeSelector)

  • preferredDuringSchedulingIgnoredDuringExecution (软限制)

看以下例子:

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: k8s.gcr.io/pause:2.0

以上规则表达的意思是,该 Pod 只能被调度到拥有 kubernetes.io/e2e-az-name=e2e-az1 或者 kubernetes.io/e2e-az-name=e2e-az2 标签的节点上,其中在满足之前标签条件的同时更倾向于调度在拥有 another-node-label-key=another-node-label-value 标签的节点上。

如果同时指定 nodeSelector 和 nodeAffinity,两者同时满足才会被调度。如果 nodeAffinity 中指定了多个 nodeSelectorTerms,只要满足其中一个 nodeSelectorTerms 匹配条件即可调度。如果 nodeSelectorTerms 中有多个 matchExpressions,那么自由满足所有的条件才会被调度。

Pod affinity and anti-affinity

pod 亲和性和反亲和性在 K8s 1.4 版本引入,它基于运行在 node 上的 pod 标签来限制 pod 调度在哪个节点上,而不是节点的标签。

pod 亲和性和反亲和性需要大量的计算,会显著降低集群的调度速度,不建议在大于几百个节点的集群中使用。

pod 反亲和性要求集群中的所有节点必须具有 topologyKey 匹配的标签,否则可能会导致意外情况发生。

同 node affinity,pod 亲和性和反亲和性也有两种类型:

  • requiredDuringSchedulingIgnoredDuringExecution (硬限制)

  • preferredDuringSchedulingIgnoredDuringExecution (软限制)

不同的是,pod 通过 podAntiAffinity 设置反亲和性,如下例子:

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  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
  containers:
  - name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0

以上示例表示,pod 必须调度在至少运行一个 security=S1 标签的 pod 的节点上(更准确的说,这个 pod 可以运行在节点 N 上,如果该节点有标签 key 为 failure-domain.beta.kubernetes.io/zone,而且运行着标签为 security=S1 的实例)。另外,反亲和规则表明最好不要调度到运行有 security=S2 标签的 pod 的节点上(更准确的说,如果这个节点拥有标签 key 为 failure-domain.beta.kubernetes.io/zone,但运行有 security=S2 标签的 pod,那么这个节点就不会被优先选择调度)。

podAffinity 和 podAntiAffinity 支持 In、NotIn、Exists、DoesNotExist 四种表达式。

原则上,topologyKey 可以为任何合法的键值对。但是因为性能和安全的原因,有以下限制:

  • 1、针对 podAffinity 和 podAntiAffinity 中的 requiredDuringSchedulingIgnoredDuringExecution, topologyKey 为空是不允许的

  • 2、针对 podAntiAffinity 中的 requiredDuringSchedulingIgnoredDuringExecution,准入控制器选项 LimitPodHardAntiAffinityTopology 可以把 topologyKey 限制为 kubernetes.io/hostname,如果想自定义值,可以修改准入控制器或者直接禁用

  • 3、针对 podAntiAffinity 中的 preferredDuringSchedulingIgnoredDuringExecution,topologyKey 为空,则代表所有拓扑(仅限于 kubernetes.io/hostname, failure-domain.beta.kubernetes.io/zone and failure-domain.beta.kubernetes.io/region)

  • 4、除了以上情况,topologyKey 可以为任意合法的键值对

除了 labelSelector 和 topologyKey,还可以指定 namespace 的 labelSelector 作为匹配。labelSelector 和 topologyKey 属于同一级别,如果未定义或设置为空值,那么默认为定义 pod affinity 和 anti-affinity 所在的空间。

实践案例

始终调度在同一个节点

在一个三个节点的集群中,一个 web 应用程序依赖内存存储,如 redis,我们想 web 程序尽可能的和缓存调度在同一个节点上。redis 的 Deployment 配置如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
        image: redis:3.2-alpine

redis-cache 规则表达 pod 不被调度在同一个节点上。web-server 规则设置如下,表达 pod 不被调度在同一个节点上,并且必须调度在运行标签 app=store pod 的节点上。

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

新的 node affinity 支持 In、NotIn、Exists、DoesNotExist、Gt、Lt 操作符。可以使用 NotIn、DoesNotExist 来实现反亲和性,也可以通过 来实现。

相关更多应用场景,建议阅读 ,讲解的非常详细。

Assigning Pods to Nodes
label selectors
node taints
Node affinity and NodeSelector 设计文档
pod affinity and anti-affinity 设计文档
抽象优雅的 Affinity