Koordinator设计思路
- 拥抱 Kubernetes 上游标准,基于 Scheduler-Framework 来构建调度能力,而不是实现一个全新的调度器。构建标准形成共识是困难的,但破坏是容易的,Koordinator 社区与 Kubernetes sig-scheduling 社区相向而行。
- QoS 是系统的一等公民,与业界大多数调度器更多的关注编排结果(静态)不同,Koordinator 更关注 Pod 运行时质量(QoS),因为对于调度系统的用户而言,运行时稳定性是其业务成功的关键。
- 状态自闭环,Koordinator 认为调度必须是一个完整的闭环系统,才能满足企业级应用要求。因此,我们在第一个版本就引入了状态反馈回路,节点会根据运行时状态调优容器资源,中心会根据节点运行时状态反馈做调度决策。
- 中心调度 + 单机调度联合决策,中心调度看到全局视角,其决策可以找到集群中最适合应用需求的节点,而单机调度可以在节点侧做一定的灵活度,以应对应用突发的流量颠簸。
- 调度 + 重调度密切配合,调度解决 Pod 一次性放置的问题,而重调度才是驱动集群资源编排长期保持最优化的关键。Koordinator 将建设面向 SLO 的重调度能力,持续的驱动应用的编排符合预定义的 SLO。
- 智能化、简单化,Koordinator 并不是就所有的选择暴露把问题留给客户,而是根据应用特征智能的为用户提供优化配置建议,简化用户使用 Kubernetes 的成本。
- 调度系统不止于解决调度、重调度、弹性伸缩等领域问题,一个完整的调度系统,需要具备基于历史数据驱动的自我迭代演进的能力。为工作负载的运行历史状态建立数据仓库,基于这些运行历史的大数据分析,持续的改进应用间在各个维度的的亲和、互斥关系,才能在用户运行时体验、集群资源利用效率同时达到最佳状态。
优先级和QoS
- 资源优先级策略的激进与保守,决定了集群资源的超卖容量,与资源稳定性的高低成反相关,以k8s PriorityClass声明,分为Product、Mid、Batch、Free四个等级,表现为 超卖资源从PriorityClass 高的Pod来,用到低的地方去。
- QoS主要表现为使用的隔离参数不同,以Pod Label 的形式声明。
- Koordinator 针对智能化调度的设计思路如下:
- 优先级:智能资源超卖,超卖的基本思想是去利用那些已分配但未使用的资源来运行低优先级的任务(一种新资源的上报和调度)。Koordinator 首先解决的是节点资源充分利用的问题,通过分析节点容器的运行状态计算可超卖的资源量,并结合 QoS 的差异化诉求将超卖的资源分配给不同类型的任务,大幅提高集群的资源利用率。 PS:已分配但未使用的资源,对应下图灰色和深蓝色之间的部分。
- Priority 和 QoS 是两个正交的维度,可以排列组合使用,部分排列组合不合法。
- QoS 感知的重调度,当节点中 Pod 的运行时 QoS 不符合预期时(干扰检测),Koordinator 将智能决策抑制更低优先级的任务亦或是迁移当前受到干扰的容器,从而解决应用 QoS 不满足导致的问题。问题:重调度的细节问题很多,Pod驱逐后 集群是否有资源保证可以运行,涉及到资源预留。
如何保证QoS
- QoS 表示应用在节点上运行时得到的物理资源质量,以 CPU 为主维度,包含了System、LS、BE三类,LS又细分为LSE(Latency-Sensitive Excluded)、LSR(Latency-Sensitive Reserved)、LS(Latency-Sensitive)和 LS(Best Effort),保障策略
-
- CPU 方面,通过内核自研的 Group Identity 机制,针对不同 QoS 等级设置内核调度的优先级,优先保障 LSR/LS 的 cpu 调度,允许抢占 BE 的 CPU 使用,以达到最小化在线应用调度延迟的效果;对于 LS 应用的突发流量,提供了 CPU Burst 策略以规避非预期的 CPU 限流。
- 内存方面,由于容器 cgroup 级别的直接内存回收会带来一定延时,LS 应用普遍开启了容器内存异步回收能力,规避同步回收带来的响应延迟抖动。除此之外,针对末级缓存(Last-Level Cache,LLC)这种共享资源,为了避免大数据等 BE 应用大量刷 Cache 导致 LS/LSR 应用的 Cache Miss Rate 异常增大,降低流水线执行效率,引入了 RDT 技术来限制 BE 应用可分配的 Cache 比例,缩小其争抢范围。
阿里云容器服务差异化 SLO 混部技术实践
- CPU 资源质量
- 添加 K8S CPU limit 会降低服务性能? 可以查看container_cpu_cfs_throttled_periods_total 指标
- CPU Burst,例如对于 CPU Limit = 2 的容器,操作系统内核会限制容器在每 100 ms 周期内最多使用 200 ms 的 CPU 时间片,进而导致请求的响应时延(RT)变大。当容器真实 CPU 资源使用小于 cfs_quota 时,内核会将多余的 CPU 时间“存入”到 cfs_burst 中;当容器有突发的 CPU 资源需求,需要使用超出 cfs_quota 的资源时,内核的 CFS 带宽控制器(CFS Bandwidth Controller,简称 BWC) 会允许其消费其之前存到 cfs_burst 的时间片。最终达到的效果是将容器更长时间的平均 CPU 消耗限制在 quota 范围内,允许短时间内的 CPU 使用超过其 quota。
- CPU 拓扑感知调度。在多核节点下,进程在运行过程中经常会被迁移到其不同的核心,考虑到有些应用的性能对 CPU 上下文切换比较敏感,kubelet 提供了 static 策略,允许 Guarantee 类型 Pod 独占 CPU 核心。CPU 绑核并不是“银弹”,若同一节点内大量 Burstable 类型 Pod 同时开启了拓扑感知调度,CPU 绑核可能会产生重叠,在个别场景下反而会加剧应用间的干扰。因此,拓扑感知调度更适合针对性的开启。
- 针对低优先级离线容器的 CPU 资源压制能力:内核Group Identity,Group Identity 功能可以对每一个容器设置身份标识,以区分容器中的任务优先级,系统内核在调度包含具有身份标识的任务时,会根据不同的优先级做相应处理。比如高优先级任务 有更多资源抢占机会。
- 在 CPU 被持续压制的情况下,BE 任务自身的性能也会受到影响,将其驱逐重调度到其他空闲节点反而可以使任务更快完成。
- 内存资源质量
- 时延敏感型业务(LS)和资源消耗型(BE)任务共同部署时,资源消耗型任务时常会瞬间申请大量的内存,使得系统的空闲内存触及全局最低水位线(global wmark_min),引发系统所有任务进入直接内存回收的慢速路径,进而导致延敏感型业务的性能抖动。
- 后台异步回收,当容器内存使用超过 memory.wmark_ratio 时,内核将自动启用异步内存回收机制,提前于直接内存回收,改善服务的运行质量。
干扰检测
- 干扰检测和优化的过程可以分为以下几个过程:
- 干扰指标的采集和分析:选取干扰检测使用的指标需要考虑通用性和相关性,并尽量避免引入过多额外的资源开销。
- 干扰识别模型及算法:分为横向和纵向两个维度,横向是指分析同一应用中不同容器副本的指标表现,纵向是指分析在时间跨度上的数据表现,识别异常并确定“受害者”和“干扰源”。
- 干扰优化策略:充分评估策略成本,通过精准的压制或驱逐策略,控制“干扰源”引入的资源竞争,或将“受害者”进行迁移。同时建设相关模型,用于指导应用后续的调度,提前规避应用干扰的发生。
- Koordinator v1.1发布:负载感知与干扰检测采集当前 Koordinator 已经实现了一系列 Performance Collector,在单机侧采集与应用运行状态高相关性的底层指标,并通过 Prometheus 暴露出来,为干扰检测能力和集群应用调度提供支持。目前提供以下几个指标采集器:
- CPICollector:用于控制 CPI 指标采集器。CPI:Cycles Per Instruction。指令在计算机中执行所需要的平均时钟周期数。CPI 采集器基于 Cycles 和 Instructions 这两个 Kernel PMU(Performance Monitoring Unit)事件以及 perf_event_open(2) 系统调用实现。
- PSICollector:用于控制 PSI 指标采集器。PSI:Pressure Stall Information。表示容器在采集时间间隔内,因为等待 cpu、内存、IO 资源分配而阻塞的任务数。使用 PSI 采集器前,需要在 Anolis OS 中开启 PSI 功能,您可以参考文档获取开启方法。
- 腾讯云:存量 Pod 在节点上也有可能发生高负载,这时我们在节点上部署 Pod-Problem-Detecor、NodeProblem-Detecor,检测出哪个 Pod 会导致节点高负载,哪些 Pod 属于敏感 Pod,通过事件上报告诉API Server,让调度器将异常 Pod、敏感 Pod 重新调度到空闲节点。
- 云原生场景下,如何缓减容器隔离漏洞,监控内核关键路径?OpenCloudOS 社区中的容器级别的性能跟踪机制——SLI,从容器的角度对 CPU、内存资源的竞争情况进行跟踪、观测,从而为容器性能问题的定位、分析提供可靠的指标。
- 用户态周期性监测:SLI 通过cgroup 接口提供容器的性能数据,用户态可以通过这些数据对容器性能进行监控。
- 容器内负载情况监控。容器内处于 R/D 态的进程平均数量(分别是 1min、5min、15min 的容器平均负载),R 进程数量指标:评估容器内进程数量是否 overload。D进程数量指标:D状态进程的数量可以反馈IO等待以及锁竞争等的情况
- 进程在内核态执行时间。内核态时间可能是因为系统调用、中断或者缺页异常等原因引起的。内核态的执行时间过长,会导致用户业务有较大延迟,从而引起性能抖动问题。
- 调度延迟, 监控容器进程的调度延迟信息(容器进程在调度队列上的等待时间),反馈容器的 CPU 竞争情况,过大的调度延迟会导致业务出现性能抖动。
- iowait 延迟,进程 IO 完成而引起的延迟时间。反馈容器进程的 IO 性能问题,过大的 IO 延迟会让业务文件访问产生性能问题
- 内存延迟,监控容器进程的内存申请延迟信息,反馈容器的内存申请性能情况,过大的内存申请延迟会导致业务出现性能抖动。
- 容器场景下,各个容器是相互独立的应用程序。由于不同容器在运行过程中,各自的资源使用情况、运行情况都不同,需要有一个独立的地方记录不同容器内核层面的异常日志信息,上层应用可以根据日志信息,直接定位到对应容器,从而进行合理的调度等操作;mbuf 就应运而生。
- 借助mbuf 如何实现混部时的干扰检测呢? 比如在线容器A延迟增加,主要原因是离线计算容器B导致(如何定位到)?在线容器A延迟增加,触发阈值打印的是prev进程的信息以及堆栈,这样就可以知道是被谁干扰的。PS:从cpu 执行的视角,cpu 时间被分摊给容器ABC等等,比例一段时间内是固定的,现在容器A延迟增加,cpu时间分摊比例相对了之前,多了很多容器B的,就知道是被B干扰的?
koordinator架构
下图展示了 Koordinator 系统的整体架构和各组件的角色分工,其中绿色部分描述了 K8s 原生系统的各个组件,蓝色部分是 Koordinator 在此基础上的扩展实现。从整个系统架构来看,我们可以将 Koordinator 分为中心管控和单机资源管理两个维度。在中心侧,Koordiantor 在调度器内部和外部分别都做了相应的扩展能力增强;在单机侧,Koordinator 提供了 Koordlet 和 Koord Runtime Proxy 两个组件,负责单机资源的精细化管理和 QoS 保障能力。
-
- Koordinator 各组件的详细功能如下
- Koord-Manager
- SLO-Controller:提供资源超卖、混部 SLO 管理、精细化调度增强等核心管控能力。
- Recommender:围绕资源画像为应用提供相关的弹性能力。
- Colocation Profile Webhook:简化 Koordinator 混部模型的使用,为应用提供一键接入的能力,自动注入相关优先级、QoS 配置。
- Koord extensions for Scheduler:面向混部场景的调度能力增强。
- Koord descheduler:提供灵活可扩展的重调度机制。
- Koord Runtime Proxy:作为 Kubelet 和 Runtime 之间的代理,满足不同场景的资源管理需求,提供插件化的注册框架,提供相关资源参数的注入机制。
- Koordlet:在单机侧负责 Pod 的 QoS 保障,提供细粒度的容器指标采集,以及干扰检测和调节策略能力,并支持一系列的 Runtime Proxy 插件,用于精细化的隔离参数注入。
在 Koordinator 的设计模型中,一个核心的设计概念就是优先级(Priority),Koordinator 定义了四个等级,分别是 Product、Mid、Batch、Free ,Pod 需要指定申请的资源优先级,调度器会基于各资源优先级总量和分配量做调度。各优先级的资源总量会受高优先级资源的 request 和 usage 影响,例如已申请但未使用的 Product 资源会以 Batch 优先级再次分配。节点各资源优先级的具体容量,Koordinator 会以标准的 extend-resource 形式更新在 Node 信息中。
下图展示了一个节点各资源优先级的容量情况,其中黑色的直线 total 代表了节点的物理资源总量,红色折线代表了高优先级 Product 的真实使用量,蓝色折线到黑色直线之间反映了 Batch 优先级的资源超卖变化情况,可以看到当 Product 优先级处于资源消耗的低谷时,Batch 优先级可以获得更多的超卖资源。事实上,资源优先级策略的激进或保守,决定了集群资源的超卖容量,这点我们也可以从图中绿色直线对应的 Mid 资源优先级超卖情况分析看出。
如下表所示,Koordinator 以 K8s 标准的 PriorityClass 形式对各资源优先级进行了定义,代表 Pod 申请资源的优先级。在多优先级资源超卖情况下,当单机资源紧张时,低优先级 Pod 会被压制或驱逐。此外,Koordinator 还提供了 Pod 级别的子优先级(sub-priority),用于调度器层面的精细化控制(排队,抢占等)。
-
Koordinator 的设计中另一个核心的概念是服务质量(Quality of Service),Koordinator 将 QoS 模型在 Pod Annotation 级别进行了扩展定义,它代表了 Pod 在单机运行过程中的资源质量,主要表现为使用的隔离参数不同,当单机资源紧张时会优先满足高等级 QoS 的需求。如下表所示,Koordinator 将 QoS 整体分为 System(系统级服务),Latency Sensitive(延迟敏感性的在线服务),Best Effort(资源消耗型的离线应用)三类,根据应用性能敏感程度的差异,Latency Sensitive 又细分为 LSE,LSR 和 LS。
-
各场景的实际使用举例如下。
- 典型场景:
- Prod + LS:典型的在线应用,通常对应用时延要求较高,对资源质量要求较高,也需要保证一定的资源弹性能力。
- Batch + BE:用于混部场景中的低优离线,对资源质量有相当的忍耐度,例如批处理类型的 Spark/MR 任务,以及 AI 类型的训练任务
- 典型场景的增强:
- Prod + LSR/LSE:比较敏感的在线应用,可以接受牺牲资源弹性而换取更好的确定性(如CPU绑核),对应用时延要求极高。
- Mid/Free + BE:与“Batch + BE”相比主要区别是对资源质量要求的高低不同。
- 非典型的应用场景:
- Mid/Batch/Free + LS:用于低优先级的在线服务、近线计算以及AI推理类等任务,这些任务相较于大数据类型任务,它们无法接受过低的资源质量,对其他应用的干扰也相对较低;而相较于典型的在线服务,它们又可以忍受相对较低的资源质量,例如接受一定程度的驱逐。
样例
Koordinator 支持多种工作负载的灵活接入混部,这里我们以 Spark 为例,介绍如何使用混部超卖资源。在 K8s 集群中运行 Spark 任务有两种模式:一种是通过 Spark Submit 提交,也就是在本地使用 Spark 客户端直接连接 K8s 集群,这种方式比较简单快捷,不过在整体的管理能力上有所缺乏,常用于开发自测;另一种方式是通过 Spark Operator 提交,如下图所示,它定义了 SparkApplication CRD,用于 Spark 作业的描述,用户可以通过 kubectl 客户端将提交 SparkApplication CR 到 APIServer,随后由 Spark Operator 负责作业生命周期以及 Driver Pod 的管理。
- 凭借 Koordinator 能力的加持,ColocationProfile Webhook 会自动为 Spark 任务的 Pod 注入相关混部配置参数(包括QoS,Priority,extened-resource等),如下所示。Koordlet 在单机侧负责 Spark Pod 在混部后不会影响在线应用性能表现,通过将 Spark 与在线应用进行混部,可以有效提升集群整体资源利用率。
- Spark Driver Pod example
apiVersion: v1
kind: Pod
metadata:
labels:
koordinator.sh/qosClass: BE
...
spec:
containers:
- args:
- driver
...
resources:
limits:
koordinator.sh/batch-cpu: "1000"
koordinator.sh/batch-memory: 3456Mi
requests:
koordinator.sh/batch-cpu: "1000"
koordinator.sh/batch-memory: 3456Mi