成本最高降低70%,腾讯大规模业务集群的云原生成本优化实践!

2022-11-28 16:11:25 浏览数 (1)

唐聪,腾讯云容器技术专家,极客时间专栏《etcd实战课》作者,开源项目kstone和crane内部雏形版 founder,etcd活跃贡献者,主要负责腾讯云大规模k8s和etcd平台稳定性和性能优化、业务集群成本优化、有状态服务容器化等产品研发设计工作。

背景

2021年下半年以来,在新冠疫情和互联网政策的冲击之下,各大互联网公司都在进行降本增效。降本增效的一大核心手段就是优化计算资源成本,本文将以腾讯某内部 Kubernetes/TKE 业务为案例,详细阐述如何从 0到1(成本数据采集与分析、优化措施、行业现状与方案选型、方案设计与实现、落地与效果、总结)进行大规模、高可靠、高效率的成本优化的实践,并在这过程中实现了零故障突发,CPU 最高节省70%,Memory 节省50%的成果。本文所介绍的成本优化整体方案实现是腾讯云开源项目 Crane 的内部雏形版,我们在内部成功实践的基础上,将相关设计方案与最佳实践进一步输出给对外开源项目 Crane(https://github.com/gocrane/crane),致力于为云原生广大用户提供云成本优化一站式解决方案。

业务现状

(一)数据采集与分析

本文提及的若干业务全部容器化部署在 TKE 集群中,在经历了两三年的用户大规模增长后,扩容了大量节点,账单高峰期费用每月千万级,为了搞清楚高昂成本背后的缘由,以及正确的选择收益大的优化方向,我们需要基于一系列数据做科学的决策,因此成本优化的第一步,就是进行成本数据的采集与分析,如下图所示:

上图我罗列了成本采集与分析的核心几个维度:

1.成本账单,搞清楚各产品、各模块每月总成本、趋势,找准成本大头所属业务和云资源,同时了解每月的新增费用主要源自什么云计算产品,在对存量资源进行成本优化时,同时也需要尽量堵住新增的。

2.当前成本大头云资源的明细数据分析,这里我以 CVM 节点为例,核心数据如下:

CVM 节点总规模,各地域各集群的 CPU/Memory/Extended Resource 资源总量

节点 CPU/Memory/Extended Resource 资源使用率,也就是节点实际负载

节点 CPU/Memory/Extended Resource 的资源分配率,kubernetes Node 中的Request 分配率,kubernetes 调度器是基于 Pod的Request 来调度的,只有当节点剩余足够的 Request 资源时,才能将 Pod 调度到节点上运行

3.业务 Pod 组件负载

业务 Pod 的 CPU/Memory 分配资源总量,可以直观了解到当前集群占用资源大头的组件名

业务 Pod 的 CPU/Memory 等各资源实际使用资源总量,与业务 Pod 分配的资源相比,可获得资源分配的实际有效率

业务 Pod 异常状态统计,如 OOM 次数

4.HPA 有效性数据分析

覆盖度

最小最大副本是否合理

是否有触发过 HPA 等

5.业务分析

负载特点,是否具备周期性特点,这个决定着资源预测算法等

服务类型,无状态服务类型,一般由延时敏感型和异步服务组成,有状态服务类型一般由 Master/Slave 类型如单机主备版 Redis/MySQL、中心化分布式集群如 Codis、去中心分布式集群如 Redis Cluster 组成,不一样的服务类型决定着上层不同的扩缩容策略、更新 Pod 策略

Workload 组成,像 kubernetes 常见的 Deployment、StatefulSets、自研Operator,不一样的 Workload 对应上层的更新策略也是不一致的

通过以上核心数据的采集与分析,此内部 Kubernetes 平台基本信息如下:

  • 核心成本是 CVM 资源,占据了80%的成本,核心资源被三大头部业务所使用
  • CVM 节点 CPU 负载平均峰值5%左右,高峰15%,集群节点资源分配率55%左右,节点之间负载不均衡
  • 业务 Pod Request 与实际使用值差异较大,但少部分 Pod 还出现过 OOM,各个业务 Pod OOM 无扩容机制,根据此数据分析,我们可以通过 VPA 技术可以节省大量资源
  • HPA 覆盖率不高,最小和最大副本设置不合理
  • 业务负载类型主要由 Deployment、自研 Operator 组成
  • 业务模块既含无状态服务,又含有状态服务,无状态服务中有对延时敏感的API网关服务、又有非敏感性的异步后台服务
  • 业务 Pod、容器数百万级缺少
  • 业务维度的 HPA 机制

优化措施

通过对一系列核心成本数据进行采集和分析后,我们针对现状,从业务 Pod 资源使用率提升、节点分配率提升、节点负载提升、计费优化四个方面梳理了如下优化措施:

1.业务 Pod 资源使用率提升

引入 VPA 垂直扩缩容组件,基于用户实际使用资源的画像来进行扩缩容,覆盖所有组件,一方面可以解决业务 Pod Request 与实际使用值差异较大的问题 (成本浪费大头),另一方面也可以解决少量 Pod OOM 后无自动扩容的不可用问题

HPA 覆盖所有业务组件,优化最小最大副本数,推荐合理的初始副本

针对周期性、活动性特点业务,使用 CronHPA 组件

2.节点分配率提升

基于业务实际负载模型选择最佳机型,而不是人工经验、直觉,从成本数据分析中,我们发现部分节点 CPU 资源未分配完,但 Memory 已基本分配。从实际业务负载数据看,业务 CPU 与 Memory 比例应是1:4,而不是线上大规模使用的 CPU 与 Memory 1:2比例的机型。

将调度策略从默认的 LeastRequestedPriority 优化成 MostRequestedPriority,优先将 Pod 调度到分配资源比较多的节点(剩余资源较少的节点),提升单节点 Pod 的密度

修改 Kubelet Pod CIDR,提升单节点能支撑的 Pod 数

在提升分配率的同时,我们还需要一系列模块保障业务稳定性。动态调度器,可以在调度的时候,将高负载节点过滤掉。Descheduler, 可以在节点运行过程中,对异常高负载的节点进行业务驱逐

3.节点负载提升

通过 Admission Webhook 拦截 Node的Patch 资源请求,基于节点实际负载等,将 Node 已分配的资源调低,此方案对 Pod QoS 策略没有强制的要求,但是细节若处理不好,会产生非常严重的故障,节点重启时,可能会导致大规模的Pod驱逐。

通过扩展资源封装节点可超卖的 CPU 资源,新建的 Pod 声明的 CPU Request/Limit 改成对应的扩展资源,Pod Qos 策略必须为 Best Effort。

基于业务资源画像缩容,通过此技术业务 Pod Request 与实际使用的真实值更加接近,极大降低浪费空间

非敏感核心业务的 Pod QoS 策略可调整为 Burstable, Limit 资源适当大于 Request, 对 CPU/Memory 资源进行超额使用(overcommit)。如扩容的一个触发因子是 CPU 利用率,如果扩容是基于 Request 计算使用率,当使用率大于125%时阈值时再触发扩容。

节点资源静态和动态超卖,节点可超卖资源 = 节点总资源 - 节点当前已经使用的 - 节点预留资源,一般实现方案主要有两种:

在离线混布,是节点资源超卖的一种特殊形式,一般在线业务具有明显的潮汐效应,通过在离线混布技术(如腾讯开源的 Caelus)实现错峰部署离线任务提升节点 CPU 使用率

4.计费优化

各大云厂商云服务节点都提供三种计费模式,价格分别是竞价实例(可能随时会被云厂商释放) < 包年包月 < 按量付费,可根据业务类型、场景选择合适的计费模式,达到成本节省的目的。

根据自身业务需求,各机型优缺点,选择最具性价比的机型。

行业现状与方案选型

从我们成本数据分析中我们得到的结论核心优化大杀器是 VPA 和 HPA。那么业界当前有哪些VPA、HPA方案呢?是否能满足我们期望的目标呢?

(一)VPA

VPA 是社区开源的垂直扩缩容开源项目,它的架构图如下,主要由如下几个组件组成:

1.Metrics Server,提供 Pod/Node 的实时 CPU/Memory 负载数据查询服务

2.History Storage,默认为 Prometheus, 提供 Pod/Node 的 CPU/Memory 的历史负载数据查询服务

3.VPA Controller,由 Recommender 和 Updater 组件组成,Recommender 组件负责生成对应 Workload 的画像数据,Updater 组件负责监听 Pod,并通过驱逐 Pod 的方式来更新 Pod 的资源。

4.VPA Admission Controller,负责拦截 Pod 的创建请求,并使用 Recommender 推荐的资源来覆盖 Pod 中的容器资源。

VPA 有如下的局限性或待优化点:

1.大规模集群下存在性能和稳定性问题

2.不支持基于更多业务指标等设置扩缩容条件,控制扩缩容时机等

3.需要为每个 Workload 创建对应的 VerticalPodAutoscaler 资源

4.通过 Evict 的方式触发 Pod 资源更新

5.不支持多种画像算法,内置的指数级衰减直方图算法当 CPU 瞬间高负载的时候,响应较慢

6.可观测性等能力较弱

......

(二)HPA

HPA 是 Kubernetes 项目内置的水平扩缩容开源项目,它会基于 HPA 资源中业务声明的各种 Metrics 指标和扩缩容条件,周期性计算副本数,判定是否需要扩缩容,它的架构图如下:

1.扩容数据源 Metrics API:

metrics.Kubernetes.io[1], 数据源一般由 metrics-server 提供,提供了基本的 CPU、Memory 指标

custom.metrics.Kubernetes.io[2], 开源的数据源实现如 prometheus-adapter ,提供了集群对象资源相关的指标,如 Pod 的 HTTP QPS 和延时。

external.metrics.Kubernetes.io[3], 监控指标的来源跟 Kubernetes 本身无关, metrics 的数据完全取自外部的系统,如消息队列的任务数。

2.HPA API

autoscaling/v1, 指定最小最大副本数,关联的 Deployment 服务, v1 只支持基于 CPU 利用率(targetCPUUtilizationPercentage)一个指标

autoscaling/v2,支持多种数据源指标,并且支持自定义多种指标,缩容和扩容的详细规则。

扩容算法

desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]

