从资源孤岛到统一资源池:vivo大数据平台Spark on K8s混部实战解析

在vivo大数据平台的发展历程中,资源利用率的提升始终是一个核心命题。随着业务规模的持续扩张,在线服务与离线计算对资源的需求呈现出截然不同的特征。在线服务需要低延迟、高稳定的资源保障,而离线计算则追求高吞吐、可容忍延迟的弹性资源。传统做法是将两类业务部署在独立的物理集群中,通过硬隔离保证稳定性,但代价是资源利用率长期偏低,在线集群在业务低谷期大量资源闲置,离线集群在高峰期又常常资源不足。

混部技术的出现为这一矛盾提供了新的解法。混部即将在线服务和离线任务部署在同一批物理资源上,通过精细化的资源隔离、调度和优先级控制,在保证在线服务质量的前提下,将离线任务填充到在线业务的资源空闲时段,从而大幅提升整体资源利用率。Spark on K8s作为离线计算的核心形态,在混部场景中扮演着关键角色。

本文将从vivo大数据平台的真实实践出发,系统梳理Spark on K8s在混部场景下的架构设计、关键技术挑战、优化方案和落地效果,为正在探索混部技术的团队提供一份可参考的实战记录。

一、为什么需要混部

1.1 大数据平台的资源困境

在混部之前,vivo大数据平台采用在线与离线分离的部署模式。在线集群承载推荐、搜索、广告等对延迟敏感的实时服务,这些服务通常部署在物理机或虚拟机上,资源预留较为充裕,但实际利用率往往在百分之二十到百分之四十之间波动。离线集群承载Spark、Hive、Flink等批量计算任务,任务提交具有明显的潮汐特征,白天资源紧张,夜间相对空闲,整体利用率也不理想。

两套集群各自独立扩容,导致硬件采购成本居高不下。更关键的是,当在线业务出现突发流量时,运维团队需要紧急扩容在线集群,而离线集群的资源无法快速调配过来。反之,当离线任务量激增时,也无法借用在线集群的空闲资源。这种资源孤岛效应严重制约了平台的弹性能力。

1.2 在线与离线业务的资源特征

要理解混部的可行性,首先需要分析两类业务的资源特征差异。

在线服务通常具有以下特征:请求延迟要求在毫秒级,对CPU和内存的稳定性要求极高,资源使用率波动较大但峰值明确,进程生命周期长,通常以天或周为单位。在线服务对资源超卖非常敏感,因为任何资源争抢都可能导致请求超时,进而影响用户体验。

离线计算则呈现不同特征:任务运行时间从数分钟到数小时不等,对延迟不敏感但对吞吐量要求高,资源使用率在任务运行期间相对稳定,任务生命周期短,结束后资源立即释放。离线任务可以容忍一定程度的资源超卖,因为任务失败后可以重试,重试成本相对可控。

这种资源特征的互补性,正是混部能够成立的基础。在线业务在低谷期释放的资源,恰好可以被离线任务利用;而离线任务在在线业务高峰期主动让出资源,则保障了在线服务的稳定性。

1.3 混部的核心价值

混部的核心价值体现在三个方面。

第一是提升资源利用率。通过将离线任务填充到在线业务的空闲资源中,整体资源利用率可以从百分之三十左右提升到百分之六十以上,部分场景甚至可以达到百分之八十。

第二是降低硬件成本。在同等业务规模下,混部减少了对独立离线集群的硬件需求,直接降低了采购成本和机房成本。

第三是提升弹性能力。当在线业务需要紧急扩容时,可以快速驱逐离线任务,释放资源给在线服务。当离线任务需要更多资源时,也可以利用在线业务的空闲容量。这种双向弹性是独立集群无法实现的。

二、Spark on K8s的技术选型

2.1 从YARN到K8s的演进

vivo大数据平台最初使用YARN作为资源调度器,Spark任务通过YARN进行资源申请和调度。YARN在大数据领域成熟稳定,但与云原生技术栈的融合存在天然障碍。YARN的资源模型面向大数据任务设计,无法统一管理在线服务的容器化部署。YARN的调度粒度较粗,对GPU、内存超卖等高级特性的支持有限。

随着Kubernetes成为云原生的事实标准,将Spark迁移到K8s上运行成为越来越自然的选择。K8s提供了统一的容器编排能力,可以同时管理在线服务和离线任务。K8s的调度器可扩展性强,支持自定义调度策略。K8s的生态丰富,与监控、日志、网络等基础设施的集成更加顺畅。

2.2 Spark on K8s的架构特点

