本文介绍了美团在如何解决大规模集群管理的难题、设计优秀且合理的集群调度系统方面的实践,阐述了美团在落地以Kubernetes为代表的云原生技术时,比较关心的问题、挑战以及对应的推进策略。同时本文也介绍了针对美团业务需求场景做的一些特色支持,希望本文能够对云原生领域感兴趣的同学有所帮助或者启发。
导语
集群调度系统介绍
大规模集群管理的难题
运营大规模集群的挑战
设计集群调度系统时的取舍
美团集群调度系统演变之路
多集群统一调度:提升数据中心资源利用率
调度引擎服务:赋能PaaS服务云原生落地
未来展望:构建云原生操作系统
集群调度系统在企业数据中心中占有举足轻重的地位,随着集群规模与应用数量的不断激增,开发者处理业务问题的复杂度也显著提升。如何解决大规模集群管理的难题,设计优秀且合理的集群调度系统,做到保稳定,降成本,提效率?本文将会逐一进行解答。
| 备注:文章最早发布于《新程序员003》云原生时代的开发者专栏。
集群调度系统,又被称为数据中心资源调度系统,普遍用来解决数据中心的资源管理和任务调度问题,它的目标是做到数据中心资源的有效利用,提升资源的利用率,并为业务方提供自动化的运维能力,降低服务的运维管理成本。工业界比较知名的集群调度系统,如开源的OpenStack、YARN、Mesos和Kubernetes等等,再如知名互联网公司Google的Borg、微软的Apollo、百度的Matrix、阿里巴巴的Fuxi和ASI。
集群调度系统作为各互联网公司核心的IaaS基础设施,在近十几年经历了多次架构演进。伴随着业务从单体架构向SOA(面向服务的架构)演进和微服务的发展,底层的IaaS设施也从物理机裸机时代逐步跨越到容器时代。虽然在演进过程中我们要处理的核心问题没有改变,但由于集群规模和应用数量的急剧膨胀,问题的复杂度也成指数级增长。本文将阐述大规模集群管理的挑战和集群调度系统的设计思路,并以美团集群调度系统落地实践为例,讲述通过打造多集群统一调度服务,持续提升资源的利用率,提供Kubernetes引擎服务赋能PaaS组件,为业务提供更好的计算服务体验等一系列云原生实践。
众所周知,业务快速增长带来的是服务器规模和数据中心数量的暴增。对于开发者而言,在大规模集群调度系统的业务场景下,必须要解决的两个难题是:
如何管理好数据中心大规模集群部署调度,特别是在跨数据中心场景下,如何实现资源的弹性和调度能力,在保障应用服务质量的前提下尽可能地提升资源的利用率,充分降低数据中心成本。
如何改造底层基础设施,为业务方打造云原生操作系统,提升计算服务体验,实现应用的自动化容灾响应和部署升级等,减少业务方对底层资源管理的心智负担,让业务方可以更专注于业务本身。
为了在真实的生产环境解决上述两个难题,具体又可以再拆分成以下四个大规模集群运营管理挑战:
如何解决用户多样化需求并快速响应。业务的调度需求和场景丰富且动态多变,作为集群调度系统这样的平台型服务,一方面需要能够快速交付功能,及时满足业务需求;另一方面还需要把平台打造得足够通用,将业务个性化需求抽象为可落地到平台的通用能力,并长期进行迭代。这非常考验平台服务团队的技术演进规划,因为一不小心,团队就会陷入无休止的业务功能开发中,虽然满足了业务需求,却会造成团队工作低水平重复的现象。
如何提高在线应用数据中心的资源利用率且同时保障应用服务质量。资源调度一直是业界公认的难题,随着云计算市场快速发展,各云计算厂商不断加大对数据中心的投入。数据中心的资源使用率却非常低,更加剧了问题的严重性。Gartner调研发现全球数据中心服务器CPU利用率只有6%~12%,即使是亚马逊弹性计算云平台(EC2,Elastic Compute Cloud)也只有7%~17%的资源利用率,可见资源浪费有多严重。究其原因,在线应用对于资源利用率非常敏感,业界不得不预留额外资源以保障重要应用的服务质量(QoS,Qualityof Service)。集群调度系统需要在多应用混合运行时消除应用间的干扰,实现不同应用之间的资源隔离。
如何为应用,特别是有状态应用提供实例异常自动处理,屏蔽机房差异,降低用户对底层的感知。随着服务应用规模的持续扩大,以及云计算市场的日趋成熟,分布式应用往往会配置在不同地域的数据中心,甚至是跨越不同的云环境,实现了多云或混合云部署。而集群调度系统需要为业务方提供统一的基础设施,实现混合多云架构,屏蔽底层的异构环境。同时降低应用运维管理的复杂性,提升应用的自动化程度,为业务提供更好的运维体验。
如何解决单集群过大或集群数量过多,而带来的与集群管理相关的性能和稳定性风险。集群本身的生命周期管理复杂度会伴随集群规模和数量的增多而增大。以美团为例,我们所采取的两地多中心多集群方案,虽然在一定程度上规避了集群规模过大的隐患,解决了业务隔离性、地域延迟等问题。随着边缘集群场景和数据库等PaaS组件上云需求的出现,可以预见小集群数量将会有明显的上涨趋势。随之带来的是集群管理复杂度、监控配置成本、运维成本的明显增加,这时集群调度系统需要提供更有效的操作规范,并保证操作安全性、报警自愈和变更效率。
为了解决上述挑战,一个好的集群调度器将发挥关键作用。但现实中从来不存在一个完美的系统,所以在设计集群调度系统时,我们需要根据实际场景在几个矛盾中做出取舍:
集群调度系统的系统吞吐量和调度质量。系统吞吐量是我们通常评估一个系统好坏很重要的标准,但在面向在线服务的集群调度系统里更重要的是调度质量。因为每次调度结果的影响是长期的(数天、数周甚至数月),非异常情况不会调整。所以如果调度结果错误,会直接导致服务时延增高。而调度质量越高则意味着需要考虑的计算约束条件越多,而且调度性能越差的话,系统吞吐量越低。
集群调度系统的架构复杂度和可扩展性。系统对上层PaaS用户开放的功能和配置越多,通过支持更多功能来提升用户体验(比如支持应用资源抢占回收和应用实例异常自愈),也就意味着系统复杂度越高,各子系统越容易发生冲突。
集群调度系统的可靠性和单集群规模。单集群规模越大,则可调度范围则越大,但对集群的可靠性挑战也越大,因为爆炸半径会增加,出现故障的影响也越大。单集群规模较小的情况下,虽然可以提升调度并发度,但可调度范围变小,调度失败概率变高,且集群管理复杂度变大。
目前,业内的集群调度系统按照架构区分,可以分为单体式调度器、两级调度器、共享状态调度器、分布式调度器和混合调度器这五种不同架构(见下图1),都是根据各自的场景需求做了不同的选择,没有绝对的好与坏。
图1 集群调度系统架构分类(摘自Malte Schwarzkopf - The evolution of cluster scheduler architectures)单体式调度器使用复杂的调度算法结合集群的全局信息,计算出高质量的放置点,不过延迟较高。如Google的Borg系统、开源的Kubernetes系统。
两级调度器通过将资源调度和作业调度分离,解决单体式调度器的局限性。两级调度器允许根据特定的应用做不同的作业调度逻辑,且同时保持了不同作业之间共享集群资源的特性,可是无法实现高优先级应用的抢占。具有代表性的系统是Apache Mesos和Hadoop YARN。
共享状态调度器通过半分布式的方式来解决两级调度器的局限性,共享状态下的每个调度器都拥有一份集群状态的副本,且调度器独立对集群状态副本进行更新。一旦本地的状态副本发生变化,整个集群的状态信息就会被更新,但持续资源争抢会导致调度器性能下降。具有代表性的系统是Google的Omega和微软的Apollo。
分布式调度器使用较为简单的调度算法以实现针对大规模的高吞吐、低延迟并行任务放置,但由于调度算法较为简单并缺乏全局的资源使用视角,很难达到高质量的作业放置效果,代表性系统如加州大学的Sparrow。
混合调度器将工作负载分散到集中式和分布式组件上,对长时间运行的任务使用复杂算法,对短时间运行的任务则依赖于分布式布局。微软Mercury就采取了这种这种方案。
所以,如何评价一个调度系统的好坏,主要取决于实际的调度场景。以业内使用最广泛的YARN和Kubernetes为例,虽然两个系统都是通用资源调度器,实际上YARN专注于离线批处理短任务,Kubernetes专注于在线长时间运行的服务。除了架构设计和功能的不同(Kubernetes是单体式调度器,YARN是两级调度器),二者的设计理念和视角也不同。YARN更专注任务,关注资源复用,避免远程数据多次拷贝,目标是以更低成本、更高速度执行任务。Kubernetes更专注服务状态,关注错峰、服务画像、资源隔离,目标是保障服务质量。
美团在落地容器化的过程中,根据业务场景需求,集群调度系统核心引擎由OpenStack转变为Kubernetes,并在2019年底完成了在线业务容器化覆盖率超过了98%的既定目标。但依然面临资源利用率低、运维成本高等问题:
集群整体的资源利用率不高。如CPU资源平均利用率还处于业内平均水平,相较于其他一线互联网公司差距较大。
有状态服务的容器化率程度不够,特别是MySQL、Elasticsearch等产品没有使用容器,业务运维成本和资源成本存在较大的优化空间。
从业务需求考虑,VM产品会长期存在,VM调度和容器调度是两套环境,导致团队虚拟化产品运维成本较高。
因此,我们决定开始对集群调度系统进行云原生改造。打造一个具有多集群管理和自动化运维能力、支持调度策略推荐和自助配置、提供云原生底层扩展能力,并在保障应用服务质量的前提下提升资源使用率的大规模高可用调度系统。核心工作围绕保稳定、降成本、提效率三大方向来构建调度系统。
保稳定:提升调度系统的健壮性、可观测性;降低系统各模块之间的耦合,减少复杂度;提升多集群管理平台的自动化运维能力;优化系统核心组件性能;确保大规模集群的可用性。
降成本:深度优化调度模型,打通集群调度和单机调度链路。从资源静态调度转向资源动态调度,引入离线业务容器,形成自由竞争与强控结合,在保障高优业务应用服务质量的前提下,提升资源使用率,降低IT成本。
提效率:支持用户自助调整调度策略,满足业务个性化需求,积极拥抱云原生领域,为PaaS组件提供包括编排、调度、跨集群、高可用等核心能力,提升运维效率。
最终,美团集群调度系统架构按照领域划分为三层(见上图2),调度平台层、调度策略层、调度引擎层:
平台层负责业务接入,打通美团基础设施,封装原生接口和逻辑,提供容器管理接口(扩容、更新、重启、缩容)等功能。
策略层提供多集群统一调度能力,持续优化调度算法和策略,结合业务的服务等级和敏感资源等信息,通过服务分级提升CPU使用率和分配率。
引擎层提供Kubernetes服务,保障多个PaaS组件的云原生集群稳定性,并把通用能力下沉到编排引擎,降低业务云原生落地的接入成本。
通过精细化运营和产品功能打磨,我们一方面统一纳管了美团近百万的容器/虚拟机实例,另一方面将资源利用率从业内平均水平提升到了一流水平,同时还支撑了PaaS组件的容器化和云原生落地。
评估考核集群调度系统的好坏,资源利用率是最重要的指标之一。因此,虽然我们在2019年完成了容器化,不过容器化不是目的,只是手段。我们的目标是通过从VM技术栈切换到容器技术栈,为用户带来更多的收益,比如全面降低用户的计算成本。
而提升资源利用率受限于集群的个别热点宿主,一旦扩容,业务容器就有可能扩容到热点宿主,业务的性能指标如TP95耗时会出现波动,以至于我们只能像业界其他公司一样,通过增加资源冗余来保障服务质量。究其原因,Kubernetes调度引擎的分配方式仅简单考虑了Request/Limit Quota(Kubernetes为容器设定了请求值Request和约束值Limit,作为用户申请容器的资源配额),属于静态资源分配。导致不同宿主机虽然分配了同样多的资源,却因宿主机的服务差异性使得宿主机的资源利用率也存在较大的差异。
在学术界和工业界中,有两种常用的方法解决资源使用效率和应用服务质量之间的矛盾。第一种方法是通过高效的任务调度器在全局角度解决;第二种方法是通过单机资源管理手段来加强应用之间的资源隔离。不管是哪一种方法,都意味着我们需要全面掌握集群状态,所以我们做了三件事:
系统地建立了集群状态、宿主状态、服务状态的关联,并结合调度仿真平台,综合考虑了峰值利用率和平均利用率,实现了基于宿主历史负载和业务实时负载的预测和调度。
通过自研的动态负载调节系统和跨集群重调度系统,实现了集群调度和单机调度链路的联动,根据业务分级实现了不同资源池的服务质量保障策略。
经过三版迭代,实现了自有集群联邦服务,较好地解决了资源预占和状态数据同步问题,提升了集群间的调度并发度,实现了计算分离、集群映射、负载均衡和跨集群编排控制(见下图3)。
集群联邦服务第三版本按照模块拆分为Proxy层和Worker层,独立部署:
Proxy层会综合集群状态的因子及权重选择合适的集群进行调度,并选择合适的Worker分发请求。Proxy模块使用etcd做服务注册、选主和发现,Leader节点负责调度时预占任务,所有节点都能负责查询任务。
Worker层对应处理一部分Cluster的查询请求,当某集群任务阻塞,可以快速扩容一台对应的Worker实例缓解问题。当单集群规模较大时会对应多个Worker实例,Proxy将调度请求分发给多个Worker实例处理,提升调度并发度,并减少每一个Worker的负载。
最终通过多集群统一调度,我们实现了从静态资源调度模型转向动态资源调度模型,从而降低了热点宿主比例,减少了资源碎片比例,保障了高优业务应用的服务质量,将在线业务集群的服务器CPU利用率均值提升了10个百分点。集群资源利用率均值计算方式:Sum(nodeA.cpu.当前使用核数 + nodeB.cpu.当前使用核数 + xxx) / Sum(nodeA.cpu.总核数 + nodeB.cpu.总核数 + xxx),一分钟一个点,当天所有值取平均。
集群调度系统除了解决资源调度的问题之外,还解决服务使用计算资源的问题。正如《Software Engineering at Google》一书中提到的,集群调度系统作为Compute as a Service中关键组件之一,既要解决资源调度(从物理机拆解到CPU/Mem这样的资源维度)和资源竞争(解决“吵闹邻居”),还需要解决应用管理(实例自动化部署、环境监控、异常处理、保障服务实例数、确定业务需求资源量、不同服务种类等)。而且从某种程度上来说应用管理比资源调度更重要,因为这会直接影响业务的开发运维效率和服务容灾效果,毕竟互联网的人力成本比机器成本更高。
复杂的有状态应用的容器化一直是业界难题,因为这些不同场景下的分布式系统中通常维护了自己的状态机。当应用系统发生扩缩容或升级时,如何保证当前已有实例服务的可用性,以及如何保证它们之间的可连通性,是相较无状态应用复杂许多的棘手问题。虽然我们已经把无状态服务都容器化了,但我们还没有充分发挥出一个良好的集群调度系统的全部价值。如果要想管好计算资源,必须管理好服务的状态,做到资源和服务分离,提升服务韧性,而这也是Kubernetes引擎所擅长的。
我们基于美团优化定制的Kubernetes版本,打造了美团Kubernetes引擎服务MKE:
加强集群运维能力,完善了集群的自动化运维能力建设,包括集群自愈、报警体系、Event日志分析等,持续提升集群的可观测性。
竖立重点业务标杆,与几个重要的PaaS组件深入合作,针对用户的痛点如Sidecar升级管理、Operator灰度迭代、报警分离做快速优化,满足用户的诉求。
持续改进产品体验,持续优化Kubernetes引擎,除了支持用户使用自定义Operator之外,也提供了通用的调度和编排框架(见图4),帮助用户以更低的成本接入MKE,获得技术红利。
在我们推进云原生落地过程中,一个广泛被关注的问题是:基于Kubernetes云原生方式来管理有状态应用,相比于之前自己打造管理平台有什么区别?
对于这个问题,需要从问题根源——可运维性考虑:
基于Kubernetes意味着系统做到了闭环,不用担心两套系统经常出现的数据不一致问题。
异常响应可以做到毫秒级别,降低了系统的RTO(Recovery Time Objective,即恢复时间目标,主要指所能容忍的业务停止服务的最长时间,也是从灾难发生到业务系统恢复服务功能所需要的最短时间周期)。
系统运维复杂度也降低了,服务做到了自动化容灾。除了服务本身之外,服务依赖的配置和状态数据都可以一起恢复。
相比于之前各个PaaS组件“烟囱式”的管理平台,通用能力可以下沉到引擎服务,减少开发维护成本,而通过依托于引擎服务,可以屏蔽底层异构环境,实现跨数据中心和多云环境的服务管理。
我们认为,云原生时代的集群管理,会从之前的管理硬件、资源等职能全面转变为以应用为中心的云原生操作系统。以此为目标,美团集群调度系统还需从以下几方面发力:
应用链路交付管理。随着业务规模和链路复杂度的增大,业务所依赖的PaaS组件和底层基础设施的运维复杂度早已超过普遍认知,对于刚接手项目的新人更是难上加难。所以我们需要支持业务通过声明式配置交付服务并实现自运维,给业务提供更好的运维体验,提升应用的可用性和可观测性,减少业务对底层资源管理的负担。
边缘计算解决方案。随着美团业务场景的不断丰富,业务对边缘计算节点的需求增长,比预期快很多。我们会参考业内最佳实践,形成适合在美团落地的边缘解决方案,尽快为有需求的服务提供边缘计算节点管理能力,实现云边端协同。
在离线混部能力建设。在线业务集群的资源利用率提升是有上限的,根据Google在论文《Borg: the Next Generation》中披露的2019年数据中心集群数据,刨去离线任务,在线任务的资源利用率仅为30%左右,这也说明了再往上提升风险较大,投入产出比不高。后续,美团集群调度系统将持续探索在离线混部,不过由于美团的离线机房相对独立,我们的实施路径会与业界的普遍方案有所不同,会先从在线服务和近实时任务的混部开始,完成底层能力的构建,再探索在线任务和离线任务的混部。
美团集群调度系统在设计时,整体遵循合适原则,在满足业务基本需求的情况下,保证系统稳定后再逐步完善架构,提升性能和丰富功能。因此,我们选择了:
在系统吞吐量和调度质量中我们选择优先满足业务对系统的吞吐量需求,不过度追求单次调度质量,而是通过重调度调整完善。
在架构复杂度和可扩展性中我们选择降低系统各模块之间的耦合,减少系统复杂度,扩展功能必需可降级。
在可靠性和单集群规模中我们选择通过多集群统一调度来控制单集群规模,保障系统可靠性,减少爆炸半径。
未来,我们也会根据同样的逻辑持续优化迭代美团的集群调度系统,彻底转变为以应用为中心的云原生操作系统。
谭霖,来自美团基础研发平台/基础技术部。