比如当前 QPS Metrics 指标值为1000,期望的 QPS Metrics 指标值为 500,那么副本数将翻倍。

下面是配置多个扩容指标的 yaml 例子,HPA 将依次计算多个指标下的期望副本数,然后选择具有最高副本的指标进行扩容。

代码语言:javascript复制
APIVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata:  name: php-apachespec:  scaleTargetRef:    apiVersion: apps/v1    kind: Deployment    name: php-apache  minReplicas: 1  maxReplicas: 10  metrics:    - type: Resource      resource:        name: CPU        target:          type: Utilization          averageUtilization: 50    - type: Pods      Pods:        metric:          name: packets-per-second        target:          type: AverageValue          averageValue: 1k    - type: Object      object:        metric:          name: requests-per-second        describedObject:          apiVersion: networking.Kubernetes.io/v1          kind: Ingress          name: main-route        target:          type: Value          value: 10k

HPA 也存在一定的缺陷,如弹性不够及时,是对监控数据的被动响应,这使得 HPA 天生具有滞后性,进而可能影响业务稳定。另外,它的可观测性待提高,不支持 Dryrun 测试,一旦使用便会实际修改应用的实例数量,存在一定风险等。

(三)Google Autopilot

介绍完 kubernetes 生态社区的 VPA 和 HPA 组件原理后,我们再来看看他们的鼻祖 Google Autopilot。Google  2020年发表了一篇论文《Autopilot: Workload autoscaling at Google》,其中这样介绍 Autopilot:

