本文引见爱奇艺针对视频消费场景、在 Kubernetes(以下简称 K8s) 集群优化方面的理论:如何使高优先级任务取得更多的 CPU 资源,更快完成任务。 背景 视频消费集群所面临的一个应战是 K8s 原生机制无法分辨业务优先级。视频消费任务具有不同的业务优先级和资源需求,业务优先级是依据视频节目的重要水平及上线时间的紧迫水平中止分辨的,重点节目相关的任务优先级高,上线时间紧迫的任务优先级高,其他节目的优先级低。 资源需求是从任务类型维度分辨,如超清视频任务需求 8 CPU,普通视频任务需求 4 CPU。因而存在低优先级任务和高优先级任务申请相同数量 CPU 的状况,例如高优先级超清视频任务和低优先级超清视频任务都申请 8 CPU。 K8s 原生机制并不感知业务优先级,在上述状况下会给两者分配同样多的 CPU 资源。用户希冀高优先级任务能够抢占低优先级任务的 CPU 资源,从而更快执行。 业界也提出了相似于业务优先级的概念,如参考链接中提出的应用优先级,但是并未给出细致的处置计划。本文引见了一种允许高优先级任务抢占低优先级任务 CPU 资源,加速高优先级任务执行的计划。该计划不只能够在提交任务时指定优先级,还能够对执行过程中的任务中止优先级调整,满足视频消费效率请求。 原理概述 为了给高优先级任务提供更多的 CPU 资源,我们在集群中每个物理节点上运转一个代理效劳进程,该代理进程会分辨节点上任务的优先级,将高优先级任务对应的 Cgroups cpu 子系统中控制 CPU 运用量的值调高,从而允许高优先级任务能够抢占低优先级任务的 CPU 资源,使其更快地执行。这个计划依赖于 K8s 集群中各个节点关闭 CPU 限流,从而使高优先级任务能够自由抢占低优先级任务的 CPU 资源,不会遭到 CPU 限流的影响。 高优先级任务 CPU 抢占 前提:关闭节点 CPU 限流 在 K8s 中,任务以 Pod 方式存在,其 CPU 资源分配和运用遭到 Pod 元数据中两个字段控制:CPU Resource Requests 和 CPU Resource Limits,Pod 可运用的 CPU 总量介于两者之间。CPU Resource Requests 为 Pod 提供了最少 CPU 保障,该字段的值会写入到 Pod 对应的 Cgroups cpu 子系统 cpu.shares 文件中,经过 cpu.shares 保障 Pod 运用的资源量。CPU Resource Limits 为 Pod 提供 CPU 运用上限,它的值对应到 Cgroups cpu 子系统的 cpu.cfs_quota_us 和 cpu.cfs_period_us 文件,细致对应关系请参考 链接。 为了解除 CPU Resource Limits 对 Pod 的 CPU 限流,我们在视频消费 K8s 集群中经过 kubelet 的配置项 '--cpu-cfs-quota=false' 关闭了 CPU CFS Quota,允许高优先级 Pod 自由抢占低优先级 Pod 的 CPU 资源而不会遭到限流。 顺便一提,关闭 CPU CFS Quota 之后,Pod 在需求时,能够超额运用节点上存闲暇的 CPU,提升了集群的 CPU 应用率和 Pod 的执行效率。 整体设计 该计划的名字为 cpu-share-syncer,基本原理章节中曾经概述了其思想,整体设计如图 1 所示: 图1 cpu-share-syncer 总体设计 运用方式 cpu-share-syncer 代理效劳采用 K8s 中 DaemonSet 控制器的方式中止部署,使得每个 K8s 工作节点之上都运转了一个代理效劳 Pod,这些 Pod 经过 hostpath 的方式挂载了宿主机 Cgroups 途径以调整 Pod 对应的 cpu.shares 文件。 代理效劳如何知道哪些 Pod 是高优先级任务呢?这需求用户在创建高优先级 Pod 时,在 Pod 的注解字段中添加一个表示较高 CPU 权重的 annotation:iqiyi.com/cpu-share,该注解值的类型为整数,在含义上与 CPU Resource Requests 相似,因而用户能够依据 CPU Resource Requests 字段的规则来设置 iqiyi.com/cpu-share 的值,只是 iqiyi.com/cpu-share 仅支持以一个 CPU 的 1/1024 为单位。例如,假如希冀 Pod 在运转时能够运用 10 CPU 的资源,iqiyi.com/cpu-share 的值应该设置为10 * 1024 = 10240,这和 Cgroups 中对 CPU 的计量单位坚持分歧。关于曾经运转的 Pod,用户能够经过 patch 方式在 Pod 注解字段中添加 iqiyi.com/cpu-share 注解改动任务的优先级。 cpu-share-syncer 代理效劳检测到 Pod 中存在这个 annotation,就会将其值写入到 Pod 对应 cpu.shares 文件中,抵达用户指定 Pod CPU 权重的目的。 因而,关于设置了 iqiyi.com/cpu-share annotation 的 Pod,会依照 CPU Resource Requests 字段的值中止调度,依照 iqiyi.com/cpu-share的值在节点上分配 CPU 资源。 iqiyi.com/cpu-share 的值越大,Pod 对 CPU 的权重就越高,抢占其他 Pod CPU 资源就会越多。关于高优先级 Pod,用户能够依据需求将 iqiyi.com/cpu-share 的值设置为较高的值,获取更多的 CPU 资源。 技术细节 cpu-share-syncer 代理效劳的细致工作流程如图 2 所示: 图2 cpu-share-syncer 细致处置流程 cpu-share-syncer 代理是一个长生命周期的任务,它会周期性地将用户经过 Pod annotation 指定的 CPU 权重写入到 Pod 对应的 cpu.shares 文件中,周期性执行的缘由是避免 Pod 由于某种缘由重启之后,用户指定的 CPU 权重丧失。周期能够由用户中止配置,默许与 kubelet 进程中 CPUManager 同步周期(默许 10 秒)坚持分歧。需求留意的是,kubelet 中 CPUManager 会周期性的同步 Pod 中容器对应的 cpu.shares 文件,该操作仅会影响 Pod 内容器之间的相对优先级,细致信息可参考 [3]。而 cpu-share-syncer 代理同步的是 Pod 自身对应的 cpu.shares 文件,该同步工作会改动整个 Pod 相关于其他 Pod 的优先级。两个同步操作互不影响。 cpu-share-syncer 代理睬从 master上的 kube-apiserver 进程获取其所在节点上的 Pod 信息,为了避免每次全量从 kube-apiserver 获取节点上一切 Pod 信息,减轻 kube-apiserver 的担负,运用 List-Watch 的方式获取。 在获取节点上一切 Pod 之后,cpu-share-syncer 代理睬对这些 Pod 中止遍历,并从 Pod 注解中取出 iqiyi.com/cpu-share 对应的值。然后依据 Pod UID 找到其对应的 cpu.shares 文件,将获取的值写入到这个文件中。关于用户未指定优先级的 Pod,cpu-share-syncer 不中止处置,这些 Pod 对应的 cpu.shares 值仍为 CPU Resource Requests 对应的值,例如 1 核的 CPU 申请对应的 cpu.shares 仍为 1024,没有被改动。这样高优先级 Pod 在节点上就被赋予更高的 CPU 权重,因而能够抢占其他 Pod 的 CPU 资源,从而更快地执行。 cpu-share-syncer 测试结果 为了考证 cpu-share-syncer 的效果及其带来的收益,我们中止了单机功用考证和线上环境灰度测试。我们在 K8s 集群中的一个节点上,运转了两个 Pod,将它们的 CPU Resource Requests 值都设置为 10,并分别设置其 iqiyi.com/cpu-share 的值为 8192 及 16384,两个 Pod 中执行相同的压力测试命令 'stress --cpu 40',表示每个 Pod 启动 40 个线程对 CPU 性能中止压力测试。运用 prometheus 搜集相关指标,测试结果如下图所示: 图3 两个 Pod 对比考证 图中横轴表示时钟时间,纵轴表示 Pod 运转所占用的 CPU 总时间,单位为秒,蓝色折线表示权重为 16384 的 Pod 执行所占用的 CPU 时间,黄色折线表示权重为 8192 的 Pod 执行所占用的 CPU 时间,可察看到这两个 Pod 所耗费的 CPU 时间比例大约为 2:1,阐明Pod CPU 权重曾经生效,图中由于两个 Pod 启动先后次第问题,存在一定的误差。 图4 高优先级 Pod 抢占 CPU 时间 图中坐标轴含义与上图分歧,黄绿蓝三条折线表示先启动的三个 Pod,红色折线表示后启动的一个高优先级的 Pod。能够明显察看到,高优先级的 Pod 启动后,它所占用的 CPU 总时间大幅度高速增长,而早期启动的三个 Pod 由于遭到高优先级 Pod 的挤压,所占用的 CPU 总时间简直坚持不变,阐明高优先级任务抢占了早期 Pod 的 CPU 资源。 图5 消费环境灰度考证 图中横轴为时钟时间,纵轴为任务执行耗时比(执行时间/介质大小),耗时比越小效率越高,绿色折线表示未指定 CPU 权重的任务,蓝色折线表示指定了高 CPU 权重的任务。发现相较于未添加 CPU 权重的任务,高 CPU 权重的 Pod 执行效率提升了约 16%,完成了对高优先级任务加速的目的。 需求留意的是,从 CPU 权重(cpu.shares)比例来看,当高优先级任务存在且持续忙碌时,低优先级任务仅能取得十分少的 CPU 资源,参考 [5],效率提升会远不止 16%。经过剖析,我们发现灰度环境中的高优先级视频消费任务即便在完整放开 CPU 限流时,也不会吃掉机器上一切的 CPU 资源,它只占用自己所需的 CPU 资源。因而,灰度环境中的高优先级任务只是抢占了一部分低优先级任务的 CPU 资源满足自己的需求。另外,视频消费任务在整个生命周期中,需求经过从远端效劳器下载原始数据、集群中处置、将结果上传到远端效劳器三个过程。在下载、上传数据等候 IO 的过程中,高优先级任务不需求占用 CPU 资源,此时,低优先级任务能够再次取得一部分 CPU 资源。综合上面两部分缘由,我们以为高优先级任务执行效率提升 16% 是合理的。 总结及瞻望 我们经过 cpu-share-syncer 在关闭了 CPU CFS Quota 的 K8s 集群中提供了高优先级任务优先运用 CPU 的才干,完成了对高优先级任务的加速。目前 cpu-share-syncer 曾经普遍运转在爱奇艺视频消费集群中,为高优先级任务护航。 参考链接 https://tech.meituan.com/2019/08/22/kubernetes-cluster-management-practice.html https://kubernetes.io/zh/docs/tasks/administer-cluster/cpu-management-policies/ https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/scheduler/sched-design-CFS.rst?h=v5.17-rc1 |