Spark on K8s的架构与YARN模式有显著不同。在K8s模式下,Spark Driver和Executor都运行在Pod中,由K8s负责Pod的调度和生命周期管理。Spark Driver Pod负责创建和管理Executor Pod,Executor Pod完成具体的计算任务。

这种架构带来了几个重要变化。首先,资源申请不再通过YARN的ResourceManager,而是直接向K8s的API Server提交Pod。其次,Spark的动态资源分配需要与K8s的调度器协同工作,Executor的创建和销毁由Driver动态决定。第三,Shuffle数据的存储和管理需要额外的设计,因为K8s Pod的存储是临时的。

2.3 为什么选择K8s作为统一调度底座

选择K8s作为统一调度底座,核心考量是统一资源池和统一调度。在混部场景下,在线服务和离线任务必须运行在同一套资源池中,才能实现资源的动态调配。K8s的命名空间、资源配额、优先级和抢占机制,为混部提供了基础能力。

K8s的QoS等级机制为不同优先级的Pod提供了资源保障。Guaranteed级别的Pod获得最高保障,Burstable级别的Pod可以超卖但可能被驱逐,BestEffort级别的Pod没有任何保障。通过将在线服务设置为高优先级,离线任务设置为低优先级,可以在资源紧张时优先保障在线服务。

K8s的驱逐机制允许在节点资源不足时,优先驱逐低优先级的Pod,释放资源给高优先级的Pod。这一机制是混部稳定性的关键保障。

三、混部架构设计

3.1 集群规划与节点池划分

vivo大数据平台的混部集群采用统一的节点池设计,所有节点都同时承载在线服务和离线任务。但在实际部署中,根据节点硬件配置和网络拓扑,将节点划分为几个逻辑节点池。

高性能节点池配备高主频CPU和大容量内存,优先承载在线服务。通用节点池配备均衡的CPU和内存配置,承载大部分离线任务。GPU节点池配备GPU资源,承载需要GPU加速的在线推理和离线训练任务。

节点池的划分不是硬隔离,而是通过调度器的亲和性和反亲和性策略,引导Pod到合适的节点上运行。在线服务优先调度到高性能节点池,离线任务优先调度到通用节点池,但在资源紧张时可以跨池调度。

3.2 资源隔离机制

资源隔离是混部稳定性的基础。在K8s中,资源隔离通过多个层面实现。

CPU隔离方面,使用cgroup的CPU配额和权重机制。在线服务的Pod设置较高的CPU权重,离线任务的Pod设置较低的CPU权重。当节点CPU资源紧张时,内核会按照权重比例分配CPU时间片,保障在线服务获得足够的CPU资源。

内存隔离方面,通过cgroup的内存限制和内存回收机制实现。在线服务设置较高的内存限制和较低的内存回收优先级,离线任务设置较低的内存限制和较高的回收优先级。当节点内存不足时,内核会优先回收离线任务的内存。

IO隔离方面,使用cgroup的blkio控制器限制离线任务的磁盘IO带宽。在线服务的磁盘IO优先级高于离线任务,确保在线服务的数据库查询和日志写入不受离线任务的IO争抢影响。

网络隔离方面,通过带宽限制和流量优先级标记,保障在线服务的网络延迟和带宽。离线任务的大数据Shuffle传输在带宽紧张时主动降速。

3.3 优先级与抢占策略

K8s的PriorityClass机制为混部提供了优先级管理能力。vivo大数据平台定义了多个优先级等级。

最高优先级保留给核心在线服务,如推荐、搜索、支付等。这些Pod一旦被调度,除非节点故障,否则不会被驱逐。

次高优先级分配给一般在线服务和关键离线任务。这些Pod在资源紧张时可能被驱逐,但优先级高于普通离线任务。

普通优先级分配给大部分离线任务。这些Pod在资源紧张时会被优先驱逐,以保障更高优先级的服务。

最低优先级分配给可中断的离线任务,如日志分析、数据清理等。这些任务可以随时被驱逐,对业务影响最小。

抢占策略的核心是在资源不足时,调度器可以选择驱逐低优先级的Pod,腾出资源给高优先级的Pod。K8s的调度器支持抢占,但在混部场景下需要谨慎配置。过度抢占会导致离线任务频繁重启,影响计算效率。vivo的做法是设置抢占的冷却时间和抢占预算,限制单位时间内的抢占次数。

3.4 弹性资源池与超卖

弹性资源池是混部架构的核心组件。它维护了一个逻辑上的资源池,将在线服务的预留资源与离线任务的可借用资源统一管理。