Google uses Autopilot to configure resources automatically, adjusting both the number of concurrent tasks in a job (horizontal scaling) and the CPU/memory limits for individual tasks (vertical scaling).

同时在 Google 2020年发表的另外一篇论文《Borg: the Next Generation》提到 Autopilot 提供的 VPA 机制是非常有效的。

Our findings show that Borg features such as alloc sets are used for resource-heavy workloads; automatic vertical scaling is effective;

从中我们可以看到 Google Autopilot 是集 VPA 与 HPA 与一体的弹性伸缩组件,主要目标是减少申请资源和实际资源使用之间的差异,同时最大程度地降低因 Memory 不足(OOM)错误、CPU 高负载导致的其性能和可用性下降。

Autopilot 它的架构图如下(引用自 Google Autopilot 论文),它由如下组件组成:

1.Recommender 推荐组件,负责 Request/Limit 推荐,包含多种算法,如滑动窗口、机器学习、定制化的算法等。

2.Autopilot 服务,从 Recommender 组件读取推荐的 Request/Limit 信息,通过 Borgmaster API 更新任务资源。

3.Borgmaster 类似kubernetes master 三大组件。

4.Borglet 类似 Kubelet

Google Autopilot 的论文和相关实践数据给我们带来了一定的启发,尤其是其业务画像推荐组件的设计,在实际场景中,我们会面临各种各样的业务场景,需要结合各自业务特点使用不同的负载预测算法等。

