内容简介:无论是 IaaS 还是 PaaS,在调度方面会收到非常多类似的需求,比如基于节点类型的调度,实例之间亲和性调度等等。数年前做 OpenStack 时,那时 OpenStack 的调度功能很基础,所以笔者做了不少开发,特别是亲和性调度,因对全局缺乏充分的认知,导致代码的复用性很差,用户体验也不佳。直到接触 K8S,深入了解后愈发感叹 K8S 在调度方面的抽象之优雅,实现之精美,从实际情况上看,K8S 满足我们对调度的基本需求,几乎无需做额外的开发。Kubernetes Scheduler 根据调度算法将 p
无论是 IaaS 还是 PaaS,在调度方面会收到非常多类似的需求,比如基于节点类型的调度,实例之间亲和性调度等等。数年前做 OpenStack 时,那时 OpenStack 的调度功能很基础,所以笔者做了不少开发,特别是亲和性调度,因对全局缺乏充分的认知,导致代码的复用性很差,用户体验也不佳。直到接触 K8S,深入了解后愈发感叹 K8S 在调度方面的抽象之优雅,实现之精美,从实际情况上看,K8S 满足我们对调度的基本需求,几乎无需做额外的开发。
Kubernetes Scheduler 根据调度算法将 pod 调度到最优的节点上,和 OpenStack 和 Mesos 等非常类似,kube-scheduler 首先过滤不符合要求的节点,然后从符合要求的节点中根据权重选出最优节点,这两个步骤在 K8S 中分别被称为 predicates 和 priorities。
Predicates 主要有以下类型:
- PodFitsResources:节点 CPU,内存资源是否充足。
- 节点是否压力大:节点 CPU,内存,磁盘资源是否存在压力。
- Volume 相关调度:节点是否支持相应的云厂商,卷的数量是否超出上限等。
- MatchNodeSelector:节点亲和性调度,即 node affinity。
- MatchInterPodAffinity:容器之间的亲和性调度,即 pod affinity。
- 其它类型性
本文主要介绍 MatchNodeSelector,MatchInterPodAffinity 这两个调度模块,即 node affinity 和 pod affinity。
NodeAffinty
需求来源
源于硬件和软件层多样性,我们需要将某个 pod 调度到某些特定的节点上,例如指定机房,存储类型,网络类型等等:
- 指定机房调度:某些业务希望部署在指定的机房中。
- 专属节点资源:某些机器属于某个业务独享,只有该业务方的容器才能调度到这些节点上运行。
- 磁盘类型调度:计算节点的磁盘类型包含 ssd 和 sata,其中 sata 盘的 IO 性能较差。对于 IO 密集型 Pod,我们希望将其调度到磁盘类型为 ssd 的服务器上,对于非 IO 密集型 Pod,将其调度到磁盘类型为 sata 盘的服务器上。
- 存储类型调度:不同节点可能支持不同的持久化存储模式,例如:local/ceph/gluster 等。我们需要根据 pod 要求的存储类型将其调度到相应的节点上。
- 网络调度:根据网络信息调度到支持该网络的节点。
K8S 将这些依赖节点是否满足特定条件的调度做良好的抽象和实现,使得我们仅需要给这些节点打上相应的 label 即可完成 pod 调度,无需再做额外的代码开发。
K8S 实现
nodeSelector
K8S 早期采用 nodeSelector 将 pod 调度到具有特定 label 的节点上。它的匹配规则简单,功能也相对简单,但是具有直观易用的特点。以磁盘类型调度为例,它采用 label 标记节点的磁盘类型:
$ kubectl lable nodes node01 disktype=ssd
创建 Pod 时在 nodeSelector 注明对磁盘类型 disk_type,如下:
apiVersion: v1 kind: Pod metadata: name: scheduler-to-ssd-node spec: containers: - name: nginx image: registry.cn-beijing.aliyuncs.com/opendcp/nginx nodeSelector: disk_type: ssd
Node affinity
鉴于 nodeSelector 的功能过于简单,K8S 于 1.2 版本引入了 node affinity 功能,在 1.11 版本处于 beta 阶段。node affinity 同样采用 label 标记节点,创建 pod 时在 affinity 字段注明匹配规则,它的匹配规则丰富,使用灵活,但是用法复杂。基于官网的介绍,可以如下语句贴切的介绍 node affintiy:
this pod should (or, in the case of anti-affinity, should not) run in the node if the node meet rule Y.
Y is expressed as a LabelSelector
Y 表示 LabelSelector,它支持丰富的匹配符号,如:In, NotIn, Exists, DoesNotExist, Gt, Lt 等。Node affinity 支持两种调度模式:
- requiredDuringSchedulingIgnoredDuringExecution:一定要存在满足条件 Y 的节点,如果不存在,则 pod 创建失败,熟称 hard 模式。
- preferredDuringSchedulingIgnoredDuringExecution:优先选择满足条件 Y 的节点,如果不存在,则在其它节点中择优创建 pod,熟称 soft 模式。
以磁盘类型调度为例,它采用 label 标记节点的磁盘类型:
$ kubectl lable nodes node01 disktype=ssd
创建 Pod 时在 affinity 注明对磁盘类型 disk_type,如下:
apiVersion: v1 kind: Pod metadata: name: scheduler-to-ssd spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: disktype operator: In values: - ssd containers: - name: scheduler-to-ssd image: registry.cn-beijing.aliyuncs.com/opendcp/nginx
PodAffinity
需求来源
我们常常会收到亲和性/反亲和性相关的需求。亲和性多用于实现业务的就近部署,减少网络,降低延时;反亲和性多用于故障容灾,从多个维度分散实例,尽可能降低故障影响的同类业务实例数量,特别是数据存储类的业务,它们对反亲和性的需求往往很强烈。
从硬件资源拓扑角度来看,每个机架(rack)约有十多台的物理机和一(两)台接入交换机,这些接入交换机连接到汇聚交换机,汇聚交换机再连接到核心交换机。一般来说,汇聚交换机和核心交换机都会从硬件层面实现高可靠。
我们遇上了多种故障,最常见的是主机硬件故障,此外还有四子星机器电源故障,机柜电源故障,接入交换机故障等等。并非所有的机柜都做到双电源,并非所有的接入交换机都有冗余。所以机柜电源故障和接入交换机故障往往会影响整个机柜的实例。所以一般的业务方要求实例部署在不同的机器,特殊的业务方,如 DB 等等,需要将相同 DB 的实例分布在不同的机柜。此外因链路割接,故障演练等因素,某些业务还需要在有异地机房冗余。
K8S 实现
Pod affinity 功能于 1.4 版本引入,在 1.5 版本处于 alpha 阶段,在 1.11 版本依旧处于 beta 版本。关于 pod affinity/anti-affinity,官网用如下语句恰当的表达了其功能:
this pod should (or, in the case of anti-affinity, should not) run in an X if that X is already running one or more pods that meet rule Y
X is a topology domain like node, rack, cloud provider zone, cloud provider region, etc. Y is expressed as a LabelSelector
笔者认为上述表达非常到位,pod affinity/anti-affinty 的抽象非常优雅,功能强大。K8S 把 node, rack, zone, region 等多种拓扑层次进行抽象成了 topology domain,使得我们通过简单的配置即可实现节点/机柜/可用域/地区的亲和性或者反亲和性,无需额外代码开发。K8S 默认支持如下 topology domain。
- kubernetes.io/hostname
- failure-domain.beta.kubernetes.io/zone
- failure-domain.beta.kubernetes.io/region
用户可以方便的定义自己的 topology domain,以 rack 为例,首先给所有节点打上 rack 相关的 label 信息,如 rack=Rack01;然后在 pod 的 spec 中把 topologyKey 设置为 rack 即可。LabelSelector 功能与用法和上节 node affinity 中的一样,此处不在累述。Pod affinity 支持两种调度模式:
- requiredDuringSchedulingIgnoredDuringExecution:一定要存在满足条件 Y 的节点,如果不存在,则 Pod 创建失败,熟称 hard 模式。
- preferredDuringSchedulingIgnoredDuringExecution:优先选择满足条件 Y 的节点,如果不存在,则在其它节点中择优创建 Pod,熟称 soft 模式。
如下表示 3 redis 必须分布在不同的节点。
apiVersion: apps/v1 kind: Deployment metadata: name: redis-server spec: selector: matchLabels: app: redis replicas: 3 template: metadata: labels: app: redis spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - redis topologyKey: "kubernetes.io/hostname" containers: - name: web-app image: nginx:1.12-alpine ...
最后谈谈 pod affinity 的性能问题,对于 1.11 之前版本,亲和性调度过程中会查询所有的 pods,所以对于上千节点的大集群,调度一个 pod 多者需要数十秒的时间,严重影响用户体验。2018 年 4 月合入如下的 patch 极大的提升了调度效率,降低计算的复杂度,最终使得在一般情况下计算的复杂度和节点数量呈现线性关系。
Improve performance of affinity/anti-affinity predicate by 20x in large clusters
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 理解golang调度之一 :操作系统调度
- 理解golang调度之二 :Go调度器
- Golang 源码学习调度逻辑(三):工作线程的执行流程与调度循环
- Node.js CPU调度优化(多服务器多核心分配调度)
- Hadoop 容器调度器与公平调度器原理和实践深入剖析-Hadoop商业环境实战
- golang调度器
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。