腾讯自研业务上云:优化 Kubernetes 集群负载的技术方案探讨

阅读数:43 2019 年 10 月 31 日 13:40

腾讯自研业务上云:优化Kubernetes集群负载的技术方案探讨

Kubernetes 的资源编排调度使用的是静态调度,将 Pod Request Resource 与 Node Allocatable Resource 进行比较来决定 Node 是否有足够资源容纳该 Pod。静态调度带来的问题是,集群资源很快被业务容器分配完,但是集群的整体负载非常低,各个节点的负载也不均衡。本文将介绍优化 Kubernetes 集群负载的多种技术方案。

Kubernetes 为什么使用静态调度?

静态调度,是指根据容器请求的资源进行装箱调度,而不考虑节点的实际负载。静态调度最大的优点就是调度简单高效、集群资源管理方便,最大的缺点也很明显,就是不管节点实际负载,极容易导致集群负载不高。

Kubernetes 为什么会使用静态调度呢?因为要做好通用的动态调度几乎是不可能的,对,是通用的动态调度很难都满足不同企业不同业务的诉求,结果可能适得其反。那是不是我们就没必要去往动态调度做技术尝试呢?未必!平台根据托管的业务属性,可以适当的通过 scheduler extender 的方式扩展 Kubernetes Scheduler 来做一定权重的动态调度决策。

集群资源构成

以 CPU 资源为例,一个大规模 Kubernetes 集群的资源组成结构大致如下:

腾讯自研业务上云:优化Kubernetes集群负载的技术方案探讨

由以下几部分组成:

每个节点的预留资源,对应 kubelet 的 system-reserved, kube-reserved, eviction-hard 配置的资源之和,Kubernetes 计算 Node 的 Allocatable 资源时会减去这部分预留资源。

目前我们集群的平均资源碎片大概在 5%~10% 左右,根据不同规格的 CVM 机型略有不同。这些资源碎片分散在集群中的各个节点,以 1c1g, 2c2g, 3cxg 为主,平台提供用户选择的容器规格都很难 match 到这些碎片,经常存在这这种情况:要调度某个 Pod 时,发现某个节点上的 cpu 足够,但是 mem 不足,或者相反。

剩下的就是可以被业务 Pod 真正分配使用的资源了,业务在选择容器规格时带有一定的主观性和盲目性,导致业务容器的负载很低,这样的业务占比一大就容易导致集群低负载的情况,但是集群按照 Kubernetes 静态调度策略又无法再容纳更多的业务容器了。如上图中所示的,集群分配 cpu 水位线很高,但是实际 cpu 利用率不高的情况。

提升集群负载的方案

除了借助强大的容器监控数据做一定权重的动态调度决策之外,是否还有其他方案可以用于解决静态调度带来的集群低负载问题呢?下面我将给出一整套技术方案,从多个技术维度来尝试提升 Kubernetes 集群负载。

腾讯自研业务上云:优化Kubernetes集群负载的技术方案探讨

Pod 分配资源压缩

前面提到,研发同学部署业务选择容器资源规格时,带有一定的盲目性,而且 Kubernetes 原生也不支持实时无感知的修改容器规格(虽然这可以通过 Static VPA 方案解决),导致业务容器负载低。为了解决这个问题,我们可以给 Pod Request Resource 做一定比例的压缩(Pod Limit Resource 不压缩)。注意压缩 Pod Request Resource 只发生在 Pod 创建或者重建的时候,比如业务做变更发布之时,对于正常运行中的 Pod 不能做这一动作,否则可能导致对应 Workload Controller 重建 Pod(取决于 Workload 的 UpdateStrategy)对业务带来影响。

需要注意的是:

每个 Workload 负载变动规律不同,因此 Pod 分配资源压缩比例也对应不一样,需要支持每个 Workload 自定义配置,而且这是对用户无感知的。这个压缩比,我们设置到 Workload 的 Annotation 中,比如 cpu 资源压缩对应 Annotation stke.platform/cpu-requests-ratio;

压缩比,谁去设置?自研组件 (Pod-Resource-Compress-Ratio-Reconciler) 基于 Workload 的历史监控数据,动态的 / 周期性去调整压缩比。比如某 Workload 连续 7d/1M 的负载持续很低,那么可以把压缩比设置的更大,以此让集群剩余可分配资源更大,容纳更多的业务容器。当然实际上压缩比的调整策略并不会这么简单,需要更多的监控数据来辅助。