方案设计与实现

在介绍了当前行业的一些解决方案后,那我们业务场景需要一个怎样的弹性伸缩系统呢?

这里我从扩展性、可观测性、稳定性方面列就了一些期望的目标。

(一)设计目标

  • 扩展性

支持多业务,每个 namespace 就是一个业务模块,期望弹性伸缩组件能管控任意业务组件,实现上抽象出 ComponentProvider 接口,各业务可扩展精细化设置每个组件扩缩容规则(如特殊处理有状态服务组件等)。系统内置提供自动纳管 Namespace 下任意 Pod/Container 级别的组件 Provider。

支持多种画像数据算法(Portrait Algorithm),从 Moving Window 到机器学习算法,再到自定义的算法等。

支持多种扩缩容触发器(Scaler Provider),从常见的 CPU/Memory 利用率、OOM 事件,再到业务指标维度的 QPS、延时、队列堆积长度等

支持多种更新策略(Updater Provider),如 Evict Pod Admission Webhook 修改Request/limit、Deployment 的滚动更新、原地更新、先扩容再指定 Pod 缩容、修改Operator 触发对应的 Workload 更新等。

支持多种扩缩容记录存储策略(Record Provider),如存储到 db、日志文件、Event 输出。

支持多样化的扩缩容规则,如精细化到每个容器级别的 VPA 扩容规则、缩容规则、 HPA 扩缩容规则等,以及全局的扩缩容 Dryrun 开关。

  • 可观测性

扩缩容效果,可预测,比如通过 Dryrun 模式提前预测缩容能节省的资源明细、业务画像的 CPU:Memory 比例、最佳机型等。

核心的 VPA 扩缩容次数、HPA 扩缩容此时、延时等视图。

扩缩容队列长度。

组件 OOM 次数。

当前处于扩缩容过程中的组件数。

各个业务平均使用的 Request 资源、缩容后 CPU/Memory 资源利用率等。

  • 稳定性和高效

整体发布策略是先 Dryrun 模式运行,然后灰度,再通过自适应限速、巡检等实现大规模缩容,同时业务 Workload 遵循缓慢缩容,快速扩容策略,如有异常,可及时自愈。

组件部署后,自动为集群中所有业务组件生成画像资源 CR,生成描述整个业务模块 Namespace 的扩缩容策略资源CR,精细化到组件容器级别的 VPA 和 HPA、EHPA 扩缩容策略,无需运维人工做任何操作。

