查看原文
其他

Shopee 数据中心绿色计算实践(上)

基础架构团队 Shopee技术团队 2023-04-18

点击关注公众号👆,探索更多Shopee技术实践

碳排放以及由此导致的全球气候变暖是当前全人类共同面临的重大挑战,截至 2021 年,全球已经有 54 个国家和地区实现了“碳达峰”,越来越多政府、企业、组织为了低碳经济采取了相关行动。对于互联网公司来说,数据中心往往是碳排放的大头,建设更加绿色环保的数据中心是行业共识,业界在数据中心的绿色计算领域也有非常多的实践案例。

从数据中心选型、电路高压直流、模块化机房、行间空调、整机柜液冷、服务器智能调温,再到业务侧在离线混合部署、基于服务画像超售和 VPA(Vertical Pod Autoscaler,垂直弹性伸缩),Shopee 一直在学习和探索业界前沿经验,并基于自身业务特点不断实践和演进绿色计算。

本文将分享 Shopee 的绿色计算实践,详细介绍在 2021 年 Shopee 的工程师如何通过各种技术创新,保障在线服务平稳运行的情况下,合理优化资源分配,提高资源利用率,减少电力消耗,从而降低数据中心的碳排放。

目录

背景
1. Overcommit(超售)
2. Co-location(混部)
3. Auto Scaling(弹性伸缩) 
总结

背景

在 Shopee 业务侧,90% 的在线服务都运行在 Kubernetes 中,这使得我们可以充分借助各种云原生技术,在数据中心合理调配资源,提升资源利用率。

当前在 Shopee,我们主要使用了 Overcommit(超售)、Colocation(混部)、AutoScaling(弹性伸缩)这三种技术来实现业务侧资源利用率的提升。下文将分别详细展开描述 Shopee 在 Kubernetes 中应用这三种技术的相关实践。

1. Overcommit(超售)

Shopee 当前的用户分布横跨多个时区,每个时区都有各自的流量峰值,我们可以充分利用这一业务特点对 Shopee 的在线服务按照资源画像进行合理超售,又不影响在线服务的 SLO。但在 Kubernetes 中怎么具体实现这个策略呢?

一个非常直观的方式是按比例降低容器申请的资源,并安排更多的负载到各个集群,这也是我们采用的方案。这里我们引入了一个参数 Overcommit Factor,容器的 Request 会根据 Overcommit Factor 得到一个更小的值,而 Limit 不会变化。由于 Kubernetes 调度器在计算资源用量时只会考虑容器的 Request,通过这个参数,能用一个比较理想化的方式在机器上调度更多的容器。同时,由于 Limit 没有变化,容器的资源使用仍然可以达到相同的上限。不同业务的容器会设置客制化的 Overcommit Factor,以防止过度超售。

但是通过这种方式随意调度更多的容器到机器上会出现很多问题。虽然机器的平均 CPU 使用率确实很低,但在某些时刻,例如流量高峰的时候,有些容器是能达到 90% 以上使用率的。如果这些容器都在同一时刻达到高峰,再额外调度会导致容器拿不到应有的资源,就会出现容器驱逐以及服务请求延迟变大等一系列问题。

我们的方案是在超售的情况下,避免在一台机器上调度超过资源上限的“会同一时刻达到高峰”的容器(下文简称“同峰容器”)。借助服务画像和对业务的了解,我们可以很容易地总结出容器达到高峰的时间段以及原因,并通过 label 的方式予以区分。

这个方案的整体架构如下图所示:

上图中的 kube-apiserver、kube-scheduler 和 kubelet 都是大家熟悉的 Kubernetes 组件。而额外的两个组件分别是 Customised Scheduler Plugin 和 Cgroup Agent Daemon。

Customised Scheduler Plugin 扩展了 Kubernetes Scheduler 的功能,使它能够允许超售资源,但同时阻止调度超过资源上限的同峰容器。这个 Plugin 并不关心每台机器上的 CPU 如何分配或者是否合理,只会考虑目标机器是否有足够的资源调度容器。这种方式能有效地降低 Plugin 调度 Pod 的时间复杂度。

