亲和性调度

亲和性调度

除了让K8s集群调度器自动为Pod资源选择某个节点(默认调度考虑的是资源足够,并且load尽量平均,有些情况我们希望能更多地控制Pod应该如何调度。比如,集群中有些机器的配置更好(SSD,更好的内存等,我们希望比较核心的服务(比如说数据库)运行在上面;或者某两个服务的网络传输很频繁,我们希望它们最好在同一台机器上,或者同一个机房。

这种调度在K8s中分为两类:node affinityPod affinity

选择Node

K8s中有很多对label的使用,node就是其中一例。label可以让用户非常灵活地管理集群中的资源,service选择Pod就用到了label。这篇文章介绍到的调度也是如此,可以根据节点的各种不同的特性添加label,然后在调度的时候选择特定label的节点。

在使用这种方法之前,需要先给Node加上label,通过kubectl非常容易做:

$ kubectl label nodes <Node-name> <label-key>=<label-value>

列出Node的时候指定 –show-labels参数就能查看Node都添加了哪些label

$ kubectl get nodes --show-labels

Node有了label,在调度的时候就可以用到这些信息,用法也很简单,在Podspec字段下面加上nodeSelector,它里面保存的是多个键值对,表示节点上有对应的label,并且值也匹配。还是举个例子看得明白,比如原来简单的nginx pod

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
    - name: nginx
      image: nginx

添加上Node选择信息,就变成了下面这样:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
    - name: nginx
      image: nginx
      imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd

这个例子就是告诉K8s调度的时候把Pod放到有SSD磁盘的机器上。除了自己定义的label之外,K8s还会自动给集群中的节点添加一些label,比如:

  • kubernetes.io/hostname:节点的hostname名称
  • beta.kubernetes.io/os:节点安装的操作系统
  • beta.kubernetes.io/arch:节点的架构类型
  • ……

不同版本添加的label会有不同,这些label和手动添加的没有区别,可以通过 --show-labels 查看,也能够用在 nodeSelector 中。

Node Affinity

Affinity翻译成中文是“亲和性”,它对应的是Anti-Affinity,我们翻译成“互斥”。这两个词比较形象,可以把Pod选择Node的过程类比成磁铁的吸引和互斥,不同的是除了简单的正负极之外,podNode的吸引和互斥是可以灵活配置的。

kubernetes 1.2版本开始引入这个概念,目前(1.6版本)处于beta阶段,相信后面会变成核心的功能。这种方法比nodeSelector复杂,但是也更灵活,提供了更精细的调度控制。它的优点包括:

  • 匹配有更多的逻辑组合,不只是字符的完全相等
  • 调度分成软策略(soft)和硬策略(hard,在软策略的情况下,如果没有满足调度条件的节点,pod会忽略这条规则,继续完成调度过程

策略

目前有两种主要的Node affinityrequiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution。前者表示Pod必须部署到满足条件的节点上,如果没有满足条件的节点,就不断重试;后者表示优先部署在满足条件的节点上,如果没有满足条件的节点,就忽略这些条件,按照正常逻辑部署。

IgnoredDuringExecution正如名字所说,pod部署之后运行的时候,如果节点标签发生了变化,不再满足pod指定的条件,pod也会继续运行。与之对应的是requiredDuringSchedulingRequiredDuringExecution,如果运行的pod所在节点不再满足条件,kubernetes会把pod从节点中删除,重新选择符合要求的节点。

软策略和硬策略的区分是有用处的,硬策略适用于pod必须运行在某种节点,否则会出现问题的情况,比如集群中节点的架构不同,而运行的服务必须依赖某种架构提供的功能;软策略不同,它适用于满不满足条件都能工作,但是满足条件更好的情况,比如服务最好运行在某个区域,减少网络传输等。这种区分是用户的具体需求决定的,并没有绝对的技术依赖。

匹配规则

Node Affinity的典型示例如下:

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同时定义了requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution两种nodeAffinity。第一个要求pod运行在特定AZ的节点上,第二个希望节点最好有对应的another-node-label-key:another-node-label-value标签。

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

  • Inlabel的值在某个列表中
  • NotInlabel的值不在某个列表中
  • Exists:某个label存在
  • DoesNotExist:某个label不存在
  • Gtlabel的值大于某个值(字符串比较)
  • Ltlabel的值小于某个值(字符串比较)

如果nodeAffinitynodeSelectorTerms有多个选项,如果节点满足任何一个条件就可以;如果matchExpressions有多个选项,则只有同时满足这些逻辑选项的节点才能运行pod

Pod Affinity

通过上一部分内容的介绍,我们知道怎么在调度的时候让pod灵活地选择node;但有些时候我们希望调度能够考虑pod之间的关系,而不只是pod-node的关系。pod affinity是在kubernetes1.4版本引入的,目前在1.6版本也是beta功能。

举个例子,我们系统服务A和服务B尽量部署在同个主机、机房、城市,因为它们网络沟通比较多;再比如,我们系统数据服务C和数据服务D尽量分开,因为如果它们分配到一起,然后主机或者机房出了问题,会导致应用完全不可用,如果它们是分开的,应用虽然有影响,但还是可用的。

pod affinity可以这样理解:调度的时候选择(或者不选择)这样的节点N,这些节点上已经运行了满足条件X。条件X是一组label选择器,它必须指明作用的namespace(也可以作用于所有的namespace,因为pod是运行在某个namespace中的。

node affinity相似,pod affinity也有requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution,意义也和之前一样。如果有使用亲和性,在affinity下面添加podAffinity字段,如果要使用互斥性,在affinity下面添加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: gcr.io/google_containers/pause:2.0

这个例子中,pod需要调度到某个zone(通过 failure-domain.beta.kubernetes.io/zone 指定,这个zone至少有一个节点上运行了这样的pod:这个podsecurity:S1label。互斥性保证节点最好不要调度到这样的节点,这个节点上运行了某个pod,而且这个podsecurity:S2label

labelSelectortopologyKey 同级,还可以定义 namespaces 列表,表示匹配哪些namespace里面的pod,默认情况下,会匹配定义的pod所在的namespace;如果定义了这个字段,但是它的值为空,则匹配所有的namespaces

上一页
下一页