支持空运行提前获取成本优化后的预测数据,各个组件扩缩容行为预测数据,并可视化的展示。

支持多种灰度算法,如按业务重要性、规模、Workload 类型、客户敏感度。

支持分批、自适应限速,如在缩容过程中,若大量业务满足缩容规则,则会进行自适应限速,当前处于缩容过程中的组件数小于某个阈值才能继续进行其他组件缩容。

缩容过程中,会通过亲和策略将 Pod 调度到最佳目的机型节点,老节点一般情况下只会剩下少量 Pod。

安全的节点下线,通过滚动更新、Evict 方式下线老节点上的 Pod,若业务 Pod 存在单点,则会先扩容成多副本再进行 Evict (或通过滚动更新的方式安全销毁旧 Pod), 避免影响服务可用性。

(二)方案实现

基于以上设计目标和对行业各方案的选型,解决社区版 VPA 和 HPA 的若干不足之处,我们设计实现了如下架构,整体架构是画像模块和 KMetis 模块(负责弹性伸缩与节点下线及自愈)组成。

(三)画像模块

画像模块架构图如下所示:

其主要由以下组件组成:

1.Workload-Controller 通过监听集群下的 Deployment/Statefulset/Job/CronJob Workload,为它们自动生成对应的画像Portrait资源, 具备高效、自动化的特点。

2.Workload-Recommender 核心由两部分组成,数据源 data-source 模块和 algorithm 算法模块组成。数据源模块包含实时数据模块 metrics-server, OOM 事件,历史数据源promethues、es、以及云厂商对应的监控模块等。算法模块如 VPA 的指数级衰退直方图算法、基于机器学习算法预测未来负载曲线的算法 XGBoost 等、以及自定义的高实时响应级的SMA算法等。Workload-Recommender 模块会周期性将预测的画像数据更新到画像 Portrait 资源中。

3.业务可根据不同特点的 Workload,选择对应的算法,如 API 网关对延时非常敏感,期望极速的扩容,数据源则可以使用 metrics-server,链路短,实时响应性高,算法使用自定义的 SMA 算法,取最近几分钟的平均负载,则负载上升时响应更快,具有更高的稳定性。

(四)KMetis 模块

KMetis 模块架构图如下:

1.KMetis 是一个集 VPA HPA/EHPA 节点下线与一体的弹性扩缩容与节点自愈服务。

2.核心API资源是 CSetScaler(ComponentSetScaler)和 NodeScaler API。

CSetScaler 它描述了管理的业务服务列表、VPA 和 HPA、EHPA 的详细策略信息(扩缩容开关、触发条件、稳定时间)。

NodeScaler 它描述了节点扩缩容任务信息,主要用于节点下线,主要使用场景是成本优化和节点故障自愈。首先是成本优化,当通过画像缩容了大量的 Workload 后,集群中存在大量低负载节点,我们可以下发策略,将这些节点上的少量 Workload 安全“驱逐”,然后下线节点。其次是节点npd检测到某节点存在异常后,我们也可以通过NodeScaler 任务进行自愈。

3.KMetis 核心流程如下:

KMetis 组件部署到 Kubernetes 集群后,默认使用的 ComponentProvider 为NamespaceAllContainer,它会为每个 Namespace 下的生成 CSetScaler 资源,它包含了这个 Namespace 下面的所有容器,如果 Workload annotation 声明了对应的扩缩容策略(如只扩不缩),则依据声明设置规则,如果没有则采用默认规格(比如扩容稳定窗口为180秒、缩容随机为12-24h、扩容 CPU/Memory 阈值为90、缩容为30%、HPA最小最大副本数等)。

KMetis 会周期性检查各个 Namespace 下的 Workload 负载和副本数是否符合CSetScaler 资源所期望的,若不一致,则进行一致性协调操作,主要包括 VPA 和 HPA 操作。