而另一个自定义的组件 Cgroup Agent,则是以 Daemon 的方式运行在每一台机器上。Cgroup Agent 从 API Server 中获取 Pod 的容器信息,并通过 CRD 在集群中维护状态。由于 Customized Scheduler Plugin 保证了一定有足够的可分配资源,每一个 Cgroup Agent 只会考虑当前机器上容器的资源分配。Cgroup Agent 通过设定 Cgroups 给每个容器合理地分配资源,以保证不同容器(尤其是那些同峰容器)不会在高峰期互相抢占资源,避免由于过度超售对服务造成性能影响。

那么这个同峰容器的资源上限是怎么定义的呢?Customized Scheduler Plugin 如何判断目标机器是否有足够的资源?这里需要引入两个额外的概念。

第一个概念源自 Kubernetes 的 QoS Classes (Quality of Services),但我们的使用和官方有一定差异。我们将集群中的容器分为三类:

  • Guaranteed:这类容器会保证它们能够合理使用声明的资源,即配置在 Kubernetes 的 YAML 中的数值,它们的 Pod 的 CPU Request 始终等于 Limit。
  • Burstable:这类容器就是之前提到过的类型,在某些时刻资源用量达到高峰,但大部分时候资源用量又低于申请数值。Overcommit Factor 设置后的容器都是 Burstable 类型,它们的 CPU Request 小于 Limit,大部分时间都使用 Request 数值的资源,但是高峰期则使用 Limit 约定的资源用量。
  • BestEffort:这类容器我们一般用来运行不太关心延迟以及资源限制的服务,例如一些后台运用(Daemon)。在 Kubernetes 中,它们的 CPU Request 和 Limit 都是空。

第二个概念是 UsageClass,这个概念只对 Burstable 类型的 Pod 生效。

UsageClass 定义了在“某一情况”会达到资源用量高峰的 Burstable Pod 的集合,即同峰容器的集合。这个“某一情况”可以是某个时区,或者一些其他的条件。任意两个不同 UsageClass 的“某一情况”的交集应该为空。从容器的角度讲,即给定“某一情况”,这个容器只会最多属于某一个 UsageClass。

例如现在有两个 Usage Class:timeZone01 和 timeZone03,timeZone01 定义了所有在 UTC+1 时区会达到使用高峰的 Pod,而 timeZone03 定义了 UTC+8 时区会达到使用高峰的 Pod。

一个 Burstable Pod 可以对应多个 Usage Class。由于 Shopee 提供服务的对象横跨多个时区,我们可以将不同服务对象的时区设置为不同的 UsageClass。通过前文提到的同峰容器的 label,就能够找到容器所属的 UsageClass。

那么,我们可以达到的资源上限为:

  • 机器上容器的 Request 总和不能超过机器的可分配资源;
  • 对于每台机器上的每个 UsageClass,容器的 Limit 总和加上 Guaranteed 容器的资源不能超过机器的可分配资源(这样即使在某个时间段所有容器达到 Limit,依旧不会出现 Throttle 的情况)。

假设我们设置 Overcommit Factor 为 2,即 Request 是 Limit 的一半,那么理想情况下,我们能调度多一倍的资源,最坏情况下则能保证单机调度的资源量不变。

上图的例子是一个 8 核的机器,有两个 UsageClass,每个 Pod 都只有一个 Container,全局 Overcommit Factor 为 2(即 Request 变为了 Limit 的一半,如果有小数,则向上取整)。这个集群目前有以下的 Pod:


TypeResource
(Request & Limit)
UsageClass
Pod0Guaranteed1R1L
Pod1Burstable1R2LClass-B
Pod2Burstable2R3LClass-B
Pod3Burstable2R3LClass-A
Pod4Burstable1R2LClass-A
  • Request 总和:1+1+2+2+1=7 < 8
  • UsageClass-A:2+3+(1 Guaranteed) = 6 < 8
  • UsageClass-B:3+2+(1 Guaranteed) = 6 < 8

现有情况下,我们可以再额外调度一个 1R1L 的 Guaranteed Pod,或者一个 1R2L 的 Burstable Pod(Class-A 或 B 都可),或者一个 1RNL 的 UsageClass-X(N 取决于 Overcommit Factor)。

假设再调度了一个 1R2L 的 Class-A,机器达到完全调度,我们一共调度了 13CPU 的资源,超售了 62.5% 的资源。

2. Co-location(混部)

