Kubernetes Scheduler Framework 扩展: 1. Coscheduling

本文涉及的产品
云原生大数据计算服务 MaxCompute,5000CU*H 100GB 3个月
云原生大数据计算服务MaxCompute,500CU*H 100GB 3个月
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: # 前言 ## 为什么Kubernetes需要Coscheduling功能? Kubernetes目前已经广泛的应用于在线服务编排,为了提升集群的的利用率和运行效率,我们希望将Kubernetes作为一个统一的管理平台来管理在线服务和离线作业。但是默认的调度器是以Pod为调度单元进行依次调度,不会考虑Pod之间的相互关系。但是很多数据计算类的作业具有All-or-Nothing特点,要求所有的

前言

为什么Kubernetes需要Coscheduling功能?

Kubernetes目前已经广泛的应用于在线服务编排,为了提升集群的的利用率和运行效率,我们希望将Kubernetes作为一个统一的管理平台来管理在线服务和离线作业。但是默认的调度器是以Pod为调度单元进行依次调度,不会考虑Pod之间的相互关系。但是很多数据计算类的作业具有All-or-Nothing特点,要求所有的任务都成功创建后才能正常运行,如果只是部分任务启动的话,启动的任务将持续等待剩余的任务被调度。

如下图所示,JobA需要4个Pod同时启动,才能正常运行。Kube-scheduler依次调度3个Pod并创建,到第4个Pod时,集群资源不足,则JobA的3个Pod处于空等的状态,导致集群资源浪费。

a7625e7e076ea1714d623c1d3bf0dfd1.png

如果出现更坏的情况的话,如下图所示,集群其他的资源刚好被JobB的3个Pod所占用,同时在等待JobB的第4个Pod创建,此时整个集群就出现了死锁。

ddfd5cc8d821e1b596d238c24d5c5107.png

社区相关的方案

社区目前有Kube-batch以及基于Kube-batch衍生的Vocalno 2个项目来解决上文中提到的痛点。实现的方式是通过开发新的调度器将Scheduler中的调度单元从Pod修改为PodGroup,以组的形式进行调度。使用方式是如果需要Coscheduling功能的Pod走新的调度器,其他的例如在线服务的Pod走Kube-scheduler进行调度。

这种情况虽然能够有效的解决Coscheduling的问题,但是同样引入了新的问题。如大家所知,对于同一集群资源,调度器需要中心化。但如果同时存在两个调度器的话,有可能会出现决策冲突,例如分别将同一块资源分配给两个不同的Pod,导致某个Pod调度到节点后因为资源不足,导致无法创建的问题。解决的方式只能是通过标签的形式将节点强行的划分开来,或者部署多个集群。这种方式通过同一个Kubernetes集群来同时运行在线服务和离线作业,势必会导致整体集群资源的浪费以及运维成本的增加。

Scheduler Framework

上文中两难的处境,使得大量数据计算类的任务无法迁移到Kubernetes上。与此同时,越来越多的特性被添加到Kubernetes scheduler中,使得scheduler的代码不断变大,逻辑也更复杂。系统越复杂,则问题的定位和修复就越困难。同时原有通过extender的方式扩展scheduler的方式存在扩展点有限、无法与scheduler共用Cache以及性能较差等问题。

为了解决如上问题,使scheduler扩展性更好、代码更简洁,社区提出Scheduler Framework的机制。

Scheduler Framework定义了一组扩展点,用户可以通过实现扩展点所定义的接口来定制自己的插件,并且将插件注册扩展点。Scheduler Framework在执行调度流程时,运行到相应的扩展点时,将调用用户注册的插件。

5ce4b6a50297400388a1c574eecd6374.png

我们基于Kube-scheduler framework基础上扩展实现Coscheduling的能力,解决原生调度器对于All-or-Nothing作业调度的问题。

scheduler-plugins