优先进行 VPA 协调操作,扩缩容核心流程由 ScalerProvider、ResourceEstimator、UpdaterProvdier、RecordProvider 组成。

ScalerProvider 定义了一系列触发扩缩容的条件,如 Overload,CPU/Memory 使用率超过阈值,OOM,则是 Pod 发生了 OOM 事件,Custom 则是业务基于自定义的指标,如 gRPC 服务的 P99 延时等。

ResourceEstimator 用于评估扩容、缩容后的最佳资源,缩容主要基于业务画像,扩容在业务画像、OOM 事件的基础上,结合各个业务自定义的如 gRPC QPS 指标实现的 ResourceEstimator 进行判定,依次遍历多个 ResourceStimator, 取最大值作为扩容后的最佳资源。

在通过 ScalerProvider 判定可以缩容/扩容后,基于 ResourceEstimator 获取最佳目标资源后,下一步则是基于 UpdaterProvider 进行资源更新,如常见的 Evict、RollingUpdate、In-Place Update, 根据服务的敏感度采取不一样的策略。最后通过 EventProvider 接口,将扩缩容记录保存到日志等存储中。

在 VPA 协调操作完成后,则会尝试进行 HPA 协调操作。HPA 基于 ReplicasEstimator 预测当前副本数,一方面它会确保当前 Pod 副本数满足业务期望最小副本数,另一方面它会优先基于业务指标判定是否需要执行 HPA 操作,避免与 VPA 发生冲突。若 VPA 扩容的最大资源已达到期望的最大规格,当 CPU/Memory 负载满足 HPA 扩容规则时,也会进行 HPA 操作。

当组件之前存在依赖关系时,同时高负载的时候,还需要通过根因分析 RootCause 模块,判断导致高负载的核心瓶颈模块,优先对其进行扩容。

各业务也可通过配置指定自定义的水平扩展组件,如 Crane的EHPA,它通过 Dsp 算法,可以实现提前扩容,预测未来。

方案落地与效果

基于以上方案选型、设计目标与实现后,我们实现了自己期望的 VPA/HPA/EHPA 混合弹性伸缩套件 KMetis,那么如何实现将 KMetis 和优化建议中提到的其他措施,在大规模 Kubernetes 集群中高效、稳定落地呢?

(一)高效、可控发布

为了确保成本优化方案能在大规模 Kubernetes/TKE 集群中平稳、快速落地,我们在方案设计时其实已经明确了落地四部曲:

1.首先空运行获取预测数据,检验系统的稳定性。

2.然后通过灰度,提前发现扩缩容过程中的隐患点。

3.其次通过按比例、自适应限速实现大规模扩缩容。

4.最后通过安全的节点下线流程实现成本的优化

空运行/预测数据

在系统上线初期,我们会设置整个 KMetis 系统基于空运行模式运行,也就是不实际更新 Workload 资源、只存储扩缩容记录。通过空运行模式,一方面我们可以验证整个系统核心流程的正确性、性能、稳定性,提前发现潜在的 Bug,另一方面,我们可以直观的获取优化效果,基于业务画像数据选择合理机型,而不是靠经验、或者随意使用机型。在我们的场景中,业务 CPU 与 Memory 比例为1:4,某核心业务机型从优化前的8c16g优化成了4c16g。

下图是其中一个小业务集群的在空运行发布后,获取得成本节省视图:

某个小业务组件级 CPU 节省视图:

灰度与自适应限速

在经历了空运行阶段后,我们解决了 KMetis 在大规模集群下的准确性和性能(提升 client-go qps、随机化扩缩容稳定窗口时间、读全部通过 Informer 的Cache 机制等)问题后,接下来当然就是关闭空运行模式,开始为业务降本了。