目前 Shopee 的业务模型可以分为两种:

  • 在线服务:运行时间长,对 latency 非常敏感,SLA 要求高。这种类型的业务主要以 Kubernetes 的 Pod 形式运行在 Kubernetes 集群中。
  • 离线任务:运行时间不固定,有较为宽松的 deadline,对 delay 不敏感,允许中断且容错率高。这种类型的业务主要是使用 YARN 调度的离线 Job。

Shopee 的在线服务和离线任务分属于不同的计算集群,在线服务和离线任务的资源高峰也是错开的,我们可以通过在离线混部来充分提高在线集群的资源利用率。

而由于 Shopee 在东南亚的数据中心处于自然灾害频发的地区,常有地震、火山等导致的数据中心网络或电源异常中断,在离线混部也对各种突发场景提供了灵活调配资源的弹性。通过对离线集群中的低优先级任务驱逐或者压制,腾出空间给在线服务,来保障用户正常使用 Shopee 平台,而不受影响。

目前 Shopee 的混部通过两个维度实现:

第一个维度是从集群的视角出发,以机器为单位进行调度。混部集群每台机器同时安装了 kubelet 和 Node Manager。当我们把机器设置为在线模式,控制器会通知 YARN Resource Manager 下线对应的 Node Manager,等待机器上正在运行的 Job 平滑地退出后关闭 Node Manager。在线模式下,机器将不能再调度离线任务。相反的,当我们把机器设置为离线模式,Colocation Controller 会 taint 对应的节点,并且驱逐掉在线服务,然后重启 Node Manager。

集群维度的调度粒度较粗,通常由 SRE 在需要的时候手动发起。在 Shopee,一般是大促前或者灾备切换时,使用这种方式将部分机器设置为在线服务独占,保证在线服务的平稳运行,然后在大促结束后或者其他数据中心恢复时,用这种方式将部分机器设置为离线模式,保证积压的离线任务及时消化。

同时,这种维度的实现难度较低,能够在初期就体现出较好的效果。

第二个维度从机器的视角出发,我们以服务为单位进行调度。混部机器节点将同时运行在线和离线服务。这些机器上,会有一个管理进程维护 YARN 和 Kubernetes 的对接(如下图中的 Colocation Agent),它会向双方两个调度器汇报当前机器资源的实际使用情况,并负责预留一部分资源作为缓冲。对于那些会突发性占用超大量资源的在线业务或不能承担重试的离线业务,Colocation Agent 会阻止它们的调度。

对于机器维度的混部,比较棘手的问题还是单台机器上资源的隔离。当在离线业务同时执行的情况下,我们很难保证在线业务完全不受离线的影响,这会导致在线服务的 Latency 增加。我们的解决方案是修改 Linux CFS 调度器,通过给在线服务提供额外的设置,让 Linux CFS 调度器可以在内核中识别在线服务和离线任务,从而进行差别性调度。修改过的调度器能够保证:

  • 在线服务始终比离线任务有更高的优先级;
  • 离线服务始终能使用剩余的所有资源;
  • 当在线服务 burst 的时候,调度器会 throttle 离线服务,并保证在线服务的资源需求不受影响。

机器维度的混部粒度较细,能够较为精确地调度在离线资源,维持机器日常运行时较高的资源水位。而且这种模式 SRE 手动介入的情况较少,降低了 SRE 日常集群维护的成本。

3. Auto Scaling(弹性伸缩)

除了利用上文提到的超售、混部两种技术来提升资源利用率,我们在 Shopee Marketplace 业务线还启用了 Auto Scaling(弹性伸缩)。因为 Shopee Marketplace 业务线的在线服务都有不规律的流量尖峰,在闪购、促销等环节往往需要大量的资源扩容实例数,而过了流量尖峰后还占用如此多的资源又是对资源的极大浪费。弹性伸缩就可以很好地解决这种场景下资源浪费的问题。

Shopee Marketplace 某服务
在某个时间区间内的流量曲线

与超售和混部不同,弹性伸缩关注的不是资源的分配或者是机器和集群,而是从服务的角度看待资源的使用。弹性伸缩源自于一个非常朴素的理念:如果使用量不够多,那就少申请资源,如果使用量太大,那就多申请资源。