在线服务在部署时声明其资源需求,包括CPU、内存和GPU。弹性资源池将这些需求记录为预留资源,确保在线服务在任何时候都能获得这些资源。但在线服务的实际使用率往往低于预留值,弹性资源池将预留资源与实际使用之间的差额识别为可借用资源。

离线任务提交时,弹性资源池根据当前可借用资源的数量决定是否接受任务。如果可借用资源充足,任务立即启动;如果不足,任务进入排队等待。当在线服务的使用率上升时,弹性资源池会通知离线任务主动释放资源,或通过驱逐机制强制回收。

超卖是弹性资源池的关键策略。在线服务的CPU预留通常基于峰值负载,而实际平均负载远低于峰值。通过设置合理的超卖比例,可以将可借用资源的规模放大,进一步提升离线任务的吞吐量。vivo在实践中将CPU超卖比例设置为一点五到二点零,内存超卖比例设置为一点一到一点三,在保证在线服务稳定的前提下最大化资源利用率。

四、关键挑战与解决方案

4.1 资源竞争与性能干扰

资源竞争是混部面临的首要挑战。即使有cgroup隔离,CPU缓存、内存带宽、网络带宽等共享资源仍然可能产生争抢。离线任务的大量内存访问可能挤占在线服务的CPU缓存,导致在线服务的指令命中率下降。离线任务的网络传输可能占用带宽,增加在线服务的网络延迟。

vivo的解决方案是引入干扰检测与动态调节机制。在每个节点上部署轻量级的监控代理,实时采集在线服务的延迟指标和资源使用情况。当检测到在线服务的延迟升高超过阈值时,监控代理会自动降低同节点上离线任务的CPU权重和IO带宽,直到在线服务恢复正常。

这种动态调节机制比静态的资源限制更灵活。在在线服务负载较低时,离线任务可以获得更多资源,提高计算效率。在在线服务负载升高时,离线任务主动让出资源,保障在线服务的稳定性。

4.2 Spark动态资源分配的适配

Spark的动态资源分配机制在YARN上运行良好,但在K8s上需要适配。在YARN模式下,Executor的创建和销毁由YARN的NodeManager管理。在K8s模式下,Executor Pod的创建和销毁由Spark Driver通过K8s API完成。

动态资源分配的核心是Executor的弹性伸缩。当Spark任务需要更多计算资源时,Driver向K8s申请新的Executor Pod。当任务负载下降时,空闲的Executor Pod被释放。这一过程需要与K8s的调度器协同,确保新创建的Executor Pod能够及时获得资源。

vivo在实践中优化了动态资源分配的响应速度。默认的Executor申请间隔较长,在混部场景下容易导致资源申请不及时。通过调整Spark的配置参数,缩短Executor的申请间隔和释放延迟,使资源能够更快地响应任务负载变化。

4.3 Shuffle服务的稳定性

Shuffle是Spark任务中最容易出问题的环节。在K8s环境下,Executor Pod的临时存储可能被回收,导致Shuffle数据丢失。如果Shuffle数据丢失,下游任务需要重新计算,严重影响任务执行效率。

vivo的解决方案是部署独立的Shuffle Service。Shuffle Service作为DaemonSet运行在每个节点上,负责管理该节点上所有Executor的Shuffle数据。当Executor Pod结束时,Shuffle数据不会立即删除,而是由Shuffle Service继续管理,直到下游任务完成读取。

Shuffle Service还提供了Shuffle数据的本地化和缓存能力。当多个任务需要读取相同的Shuffle数据时,Shuffle Service可以复用已缓存的数据块,减少磁盘IO和网络传输。

4.4 调度延迟与任务启动速度

K8s的调度延迟是混部场景下的另一个挑战。在YARN模式下,Spark任务的启动通常在秒级完成。在K8s模式下,Pod的创建、调度、镜像拉取和启动过程可能需要数十秒甚至数分钟。

vivo通过多项优化缩短任务启动时间。首先是镜像预热,将常用的Spark镜像预拉取到所有节点,避免每次启动时从镜像仓库拉取。其次是调度器优化,为Spark任务配置专用的调度队列,减少与其他Pod的调度竞争。第三是Pod启动加速,通过优化容器运行时和初始化脚本,缩短Pod从创建到就绪的时间。

经过优化,Spark on K8s的任务启动时间从最初的分钟级降低到二十秒以内,接近YARN模式的水平。

4.5 监控与可观测性

混部环境的复杂性对监控提出了更高要求。需要同时监控在线服务的延迟、错误率和资源使用,离线任务的运行状态、资源消耗和排队情况,以及节点级别的资源竞争和干扰情况。