在这一阶段,我们基于空运行阶段,所获取的数据,按集群规模、业务重要度、业务规模、业务地域进行了一定规模的灰度。同时,因不少业务之前长期没关注成本和负载问题,大量业务模块会触发缩容操作,为了控制缩容的并发度和潜在的风险,我们实现了按比例和自适应限速能力,可实现无人值守的安全变更。

什么是自适应限速呢?

若我们允许最大并行中的扩缩容服务为20,KMetis 会周期性检查当前集群有多少个组件处于更新中(服务 Pod Pending、Crash、OOM 等异常),若更新中的组件数大于20个,则对常规的扩缩容操作进行熔断。

为什么需要自适应限速呢?一方面大规模 Kubernetes 集群中,大量Pod含亲和性规则会影响 Kubernetes 调度器性能,扩缩容过快会导致整个 Kubernetes 集群调度模块不可用,性能急剧恶化。另一方面,个别业务 Pod 缩容更新后,当前基于画像分配的负载不一定能够完全扛得住,因为这些业务在重启时可能会触发大量的 client 查询操作,尤其是基于 List-Watch 模型的类 etcd 业务场景。针对极小部分这类业务,我们需要能及时发现异常并进行熔断和告警,并在画像的基础上增加一定的安全阈值,以确保缩容操作的安全性。

在整个变更过程中,KMetis 对外暴露了丰富的 metrics,如下图所示,总缩容次数、缩容组件名、总扩容次数、扩容组件名、队列长度、组件 OOM 次数、扩缩容延时、处于更新中的总组件数等。

节点安全下线

通过大规模灰度进一步验证系统稳定性和准确性后,通过按比例、自适应限速,我们对线上大规模集群实施了几十万次的缩容操作,那么在这过程中,如何将之前的不合适的老机型替换掉呢,并尽量提高节点的资源分配率呢?

答案是定制调度策略。

针对老节点下线问题,我们根据业务模块画像对应的资源,当老节点上的 Pod 触发缩容的时候,会为其打上不同规格的亲和性标签。

代码语言:javascript复制
affinity:nodeAffinity:preferredDuringSchedulingIgnoredDuringExecution:- preference:matchExpressions:- key: levelOperator: Invalues:- small- key: x.y.z/componentOperator: Invalues:- normalweight: 10
代码语言:javascript复制

为了提高节点的资源分配率,我们将调度策略从默认的 LeastRequestedPriority 优化成 MostRequestedPriority,确保 Pod 优先调度到资源分配比较多的节点上,提到节点的分配率。

为了解决节点之间负载不均衡的问题,我们还引入了动态调度器和 Descheduler。动态调度器可以帮助我们避免新 Pod 调度到较高负载的节点,而 Descheduler 则可以协助我们将高负载节点 Workload 打散,下线低负载节点上的 Pod 等。

通过一系列调度策略的定制和优化,老节点的90%的 Pod 已经通过缩容操作更新到了新节点上,那么这些老节点上的 Pod 如何安全“驱逐”掉呢?节点如何安全下线呢?

这就是 KMetis NodeScaler 所做的事情,它通过提交一个 Scaler Task,声明需要下线的节点列表或者 SourceNodeProvider(如低分配率节点、包年包月即将到期节点等 Provider),进行自动化的 Pod 安全驱逐,它的核心流程如下:

1.获取待下线的源 node 列表,若某 node 上有禁止任何变更的业务模块,则过滤掉此节点

2.设置为禁止调度

3.获取受影响的业务模块

4.关闭业务模块的缩容副本功能

5.若采用 Evict 方式驱逐,若是单副本则扩容至多副本(通过 Rolling Update 方式更新的方式销毁旧 Pod 则无需扩容)

6.检查扩容后的组件是否 Ready

7.给节点打上可安全驱逐的标记

8.通过 Descheduler 或人工 kubectl Drain 方式驱逐节点上的 Pod

9.检查驱逐是否完成,完成后打开缩容副本开关

(二)效果