Kubernetes负责Kube-scheduler的小组sig-scheduler为了更好的管理调度相关的Plugin,新建了项目scheduler-plugins管理不同场景的Plugin。我们实现的Coscheduling的Plugin将会成为该项目的第一个插件。目前Kep已经merge,代码在Review阶段(https://github.com/kubernetes-sigs/scheduler-plugins/pull/4)

技术方案

总体架构

70f3b96659d7b7abfebff19d6ee6219a.png

PodGroup

我们通过label的形式来定义PodGroup的概念,拥有同样label的Pod同属于一个PodGroup。min-available是用来标识该PodGroup的作业能够正式运行时所需要的最小副本数。

labels:
     pod-group.scheduling.sigs.k8s.io/name: nginx
     pod-group.scheduling.sigs.k8s.io/min-available: "2"

备注: 要求属于同一个PodGroup的Pod必须保持相同的优先级

Permit

Framework的Permit插件提供了延迟绑定的功能,即Pod进入到Permit阶段时,用户可以自定义条件来允许Pod通过、拒绝Pod通过以及让Pod等待状态(可设置超时时间)。Permit的延迟绑定的功能,刚好可以让属于同一个PodGruop的Pod调度到这个节点时,进行等待,等待积累的Pod数目满足足够的数目时,再统一运行同一个PodGruop的所有Pod进行绑定并创建。

举个实际的例子,当JobA调度时,需要4个Pod同时启动,才能正常运行。但此时集群仅能满足3个Pod创建,此时与Default Scheduler不同的是,并不是直接将3个Pod调度并创建。而是通过Framework的Permit机制进行等待。

8bb1e6e8487120259de158d7894b0667.png

此时当集群中有空闲资源被释放后,JobA的中Pod所需要的资源均可以满足。

2baf07ba046f3cf500bbb9108ede99ac.png

则JobA的4个Pod被一起调度创建出来,正常运行任务。

5f864bec0b6827252fb45a7dff129705.png

QueueSort

由于Default Scheduler的队列并不能感知PodGroup的信息,所以Pod在出队时处于无序性(针对PodGroup而言)。如下图所示,a和b表示两个不同的PodGroup,两个PodGroup的Pod在进入队列时,由于创建的时间交错导致在队列中以交错的顺序排列。

bf0350e5c47b8c80849b563a32c037a1.png

当一个新的Pod创建后,入队后,无法跟与其相同的PodGroup的Pod排列在一起,只能继续以混乱的形式交错排列。

47f27c8933d14ee55dbe2e93cde3a103.png

这种无序性就会导致如果PodGroupA在Permit阶段处于等待状态,此时PodGroupB的Pod调度完成后也处于等待状态,相互占有资源使得PodGroupA和PodGroupB均无法正常调度。这种情况即是把死锁现象出现的位置从Node节点移动到Permit阶段,无法解决前文提到的问题。

针对如上所示的问题,通过实现QueueSort插件, 保证在队列中属于同一个PodGroup的Pod能够排列在一起。我们通过定义QueueSort所用的Less方法,作用于Pod在入队后排队的顺序:

func  Less(podA *PodInfo, podB *PodInfo) bool

首先,继承了默认的基于优先级的比较方式,高优先级的Pod会排在低优先级的Pod之前。
然后,如果两个Pod的优先级相同,我们定义了新的排队逻辑来支持PodGroup的排序。

  1. 如果两个Pod都是regularPod(普通的Pod),则谁先创建谁在队列里边排在前边。
  2. 如果两个Pod一个是regularPod,另一个是pgPod(属于某个PodGroup的Pod), 我们比较的是regularPod的创建时间和pgPod所属PodGroup的创建时间,则谁先创建谁在队列里边排在前边。
  3. 如果两个Pod都是pgPod,我们比较两个PodGroup的创建时间,则谁先创建谁在队列里边排在前边。同时有可能两个PodGroup的创建时间相同,我们引入了自增Id,使得PodGroup的Id谁小谁排在前边(此处的目的是为了区分不同的PodGroup)。

通过如上的排队策略,我们实现属于同一个PodGroup的Pod能够同一个PodGroup的Pod能够排列在一起。

df7e1a6f74c31a72fb963ba70681b93f.png

当一个新的Pod创建后,入队后,会跟与其相同的PodGroup的Pod排列在一起。

7e3b1a5c8d7978fd4af34a4ce9657be7.png

Prefilter

为了减少无效的调度操作,提升调度的性能,我们在Prefilter阶段增加一个过滤条件,当一个Pod调度时,会计算该Pod所属PodGroup的Pod的Sum(包括Running状态的),如果Sum小于min-available时,则肯定无法满足min-available的要求,则直接在Prefilter阶段拒绝掉,不再进入调度的主流程。

UnReserve

如果某个Pod在Permit阶段等待超时了,则会进入到UnReserve阶段,我们会直接拒绝掉所有跟Pod属于同一个PodGroup的Pod,避免剩余的Pod进行长时间的无效等待。

Coscheduling试用

安装部署

前提条件

  1. 目前仅支持标准专有集群,不支持托管版集群
  2. 目前仅支持Kubernetes 1.16版本
  3. 集群节点可以访问公网
  4. helm v3:ACK在master节点上默认安装helm,请确认版本是否为helm v3,如果是helm v2请升级值helm v3,安装helm v3请参考helm v3 安装

部署Coscheduling

安装ack-coscheduling时,会用增强后的调度器替换原生的Scheduler以支持Coscheduling功能,并且会修改Scheduler的相关Config文件。用户可通过下文提示的卸载功能恢复集群及相关配置。

下载helm chart包,执行命令安装

#  wget http://kubeflow.oss-cn-beijing.aliyuncs.com/ack-coscheduling.tar.gz
#  tar zxvf ack-coscheduling.tar.gz
#  helm install ack-coscheduling -n kube-system ./ack-coscheduling
NAME: ack-coscheduling
LAST DEPLOYED: Mon Apr 13 16:03:57 2020
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None

验证Coscheduling

在Master节点上,使用helm命令验证是否安装成功。

# helm get manifest ack-coscheduling -n kube-system | kubectl get -n kube-system -f -
NAME                           COMPLETIONS   DURATION   AGE
scheduler-update-clusterrole   1/1           8s         35s
scheduler-update               3/1 of 3      8s         35s

### 卸载Coscheduling
通过helm卸载服务,将kube-scheduler的版本及配置回归到安装之前的状态

# helm uninstall ack-coscheduling -n kube-system

使用方式

使用Coscheduling时,在创建的Pod处通过label的形式配置好min-available和name

labels:
    pod-group.scheduling.sigs.k8s.io/name: nginx
    pod-group.scheduling.sigs.k8s.io/min-available: "3"

name:用于表示PodGroup的Name

min-available: 用于表示当前集群资源至少满足min-available个pod时,才能统一调度创建

备注: 要求属于同一个PodGroup的Pod必须保持相同的优先级

Demo展示

当前集群资源为10核, 仅足够满足如下3个Nginx pod运行,我们把minAvailable的值设置为3

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 6
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
        pod-group.scheduling.sigs.k8s.io/name: nginx
        pod-group.scheduling.sigs.k8s.io/min-available: "3"
    spec:
      containers:
      - name: nginx
        image: nginx
        resources:
          limits:
            cpu: 3000m
            memory: 500Mi
          requests:
            cpu: 3000m
            memory: 500Mi

集群中运行3个Nginx Pod

# kubectl get pods
NAME          READY   STATUS    RESTARTS   AGE
nginx-4jw2m   0/1     Pending   0          55s
nginx-4mn52   1/1     Running   0          55s
nginx-c9gv8   1/1     Running   0          55s
nginx-frm24   0/1     Pending   0          55s
nginx-hsflk   0/1     Pending   0          55s
nginx-qtj5f   1/1     Running   0          55s

如果此时minAvailable设置为4,则因为资源不满足minAvailable的要求,所有的Nginx Pod均处于Pending状态

# kubectl get pods
NAME          READY   STATUS    RESTARTS   AGE
nginx-4vqrk   0/1     Pending   0          3s
nginx-bw9nn   0/1     Pending   0          3s
nginx-gnjsv   0/1     Pending   0          3s
nginx-hqhhz   0/1     Pending   0          3s
nginx-n47r7   0/1     Pending   0          3s
nginx-n7vtq   0/1     Pending   0          3s

未来展望

通过Scheduler Framework的不断优化,我们相信未来会释放更大的潜力,用户可以通过插件的扩展就可以满足自身的需求,再也不用陷入自定义调度器的困境中。同时为了更好的支持数据计算类型的任务迁移到Kubernetes平台中,我们也在努力将数据计算类型中常用Capacity Scheduling、Dominant Resource Fairness和多队列管理等特性,通过Scheduler Framework的机制来融入到原生的Kube-scheduler的中,敬请期待。

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
5月前
|
运维 Kubernetes Cloud Native
OpenKruise是一个基于Kubernetes的扩展套件
OpenKruise是一个基于Kubernetes的扩展套件【1月更文挑战第14天】【1月更文挑战第68篇】
39 2
|
5月前
|
Kubernetes 负载均衡 Cloud Native
猿创征文|云原生|kubernetes二进制1.18单master扩展为多master
猿创征文|云原生|kubernetes二进制1.18单master扩展为多master
86 0
|
2月前
|
Kubernetes 算法 调度
在k8S中,Scheduler使用哪两种算法将Pod绑定到worker节点?
在k8S中,Scheduler使用哪两种算法将Pod绑定到worker节点?
|
2月前
|
Kubernetes 负载均衡 算法
如何在kubernetes中实现分布式可扩展的WebSocket服务架构
如何在kubernetes中实现分布式可扩展的WebSocket服务架构
54 1
|
2月前
|
Kubernetes API 调度
在k8S中,Scheduler作用及实现原理是什么?
在k8S中,Scheduler作用及实现原理是什么?
|
3月前
|
Kubernetes 持续交付 Python
Kubernetes(通常简称为K8s)是一个开源的容器编排系统,用于自动化部署、扩展和管理容器化应用程序。
Kubernetes(通常简称为K8s)是一个开源的容器编排系统,用于自动化部署、扩展和管理容器化应用程序。
|
4月前
|
Kubernetes 监控 调度
K8S中Scheduler原理分析
【6月更文挑战第20天】K8S Scheduler是集群的关键组件,它监听API Server,为新Pod选择合适的Node。
|
5月前
|
Kubernetes 监控 Docker
|
5月前
|
Kubernetes 监控 调度
|
5月前
|
Kubernetes API 调度
总结归纳Kubernetes | 一站式速查知识,助您轻松驾驭容器编排技术(水平扩展控制)
总结归纳Kubernetes | 一站式速查知识,助您轻松驾驭容器编排技术(水平扩展控制)
109 0