加入收藏 | 设为首页 | 会员中心 | 我要投稿 上饶站长网 (https://www.0793zz.com.cn/)- 数据库平台、视觉智能、智能搜索、决策智能、迁移!
当前位置: 首页 > 云计算 > 正文

Kubernetes 资源拓扑感知调度优化

发布时间:2022-07-14 10:42:39 所属栏目:云计算 来源:互联网
导读:1.背景 1.1 问题源起 近年来,随着腾讯内部自研上云项目的不断发展,越来越多的业务开始使用云原生方式托管自己的工作负载,容器平台的规模因此不断增大。以 Kubernetes 为底座的云原生技术极大推动了云原生领域的发展,已然成为各大容器平台事实上的技术标
  1.背景
  1.1 问题源起
  近年来,随着腾讯内部自研上云项目的不断发展,越来越多的业务开始使用云原生方式托管自己的工作负载,容器平台的规模因此不断增大。以 Kubernetes 为底座的云原生技术极大推动了云原生领域的发展,已然成为各大容器平台事实上的技术标准。在云原生场景下,为了最大化实现资源共享,单台宿主机往往会运行多个不同用户的计算任务。如果在宿主机内没有进行精细化的资源隔离,在业务负载高峰时间段,多个容器往往会对资源产生激烈的竞争,可能导致程序性能的急剧下降,主要体现为:
 
  资源调度时频繁的上下文切换时间
  频繁的进程切换导致的 CPU 高速缓存失效
  因此,在云原生场景下需要针对容器资源分配加以精细化的限制,确保在 CPU 利用率较高时,各容器之间不会产生激烈竞争从而引起性能下降。
 
  1.2 调度场景
  腾讯星辰算力平台承载了全公司的 CPU 和 GPU 算力服务,拥有着海量多类型的计算资源。当前,平台承载的多数重点服务偏离线场景,在业务日益增长的算力需求下,提供源源不断的低成本资源,持续提升可用性、服务质量、调度能力,覆盖更多的业务场景。然而,Kubernetes 原生的调度与资源绑定功能已经无法满足复杂的算力场景,亟需对资源进行更加精细化的调度,主要体现为:
 
  (1)Kubernetes 原生调度器无法感知节点资源拓扑信息导致 Pod 生产失败
  kube-scheduler 在调度过程中并不感知节点的资源拓扑,当 kube-scheduler 将 Pod 调度到某个节点后,kubelet 如果发现节点的资源拓扑亲和性要求无法满足时,会拒绝生产该 Pod,当通过外部控制环(如 deployment)来部署 Pod 时,则会导致 Pod 被反复创建-->调度-->生产失败的死循环。
 
  (2)基于离线虚拟机的混部方案导致的节点实际可用 CPU 核心数变化
  面对运行在线业务的云主机平均利用率较低的现实,为充分利用空闲资源,可将离线虚拟机和在线虚拟机混合部署,解决公司离线计算需求,提升自研上云资源平均利用率。在保证离线不干扰在线业务的情况下,腾讯星辰算力基于自研内核调度器 VMF 的支持,可以将一台机器上的闲时资源充分利用起来,生产低优先级的离线虚拟机。由于 VMF 的不公平调度策略,离线虚拟机的实际可用核心数受到在线虚拟机的影响,随着在线业务的繁忙程度不断变化。因此,kubelet 通过 cadvisor 在离线宿主机内部采集到的 CPU 核心数并不准确,导致了调度信息出现偏差。
 
  图片
 
  cvm-architecture
 
  (3)资源的高效利用需要更加精细化的调度粒度
  kube-scheduler 的职责是为 Pod 选择一个合适的 Node 完成一次调度。然而,想对资源进行更高效的利用,原生调度器的功能还远远不够。在调度时,我们希望调度器能够进行更细粒度的调度,比如能够感知到 CPU 核心、GPU 拓扑、网络拓扑等等,使得资源利用方式更加合理。
 
  2.预备知识
  2.1 cgroups 之 cpuset 子系统
  cgroups 是 Linux 内核提供的一种可以限制单个进程或者多个进程所使用资源的机制,可以对 CPU、内存等资源实现精细化的控制。Linux 下的容器技术主要通过 cgroups 来实现资源控制。
 
  在 cgroups 中,cpuset 子系统可以为 cgroups 中的进程分配独立的 CPU 和内存节点。通过将 CPU 核心编号写入 cpuset 子系统中的 cpuset.cpus文件中或将内存 NUMA 编号写入 cpuset.mems文件中,可以限制一个或一组进程只使用特定的 CPU 或者内存。
 
  幸运的是,在容器的资源限制中,我们不需要手动操作 cpuset 子系统。通过连接容器运行时(CRI)提供的接口,可以直接更新容器的资源限制。
 
  复制
  // ContainerManager contains methods to manipulate containers managed by a
  // container runtime. The methods are thread-safe.
  type ContainerManager interface {
      // ......
      // UpdateContainerResources updates the cgroup resources for the container.
   UpdateContainerResources(containerID string, resources *runtimeapi.LinuxContainerResources) error
      // ......
  }
  1.
  2.
  3.
  4.
  5.
  6.
  7.
  8.
  2.2 NUMA 架构
  非统一内存访问架构(英语:Non-uniform memory access,简称 NUMA)是一种为多处理器的电脑设计的内存架构,内存访问时间取决于内存相对于处理器的位置。在 NUMA 下,处理器访问它自己的本地内存的速度比非本地内存(内存位于另一个处理器,或者是处理器之间共享的内存)快一些。现代多核服务器大多采用 NUMA 架构来提高硬件的可伸缩性。
 
  图片
 
  numa-architecture
 
  从图中可以看出,每个 NUMA Node 有独立的 CPU 核心、L3 cache 和内存,NUMA Node 之间相互连接。相同 NUMA Node 上的 CPU 可以共享 L3 cache,同时访问本 NUMA Node 上的内存速度更快,跨 NUMA Node 访问内存会更慢。因此,我们应当为 CPU 密集型应用分配同一个 NUMA Node 的 CPU 核心,确保程序的局部性能得到充分满足。
 
  2.3 Kubernetes 调度框架
  Kubernetes 自 v1.19 开始正式稳定支持调度框架,调度框架是面向 Kubernetes 调度器的一种插件架构,它为现有的调度器添加了一组新的“插件”API,插件会被编译到调度器之中。这为我们自定义调度器带来了福音。我们可以无需修改 kube-scheduler 的源代码,通过实现不同的调度插件,将插件代码与 kube-scheduler 编译为同一个可执行文件中,从而开发出自定义的扩展调度器。这样的灵活性扩展方便我们开发与配置各类调度器插件,同时无需修改 kube-scheduler 的源代码的方式使得扩展调度器可以快速更改依赖,更新到最新的社区版本。
 
  图片
 
  scheduling-framework-extensions
 
  调度器的主要扩展点如上图所示。我们扩展的调度器主要关心以下几个步骤:
 
  (1)PreFilter 和 Filter
  这两个插件用于过滤出不能运行该 Pod 的节点,如果任何 Filter 插件将节点标记为不可行,该节点都不会进入候选集合,继续后面的调度流程。
 
  (2)PreScore、Score 和 NormalizeScore
  这三个插件用于对通过过滤阶段的节点进行排序,调度器将为每个节点调用每个评分插件,最终评分最高的节点将会作为最终调度结果被选中。
 
  (3)Reserve 和 Unreserve
  这个插件用于在 Pod 真正被绑定到节点之前,对资源做一些预留工作,保证调度的一致性。如果绑定失败则通过 Unreserve 来释放预留的资源。
 
  (4)Bind
  这个插件用于将 Pod 绑定到节点上。默认的绑定插件只是为节点指定 spec.nodeName 来完成调度,如果我们需要扩展调度器,加上其他的调度结果信息,就需要禁用默认 Bind 插件,替换为自定义的 Bind 插件。

(编辑:上饶站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读