有过 Kubernetes 使用经验的人应该会对 HPA(Horizontal Pod Autoscaler)不陌生。原生 Kubernetes 的 Autoscaler 已经能够通过 metrics 里记录的 CPU 和 mem 使用,对集群中的 Pod 进行增加和减少。然而,仅仅根据 metrics 是不够准确的,面对突然增大的 CPU 压力,HPA 没法判断实际真正需要的资源,而只能根据用户配置的 step 不停地试错。如果连续出现类似于 spike 的波动,很有可能出现 HPA 不停地扩缩容,这会加大请求延迟,并且会影响服务接口的稳定性。基于 Kubernetes HPA 存在的一些问题,我们做了一系列改进。

上图是 Shopee Auto Scaler 的架构图。除了从 Kubernetes 和监控系统实时获取信息,Auto Scaler 还会从其他额外组件(我们叫做 Caller)获取信息。现在已经实现的 Caller 有以下几种:

  • Statistics Platform 是资源计算和统计的组件。根据设置的目标 QPS,它能够估测出每一个核心服务大概需要多少资源,需要多少副本。根据 Statistics Platform 返回的数据,Auto Scaler 能算出一个大致的扩缩容基线。
  • Pilot Portal 是 Auto Scaler 的前端平台,SRE 能通过对每个服务进行非常简单的观测,有多少资源被占用,资源使用率是多少,进而手动调整副本数量或是配置参数。
  • Stress Test Platform 是内部的压测平台,和 Statistics Platform 类似,压测平台也能获取为了达到目标 QPS 每个服务需要用到的资源数值。
  • Script 则提供了一个接口,用于可扩展地实现 Caller。

除了支持所有原生 HPA 的功能,以及提供更多的扩缩容信息,我们还额外关注了一些全局信息,例如机房间的带宽用量等。同时,考虑到集群中的碎片资源,Auto Scaler 还能够推荐最匹配的 Kubernetes Node。这些能力减少了由于调度产生的资源碎片化,并通过合理分配提高了集群资源的利用率。

总结

全球气候变暖是当前人类面临的重大挑战之一,过去几年,不同互联网企业纷纷参与到低碳经济中。数据中心做为互联网企业碳排放的大头,建设更加绿色的计算中心不仅是企业在践行社会责任,也对企业的成本管理有更直接的收益。

Shopee 做为领先的电商平台之一,在数据中心的绿色计算上一直在不断探索和加大投入,本文从 Overcommit(超售)、Colocation(混部)、AutoScaling(弹性伸缩)三个方面详细介绍了 2021 年 Shopee 在业务侧的绿色计算实践。

经统计,在应用这些技术之后,我们节约了数以千计的机器数量,按照数据中心当地电网排放因子(Grid Emission Factor)换算,全年减少 CO2 排放量超过 4 万吨。

后续,我们期待与读者继续分享 Shopee 在数据中心风火水电方面的绿色计算实践,也期待您加入我们,和我们一道构建更绿色的数据中心。

本文作者

Hailin、Irvin、Edward、Yixin、Shuqi,后端研发工程师,来自 Shopee Engineering Infrastructure 团队。

加入我们

Engineering Infrastructure 团队涵盖多个 Shopee 基础设施工程领域,从整体基础设施交付、Shopee 电商平台稳定性建设、IaaS 和 CaaS 运维,到技术运营监控,同时也担负各类云原生 DevOps 平台产品、流量接入和计算统一调度产品、虚拟化、SDN、Linux 内核与容器运行时等各类基础平台产品的研发和运维。

我们的愿景是成为业内领先的基础设施工程团队,团队的使命是确保 Shopee 高效和可持续性运转,研发和运营大规模、高可用、高效能的云原生基础设施和 DevOps 生态。我们的价值体现在确保海量基础设施在快速演进的同时,具备高可用性以及扩展性。并且从成本、稳定性和效能的角度切入到基础平台各产品的研发和运维,以支持公司业务多市场的特色发展。

目前团队在新加坡、北京、上海、深圳均有大量岗位持续招聘,涵盖后端、SRE,感兴趣的同学可在社招官网搜索“云原生” “流量调度” “SDN” “AZ” “MRE” “SRE”等关键词查看岗位信息,亦可将简历发送至 josie.chen@shopee.com 进行咨询(备注来自 Shopee 技术博客)。

👇点击阅读原文,共建绿色数据中心

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存