1. 线下主机托管的技术背景 1.1 为什么要主机托管
数据中心运行的服务可以分为两类:在线服务和离线任务,它们具有不同的资源使用特征。
在线服务是指那些运行时间较长、对延迟非常敏感的服务,例如电子商务、游戏等。在线服务的资源利用率存在明显的峰谷,平均利用率较低。离线任务是指运行周期短、容错能力强、实时性要求不高的业务,例如数据转换、模型训练等。离线任务执行时资源利用率高。
主机托管之前,线上线下分开部署,机器不共享,无法形成有效的资源互补。这导致数据中心整体资源利用率较低,但又要不断购买新机器,造成资源浪费。
1.2 主机托管技术定义
通过共置技术,我们可以将线上和线下系统部署在同一台物理机上,形成资源互补,提高物理机的资源利用率,降低成本。共置技术最早由于2015年提出,经过多年的发展,共置技术已经趋于成熟。目前,业界可以利用主机托管技术将数据中心的CPU利用率提高到40%左右。
vivo从2020年开始研究主机托管技术,主机托管平台将于2023年投产。目前我们已经将部分主机托管集群的CPU利用率提升到25%左右(最新已经达到30%) )。与行业标杆相比还有一定差距,但随着主机托管规模的扩大,我们将挑战更高的目标。
2. 线下主机托管平台实践 2.1 主机托管平台的产品能力
主机托管平台必须具备两个产品能力:
强大的调度能力解决了如何高效合理地将离线任务调度到在线服务所在的物理机上的问题。强大的隔离能力保证线上服务质量不受线下任务的干扰。完善的监控和运维能力可以让我们洞察整个主机托管平台的运行情况,及时发现潜在风险,帮助运维人员更高效地完成系统和业务运维工作,保证集群稳定性高。
2.2 共置差异化资源视图
共置首先要解决的问题之一是离线使用哪部分资源。
vivo主机代系统中线上和线下看到的资源视图是不同的:
同时,为了防止机器整体负载过高,影响系统稳定性,我们设置了安全水位来调整离线可用资源的大小。
2.3 共置 QoS 级别
为了保证主机代管系统的SLO,我们将服务分为高、中、低三个级别。
不同级别的服务在使用CPU、内存等物理资源时有不同的优先级。高优先级业务支持CPU-bound模式,适合对延迟非常敏感的在线业务。一般在线服务可以设置为中等优先级。离线任务设置为低优先级。通过这种层级划分,可以有效压制和隔离线上线下资源,保证线上服务质量。
2.4 混合核心组件架构
我们所有的混合组件都作为插件独立运行,不会侵入原生 K8。我们实现了一个共置调度器,它可以在线和离线使用,以避免多调度器资源账本冲突的问题。
每台物理机上都部署了主机托管代理,可以实时收集容器资源使用数据,并根据安全水位执行抑制、驱逐离线任务等操作。
在内核层面,我们采用了 OS,它具有强大的资源隔离能力,可以帮助我们更好地隔离线上线下的资源使用情况,保证线上服务的质量。
2.5 混合组件功能
我们将混合部署组件分为两类:管控组件和独立组件。
管控组件主要负责调度和控制。根据vivo业务使用场景,我们对调度器做了一些增强,提供numa感知、负载感知、热点打散、批量调度等能力。
共置控制器主要提供一些配置管理能力:如资源画像统计、节点slo配置、节点扩展资源变更等。
2.6 主机共置可视化监控
我们建立了完善的主机代管可视化监控系统。
针对线上服务,我们提供:容器资源使用指标、线下干扰指标、业务托管收益指标等监控能力。
针对离线任务,我们提供离线可用资源、任务异常状态等监控能力。
在平台层面,我们提供节点、内核和核心组件的监控。通过这些监控,可以及时发现平台潜在的风险。
2.7 主机代管平台运维
为了简化运维操作,提高运维效率,我们对共地集群的建立和节点扩缩容操作进行了白屏改造,开发了资源池管理功能,简化了物理机接入流程,大大提高了运维效率。 。
在运维平台上,运维人员可以快速调整主机隔离、水位等参数。如果发现线上服务受到干扰,运维人员可以一键关闭主机并驱逐离线任务,保证线上服务质量。
2.8 问题与挑战
2.8.1 分割
通过共置产品能力的建设,我们已经成功实现了容器共置的能力,但在实践中我们仍然遇到一些新的挑战:与普通的K8s集群相比,更多的容器运行在共置集群中,并且由于由于离线任务生命周期短,容器的创建和销毁更加频繁,这给K8s带来了很大的压力。
所以我们将其拆分,使用独立的离线任务来保证集群负载始终处于安全水平。
2.8.2 监控架构优化
同样,共置后,采集的监控指标较多,导致内存消耗过多,无法满足平台指标采集需求。针对这一问题,我们优化了监控架构,线上和线下监控组件分开部署,线下切换到性能更好的监控组件。通过这样的优化,监控组件的内存消耗降低到了原来的十分之一。
2.9 利用率提高
虽然共置初期集群CPU利用率有所提升,但仍然没有达到我们的预期。主要原因有:
针对这些问题,我们开发了定期调整安全水位的功能,在业务淡季提高安全水位,释放更多资源供线下使用。通过一系列优化方法,我们将其中一个共置集群的CPU利用率从13%提高到25%左右,几乎翻了一番。共址效应得到有效验证。
3. Spark在K8s上的弹性调度实现 3.1 方案选择
总体技术选型上,我们选择了K8s上的Spark。业界也有一些公司采用了YARN on K8s解决方案。我们还比较了这两种选择。
在业务适用性方面,YARN on K8s具有通用性,兼容Hive、Spark、Flink等引擎。不需要频繁创建Pod,对K8的压力较小。这些都是它的优点,但另一方面,ESS服务对磁盘容量和读写性能的要求,一般混机盘很难满足。所以我们需要支持不同的引擎。
例如,如果计算引擎有不同的版本,那么RSS也必须支持不同的版本。如果你有不同的引擎、不同的版本,有可能一个RSS无法满足需求。另外,需要根据K8s混合节点的剩余资源动态调整可用的vcore和内存,因此需要额外的组件来完成此操作,改造成本较高。从资源利用上来说,NM的资源粒度比较大,也占用了一些资源,造成了一定的浪费。在资源限制下,整体驱逐会影响多项任务。这些都是 YARN 在 K8s 上的缺点。
作为对比,K8s上的Spark有哪些缺点?
首先,这个功能只有在Spark 3.1以上版本才正式可用。 Spark on K8s 会频繁创建、查询、销毁大量的 pod,这会给 K8s 的调度能力和节点带来较大的压力。另一方面,它的优点是只需要支持.X的RSS,并且有更多的开源产品可供选择。而且改造成本相对较低,不需要额外的部件。资源粒度小更有利于充分利用集群资源。当资源紧张时,pod会被一一驱逐,任务的稳定性会更高。
两种选择都有各自的优点和缺点。为什么我们选择 K8s 上的 Spark?一方面, 。基于以上原因,我们最终决定在K8s上使用spark
3.2 三步走战略
确定了方案选型后,如何推动Spark在K8s上在vivo的大规模应用?回顾和总结我们走过的路,大致可以概括为三步走的策略。
在接下来的内容中,我们将对每个阶段进行详细阐述。
3.2.1 任务顺利进行
在任务运行并顺利运行的第一阶段,我们要解决的是如何将任务提交到K8s集群上,同时在易用性方面要求达到与YARN上相同的用户体验。使用和方便。简化一下我们最终采用的解决方案的架构,如图所示。
首先,为了降低任务提交的复杂度,避免用户修改任务的成本。我们在任务调度管理平台中实现了对原有Spark任务的兼容。通过vivo内部的容器开放API——这个代理层,我们不需要维护额外的K8s环境,就可以轻松实现任务提交,而且可以近乎实时地提交。获取任务状态和日志信息。
另一个关键点是我们选择Spark作为Spark任务容器化解决方案。 Spark是基于K8s模型开发的工具。它用于以声明方式向 K8s 集群提交 Spark 作业。
Spark 的方法还有其他优点:
实现第一阶段目标,确保任务顺利进行。我们主要克服了哪些关键问题和挑战?
首先是日志查看,因为Spark方法没有提供已完成作业的日志查看方法,包括和日志。另一方面,我们可以通过定期请求容器的开放API来准实时地获取Pod状态和日志。侧面,我们参考on-yarn的方法。 Pod结束后,日志上传到HDFS,类似于YARN日志聚合。
另一方面,我们也对Spark做了二次开发工作,在K8s上增加了日志查看界面。当用户查看完成的日志时,不再请求,而是请求Spark接口。体验与yarn基本一致。
在共置K8s集群上,我们也加强了三个方面的能力。
3.2.2 任务运行稳定准确
第二阶段,我们要保证的是任务运行稳定,数据运行准确。因此,我们有两个关键措施:
第二阶段,我们主要面临三个问题和挑战。
首先,我们需要为 Spark 选择一个外部服务。经过技术选型和比较,我们最终选择了开源作为我们的组件。我们测试并调整模型和参数,使性能满足我们的预期需求。在大规模应用场景中,我们也发现大任务会阻塞小任务,导致读取变慢。我们针对这种情况优化了参数和代码。当前社区在阅读方面也存在一些问题。一些改进有待优化。此外,进行容器化部署,不仅提高了自动化运维能力,还为共置集群提供了额外的计算资源。
其次,在任务稳定性方面,我们也解决了一系列问题。
在双机运行过程中,我们发现很多任务在K8s模式下容易出现OOM。这是因为on YARN模式下申请的内存大小不仅由Spark任务本身的内存参数决定,还与YARN有关。受资源粒度参数影响。因此,这里需要做一些适应和基准测试工作。当任务量比较大时,Spark的吞吐能力会遇到瓶颈,我们需要增加并发数和队列相关参数。由于Spark任务频繁请求域名解析,压力增大,甚至可能影响线上服务。这可以通过访问IP而不是域名来规避,比如node、and。水平扩展可以避免单一瓶颈,防止etcd出现问题。我们的K8的压力会随着工作负载的增加而逐渐增大,从而影响整个集群的稳定性。我们主要优化了Spark list pod的接口和使用方法,有效减轻压力。
最后要说的是数据一致性。关键是要实现行级记录的MD5验证。如果存在不一致的情况,我们将实现100%的分析覆盖率。排除由于时间戳随机函数导致的一些预期不一致,我们发现并修复了两种偶尔会导致不一致的情况:
3.2.3 任务智能运行
第三阶段,我们需要解决任务如何智能化运行以及如何定义智能的问题。我想用三个词来概括弹性、稳健性和业务需求。这是我们灵活调度的架构图。我不会详细介绍。这里我介绍一下我们的调度系统支持的关键功能。
在弹性方面,我们需要根据同地集群资源的可用性,实时智能地提交到同地集群或多个集群。早期我们的K8s集群的资源比较少。通过合理的水位控制,我们避免了大量任务同时调度到K8上而饿死的情况。
鲁棒性意味着确保任务的高可用性。
我们构建的能力包括:
目的是让用户在迁移用户任务时毫无感觉。
在满足业务需求方面,我们支持该业务的离线任务优先调度,优先满足业务部门的离线任务资源需求;支持仅在指定时间段内调度离线任务,并支持出现异常时一键终止K8s调度。这些都是为了保证线上服务的高可用性,让线上商家免除后顾之忧。
3.3 混合部分效果
在克服了三步过程中的坎坷与坎坷之后,我们终于可以将离线任务大规模地混合到K8s混合集群中了。但我们很快发现,主机托管集群的整体利用率并没有达到我们的预期,主要有以下三个原因。
初期Spark任务不足。我们通过加速双跑、迁移低版本Spark任务、迁移Hive SQL任务来解决这个问题。
在并置过程中,我们还观察到离线任务的 Pod CPU 利用率实际上并没有那么高。例如,当我们申请一个核心时,通常只能使用0.6个核心,这是一种浪费。我们推出了CPU资源超分的能力,目前是静态固定比例超分。通过这个措施,我们可以将Pod的实际CPU利用率提高到80%以上。
此外,共置集群中的机器配置不均匀。有些机器的CPU资源充足,但内存不足。通过在调度端控制可调度任务的资源粒度,我们尝试调度需要较少内存资源的任务。
通过我们的任务调度方以及之前甘青提到的其他措施。共置集群利用率进一步提高。
最后跟大家同步一下目前我们实现的混合部署效果。
目前我们有近2万个任务可供调度,这些任务每天的调度次数超过4万次。在凌晨高峰期,通过主机托管,我们能够额外增加2万个核心和50TB的内存计算资源用于离线任务。这个好处是相当可观的。我们还希望在未来2到3年内将可调度任务数量增加到6万个,弹性资源可以贡献离线计算资源总量的20%。
通过持续深入推广线下同地技术,我们希望能够继续为vivo的效率提升和成本降低工作做出贡献。
鱼云提供全球范围的云服务器和物理服务器租赁服务,具备强大的DDoS防御功能,确保您业务安全稳定运行,同时提供灵活定制和专业支持以满足多样化需求。