通过以上一整套的高效、可控、可观测、安全的发布变更,我们零故障的实现了以上成本优化方案的在大规模 Kubernetes/TKE 集群中的落地,并取得了显著的优化效果。在此业务 Kubernetes 平台中,核心的三大业务,因各自具有不同的特点,优化效果也略有差异。A 业务节省了节省70% CPU 核心, B 业务节省了45% CPU 核心,C 业务节省了50%CPU核心,整体上大幅降低业务 IT 成本,并提升了各个模块在异常情况下的可用性。

节点分配率上,从之前的50%左右提升到了下图中的最高 CPU 99%,Memory 88%。

CPU 利用率上,从之前的平均5%提升到21.4%。

总结

(一)稳定性

成本优化的难点不是将节点分配率和资源利用率提上去,而是在高分配和资源利用率的前提下,同时保障好业务稳定性。

在整个成本优化过程中,我们面临最大的挑战是在节点 Pod 密度上升后,个别负载较高的节点遭遇了节点内核 Bug、Docker、Kubelet Bug,这些挑战部分是我们始料未及的,并曾在一定程度上影响了我们优化进度和效果。

为了解决这些挑战,一方面,通过部署TKE集群的自带的 NodeProblemDetectorPlus 组件,及时发现各类节点底层问题。比如 NodeProblemDetectorPlus 若发现节点出现 Docker Hang 等 Bug 后,会给对应节点打上 KernelDeadLock Condition,KMetis 组件监听到对应的异常 Condition 后,会给节点打上禁止调度,并尝试驱逐上面的业务 Pod, 实现故障自愈。另一方面,TKE 团队通过一个个案例的深入分析,确认社区 Bug 原因和制定修复方案,随后通过分批升级节点内核、节点运行时组件等版实现彻底根治。

另外一个大的稳定性问题则是通过滚动更新的方式,“安全”驱逐业务 Pod 时,发现一些业务有受一定程度的影响,核心原因是这些业务 Pod 未遵循 Kubernetes 最佳实践和 LB 解绑 RS 存在延时,导致未能实现优雅销毁 Pod。

Kubelet 销毁 Pod 流程和业务最佳实践如下:

1.Pod 进入 Terminating 状态后,Pod 从对应的 Service 和 LB 上删除,确保新请求不会转发到销毁的 Pod 上,在这过程中,可能会因控制面负载、iptables 规则数量、LB 组件性能等出现解绑 RS 延时。

2.为了解决解绑 RS 延时等问题,业务最好配置 preStop 脚本,在 Kubelet 发送 SIGTERM 命令前先 Sleep 30s。

3.Kubelet 会优先调用业务自定义的 preStop 脚本,再向 Pod 的 Pid 1号进程发送 SIGTERM信号,实现优雅关闭进程。

4.业务需确保 Pid 1号进程能接受处理 SIGTERM 信号,而不是搞个 Shell 进程拉起或者循环检查业务进程,异常就拉起之类的非标准用法。

5.当 terminationGracePeriodSeconds 时间到达后(默认30s),若容器未正常退出, Kubelet 会发送 SIGKILL 强制杀掉尚未退出的容器。

(二)未来方向

当前 CPU 利用率依然有优化空间,下一步我们将结合节点资源超卖来实现 CPU 使用率进一步提升。

本文系统的阐述了基于 Kubernetes 平台的云原生成本优化方法论,详细介绍了我们如何从0到1的实践之路,相信其中的数据分析、方案设计思想、落地与最佳实践、稳定性问题反思能给你带来一定的启发。想更深入了解成本优化,可以关注下腾讯开源的 Caelus 和 Crane 项目。

参考资料

[1]metrics.Kubernetes.io: 【http://metrics.kubernetes.io/】 [2]custom.metrics.Kubernetes.io: 【http://custom.metrics.kubernetes.io/】 [3]external.metrics.Kubernetes.io:【http://external.metrics.kubernetes.io/】

点击下方空白 ▼ 查看明日开发者黄历

summer

time

2022

/

07.23

0 人点赞