vivo构建了统一的监控平台,将K8s的指标、Spark的任务指标和在线服务的业务指标整合到同一个视图中。通过关联分析,可以快速定位混部环境中的性能问题。例如,当在线服务的延迟升高时,可以查看同节点上是否有离线任务在大量消耗资源。当离线任务运行缓慢时,可以查看是否被在线服务挤压了资源。

五、vivo的实战优化

5.1 自定义调度器与资源画像

K8s默认的调度器面向通用场景,对混部场景的优化有限。vivo基于K8s的调度器框架开发了自定义调度器,针对混部场景进行了多项优化。

资源画像是自定义调度器的核心能力。调度器持续采集每个节点的资源使用历史,构建节点资源的画像。画像包括CPU使用率的均值、峰值和波动范围,内存使用率的分布,以及网络和磁盘IO的特征。当调度新的Pod时,调度器根据资源画像选择最合适的节点,避免将Pod调度到已经接近满载的节点。

自定义调度器还支持任务优先级感知。高优先级的Pod可以抢占低优先级Pod的资源,但抢占决策不仅考虑优先级,还考虑被抢占Pod的运行时长和剩余工作量。长时间运行且接近完成的离线任务,被抢占的优先级会降低,以减少计算资源的浪费。

5.2 基于负载预测的弹性伸缩

混部集群的资源需求是动态变化的。在线服务的负载具有明显的周期性,白天高夜间低,工作日高周末低。离线任务的提交量也有规律,通常在凌晨和午间出现高峰。

vivo引入了基于负载预测的弹性伸缩机制。通过分析历史负载数据,建立时间序列预测模型,预测未来一段时间内在线服务和离线任务的资源需求。根据预测结果,提前调整资源分配策略。例如,预测到在线服务即将进入低谷期时,提前释放更多资源给离线任务;预测到在线服务即将进入高峰期时,提前缩减离线任务的资源配额。

负载预测的引入使资源调度从被动响应转变为主动规划,进一步提升了资源利用率和稳定性。

5.3 分级QoS与驱逐机制

vivo将混部集群中的工作负载分为多个QoS等级,每个等级对应不同的资源保障和驱逐策略。

最高等级是在线核心服务,如推荐和搜索。这些服务在任何情况下都不被驱逐,资源保障级别最高。如果节点资源不足,调度器会优先将其他低等级Pod迁移到其他节点。

第二等级是在线非核心服务和关键离线任务。这些服务在资源紧张时可能被驱逐,但驱逐前会先尝试资源降级,如降低CPU权重或减少内存配额。

第三等级是普通离线任务。这些任务在资源紧张时被驱逐,但驱逐前会收到通知,允许任务优雅退出并保存中间状态。

第四等级是可中断的离线任务。这些任务可以被随时驱逐,不需要优雅退出,对业务影响最小。

驱逐机制与QoS等级联动。当节点资源不足时,kubelet按照QoS等级从低到高依次驱逐Pod。同一等级内,优先驱逐资源使用超过请求值的Pod,以及运行时间较短的Pod。

5.4 多租户配额管理

vivo大数据平台服务多个业务团队,每个团队对资源的需求和优先级不同。混部集群需要支持多租户的配额管理,确保每个团队获得公平的资源份额,同时防止某个团队过度占用资源影响其他团队。

vivo通过K8s的ResourceQuota和LimitRange实现多租户配额。每个业务团队对应一个命名空间,命名空间内设置资源配额,限制该团队可以使用的CPU和内存总量。同时设置LimitRange,限制单个Pod可以申请的资源上下限,防止任务提交时申请过大的资源。

在混部场景下,配额管理还需要考虑优先级。高优先级的在线服务团队可以获得更大的配额和更高的超卖比例,而普通离线任务团队的配额相对受限。当集群资源紧张时,低优先级团队的配额会被优先压缩。

5.5 故障隔离与自愈

混部集群的故障影响面比独立集群更大。一个节点的故障可能同时影响在线服务和离线任务。vivo通过故障隔离和自愈机制,将故障影响控制在最小范围。

节点故障时,K8s的节点控制器会将节点标记为不可用,并重新调度该节点上的Pod。对于在线服务,Pod会在其他节点上重建,服务自动恢复。对于离线任务,Spark Driver会检测到Executor丢失,重新申请Executor并继续任务执行。

网络故障是混部场景下的另一类常见问题。vivo部署了网络健康检查服务,持续监控节点之间的网络连通性和延迟。当检测到网络异常时,自动将该节点从调度池中移除,避免新Pod调度到问题节点。同时通知在线服务进行流量切换,将请求路由到健康节点。

六、落地效果

6.1 资源利用率提升数据