Pod 分配压缩特性一定要是可以关闭的和恢复的,通过 Workload Annotation stke.platform/enable-resource-compress: "n" 针对 Workload 级别 disable,通过设置压缩比为 1 进行压缩恢复。

何时通过压缩比去调整 Pod Spec 中的 Request Resource?Kubernetes 发展到现阶段,直接改 Kubernetes 代码是最愚蠢的方式,我们要充分利用 Kubernetes 的扩展方式。这里,我们通过 kube-apiserver 的 Mutating Admission Webhook 对 Pod 的 Create 事件进行拦截,自研 webhook(pod-resource-compress-webhook) 检查 Pod Annotations 中是否 enable 了压缩特性,并且配置了压缩比,如果配置了,则根据压缩比重新计算该 Pod 的 Request Resource,Patch 到 APIServer。

腾讯自研业务上云:优化Kubernetes集群负载的技术方案探讨

Node 资源超卖

Pod 资源压缩方案,是针对每个 Workload 级别的资源动态调整方案,优点是细化到每个 Workload,能做到有的放矢,缺点是业务不做变更发布,就没有效果,见效慢。

Node 资源超卖方案是针对 Node 级别的资源动态调整方案,根据每个节点的真实历史负载数据,进行不同比例的资源超卖。

每个节点的资源超卖比例,我们设置到 Node 的 Annotation 中,比如 cpu 超卖对应 Annotation stke.platform/cpu-oversale-ratio。

每个节点的超卖比例,谁去设置?自研组件(Node-Resource-Oversale-Ratio-Reconciler)基于节点历史监控数据,动态的 / 周期性的去调整超卖比例。比如某个 Node 连续 7d/1M 持续低负载并且节点已分配资源水位线很高了,那么可以把超卖比例适当调高,以此使 Node 能容纳更多的业务 Pod。

Node 超卖特性一定要是可以关闭和还原的,通过 Node Annotation stke.platform/mutate: "false" 关闭 Node 超卖,Node 在下一个心跳会完成资源复原。

何时通过压缩比去调整 Node Status 中的 Allocatable&Capacity Resource?同样的,我们通过 kube-apiserver 的 Mutating Admission Webhook 对 Node 的 Create 和 Status Update 事件进行拦截,自研 webhook(node-resource-oversale-webhook) 检查 Node Annotations 中是否 enable 了超卖并且配置了超卖比,如果配置了,则根据安超卖比重新计算该 Node 的 Allocatable&Capacity Resource,Patch 到 APIServer。

Node 资源超卖,表面上看起来很简单,但实际上要考虑的细节还很多:

Kubelet Register Node To ApiServer 的详细原理是什么,通过 webhook 直接 Patch Node Status 是否可行?

当节点资源超卖后,Kubernetes 对应的 Cgroup 动态调整机制是否能继续正常工作?

Node status 更新太频繁,每次 status update 都会触发 webhook,大规模集群容易对 apiserver 造成性能问题,怎么解决?

节点资源超卖对 Kubelet Eviction 的配置是否也有超配效果,还是仍然按照实际 Node 配置和负载进行 evict? 如果对 Evict 有影响,又该如解决?

超卖比例从大往小调低时,存在节点上 Sum(pods’ request resource) > node’s allocatable 情况出现,这里是否有风险,该如何处理?

监控系统对 Node 的监控与 Node Allocatable&Capacity Resource 有关,超卖后,意味着监控系统对 Node 的监控不再正确,需要做一定程度的修正,如何让监控系统也能动态的感知超卖比例进行数据和视图的修正?

Node Allocatable 和 Capacity 分别该如何超卖?超卖对节点预留资源的影响是如何的?

这里涉及的 Kubernetes 技术细节比较多,我将在下一篇文章中详细介绍。

腾讯自研业务上云:优化Kubernetes集群负载的技术方案探讨

优化 AutoScale 能力

