亲和性调度
亲和性调度
除了让 K8s 集群调度器自动为 Pod 资源选择某个节点(默认调度考虑的是资源足够,并且 load 尽量平均),有些情况我们希望能更多地控制 Pod 应该如何调度。比如,集群中有些机器的配置更好(SSD,更好的内存等),我们希望比较核心的服务(比如说数据库)运行在上面;或者某两个服务的网络传输很频繁,我们希望它们最好在同一台机器上,或者同一个机房。
这种调度在 K8s 中分为两类:node affinity 和 Pod 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,在调度的时候就可以用到这些信息,用法也很简单,在 Pod 的 spec 字段下面加上 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 的过程类比成磁铁的吸引和互斥,不同的是除了简单的正负极之外,pod 和 Node 的吸引和互斥是可以灵活配置的。
kubernetes 1.2 版本开始引入这个概念,目前(1.6 版本)处于 beta 阶段,相信后面会变成核心的功能。这种方法比 nodeSelector 复杂,但是也更灵活,提供了更精细的调度控制。它的优点包括:
- 匹配有更多的逻辑组合,不只是字符的完全相等
- 调度分成软策略(soft)和硬策略(hard),在软策略的情况下,如果没有满足调度条件的节点,pod 会忽略这条规则,继续完成调度过程
策略
目前有两种主要的 Node affinity:requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution。前者表示 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 同时定义了 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 中 nodeSelectorTerms 有多个选项,如果节点满足任何一个条件就可以;如果 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 也有 requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution,意义也和之前一样。如果有使用亲和性,在 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:这个 pod 有 security:S1
label。互斥性保证节点最好不要调度到这样的节点,这个节点上运行了某个 pod,而且这个 pod 有 security:S2
label。
在 labelSelector
和 topologyKey
同级,还可以定义 namespaces
列表,表示匹配哪些 namespace 里面的 pod,默认情况下,会匹配定义的 pod 所在的 namespace;如果定义了这个字段,但是它的值为空,则匹配所有的 namespaces。