混部上线后,vivo大数据平台的资源利用率显著提升。在线集群的CPU平均利用率从混部前的百分之三十五提升到百分之六十五以上,离线任务的资源获取能力提升了约两倍。整体集群的CPU利用率稳定在百分之六十到百分之七十五之间,较混部前的百分之三十左右实现了翻倍增长。

内存利用率同样改善明显。在线服务的内存预留通常基于峰值,实际使用率较低。混部后,离线任务可以利用这部分空闲内存,整体内存利用率从百分之四十提升到百分之七十。

6.2 成本节约

资源利用率的提升直接转化为成本节约。在支撑相同业务规模的前提下,混部减少了对独立离线集群的硬件采购需求。初步测算,混部方案为vivo大数据平台节省了约百分之三十的硬件采购成本和百分之二十五的机房电力成本。

更重要的是,混部提升了平台的弹性能力。在大促等流量高峰期间,在线服务可以快速驱逐离线任务,释放资源应对流量冲击,无需提前采购大量备用硬件。这种弹性能力带来的成本节约是难以精确量化的,但对业务的保障价值极高。

6.3 任务性能对比

在混部环境下,离线任务的性能表现与独立集群相比有所变化。由于资源争抢的存在,单个离线任务的执行时间可能略有增加,平均增加约百分之十到百分之十五。但考虑到混部后集群整体资源规模更大,任务排队时间大幅减少,任务从提交到完成的端到端时间反而缩短了约百分之二十。

Spark任务的稳定性也得到保障。通过Shuffle Service和动态资源分配的优化,任务失败率控制在百分之一以内,与独立集群持平。

6.4 稳定性指标

混部最关键的指标是在线服务的稳定性。混部上线以来,在线服务的P99延迟没有出现显著变化,错误率保持在百万分之一以下。在多次流量高峰期间,混部系统通过自动驱逐离线任务,保障了在线服务的资源供应,没有发生因混部导致的在线服务故障。

七、经验总结与最佳实践

7.1 混部不是简单的资源叠加

混部的本质是在共享资源上实现隔离与调度,而不是简单地将两类业务部署在一起。如果缺乏精细的资源隔离和优先级控制,混部会导致在线服务被离线任务干扰,稳定性下降。vivo的实践经验表明,混部成功的关键在于隔离机制、调度策略和监控体系的协同设计,三者缺一不可。

7.2 渐进式推进策略

混部的推进需要循序渐进。vivo采用了三步走的策略。第一步是试点,选择非核心在线服务和非关键离线任务进行混部,验证技术方案的可行性和稳定性。第二步是扩大范围,将更多在线服务和离线任务纳入混部,同时完善监控和告警体系。第三步是全量混部,将所有适合混部的业务迁移到统一集群,关闭独立集群。

渐进式推进的好处是风险可控。每一步都有回退方案,一旦出现问题可以快速恢复。同时,每一步的实践都为下一步积累了经验和数据。

7.3 组织协同与流程保障

混部不仅是技术变革,也涉及组织协同。在线服务团队和离线计算团队需要共同制定资源使用规范,明确优先级规则和抢占策略。运维团队需要建立混部环境的专项监控和应急响应流程。平台团队需要持续优化调度算法和隔离机制。

vivo建立了跨团队的混部工作组,定期评审混部运行情况,协调资源分配和策略调整。这种组织保障是混部长期稳定运行的基础。

7.4 未来演进方向

混部技术的演进仍在继续。vivo正在探索几个方向。第一是更精细的资源隔离,利用Intel RDT和AMD QoS等硬件特性,实现CPU缓存和内存带宽的隔离。第二是更智能的调度,引入机器学习模型预测任务资源需求和运行时间,优化调度决策。第三是跨集群混部,将多个K8s集群纳入统一的资源池,实现更大范围的资源调度。

结语

Spark on K8s在vivo大数据平台的混部实战,是一次从资源孤岛走向资源池化的深度实践。通过统一的K8s调度底座、精细的资源隔离、优先级抢占和弹性资源池,vivo将在线服务和离线任务融合在同一批物理资源上,实现了资源利用率翻倍、硬件成本显著降低、弹性能力大幅提升的综合收益。

混部的核心挑战不在于技术组件的堆叠,而在于对资源竞争、任务调度和稳定性的精细控制。vivo的实践表明,只要隔离机制到位、调度策略合理、监控体系完善,混部完全可以在保障在线服务质量的前提下,释放出巨大的资源效率红利。对于正在探索混部技术的大数据平台团队,这份实战记录提供了一条可参考的路径,也揭示了从技术方案到组织协同的系统性要求。