提起 Kubernetes 的弹性伸缩,大家比较熟悉的是 HPA 和 HNA,一个对 Workload 的 Pod 进行横向伸缩,一个对集群中 Node 进行横向伸缩。社区中还有一个 VPA 项目,用来对 Pod 的资源进行调整,但是需要重建 Pod 才能生效,VPA 存在的意义就是要快速扩容,如果像 HPA 一样,需要重建 Pod 启动应用来扩容,其实已经失去了价值。

Kube-controller-manager 内置的 HPA-Controller 存在以下问题:

性能问题:一个 goroutine 中循环遍历集群中所有的 HPA 对象,针对每个 HPA 对象获取对应的 Pod 监控数据、计算新 Replicas,这对于大业务是比较耗时的。

核心配置不支持 Workload 自定义:HPA 伸缩响应时间是每个业务都可能不一样的,有些业务期望能 5s 进行响应,有些业务觉得 60s 就够了。而内置 HPA Controller 在响应时间控制上只能配置全局的启动参数 horizontal-pod-autoscaler-sync-period。还有每个业务对负载的抖动容忍是不一样的,在内置的 HPA Controller 中只能通过 horizontal-pod-autoscaler-tolerance 做全局配置,无法提供业务级的自定义。

Kubernetes 目前对 custom metrics 的支持,只能注册一个后端监控服务,如果集群中有些业务通过 prometheus 来 expose 应用自定义指标,也有一些业务通过 Monitor 来监控应用自定义指标,这个时候就做不到 All in 了,这在 for 自研上云的场景中,是一定存在的场景。

我们自研了 HPAPlus-Controller 组件:

每个 HPA 对象会启动一个 goroutine 协程专门负责该 HPA 对象的管理和计算工作,各个协程并行执行,极大的优化了性能。HPAPlus-Controller 独立部署,其资源需求可以是集群规模和 HPA 数量进行合理调整,相比于原内置 HPA-Controller 有更大的灵活性。

HPAPlus-Controller 支持各个 HPA 对象自定义伸缩响应时间,支持自动感应业务是否在变更发布并决定是否要禁用 HPA(某些业务有这样的需求:升级时禁止触发弹性伸缩),支持基于 pod resource limit 为基数进行 Pod 资源利用率计算,从而推导出扩缩容后的期望 replicas,这一点对于节点超卖和 Pod 资源压缩后的集群非常重要。

支持业务级别对负载的抖动容忍度的个性化配置。

支持基于更多维度的监控数据进行 Scale 决策,比如 Pod 历史 7d/1M 的 cpu 负载。

支持 CronHPA,满足规律性扩缩容的业务诉求。

通过 Extension APIServer 的方式对接公司 Monitor 监控,保留 Prometheus-Adaptor 的方式来支持基于 Prometheus 的应用监控,满足基于多种应用监控系统的 custom metrics 进行 HPA。

注意:HPAPlus-Controller 与 Kubernetes buit-in HPA-Controller 存在功能冲突,上线前需要 disable kube-controller-manager 的 HPA-Controller 控制器。

腾讯自研业务上云:优化Kubernetes集群负载的技术方案探讨

除了 HPA 的优化和增强之外,我们也在进行 Dynamic VPA 技术研发,后续再单独文章介绍。

其他技术方案

另外,通过 scheduler extender 的方式开发动态调度器、基于业务级的配额动态管理组件、基于业务优先级和配额管理的在线离线业务混部能力、主动探测节点资源碎片信息并上报到控制器进行 Pod 的再漂移进行资源碎片管理等方案,也是我们正在进行实践的方向,对应方案及实现复杂度更高,后续再单独文章介绍。

总结

Kubernetes 静态调度带来的集群资源分配水位线高但集群实际负载低的问题进行了技术方案上的探讨,详细介绍了 Pod 资源动态压缩、节点资源动态超卖、优化 AutoScale 的能力的技术方案,后面会再对动态调度、动态业务配额管理、在线离线业务混部方案进行介绍。所有这些集群负载提升方案,要做到动态,都强依赖于强大的容器监控系统。我们正与腾讯云监控产品团队深入合作,更好的服务于腾讯自研业务上云。

本文转载自公众号云加社区(ID:QcloudCommunity)。

原文链接:

https://mp.weixin.qq.com/s/FK9cYbGvzCLUsO7q63TrCA

评论

发布