Vineyard是一个专为云原生环境下大数据分析场景中端到端工作流提供内存数据共享的分布式引擎,我们很高兴宣布Vineyard在年4月27日被云原生基金会(CNCF)TOC接受为沙箱(Sandbox)项目。同时,Vineyard作为GraphScope的底层存储引擎,负责图数据在各个计算引擎之间的共享,提升图计算端到端的整体性能。
项目介绍
现有的大数据分析场景中,对于端到端任务,不同的子任务之间通常使用例如HDFS、S3、OSS这样的分布式文件系统或对象存储系统来共享任务之间的中间数据,这种方式在运行效率和研发效率上存在诸多问题,以下图所示的一个风控作业工作流为例:
1.工作流中不同任务之间为了共享中间数据,前一个任务将结果写入文件系统,完成之后,后一个再将文件读出作为输入,这个过程带来了额外的序列化及反序列化、内存拷贝、以及网络、IO的开销,我们从历史任务中观察到有超过60%的任务为此花费了40%以上的执行时间;
2.对于生产环境,为了高效地解决某一个特定范式的问题往往会引入一个新系统(例如分布式图计算),但这样的系统往往难以直接与工作流中的其他系统无缝衔接,需要很多重复的IO、数据格式转换和适配的研发工作;
3.使用外部文件系统共享数据给工作流带来了额外的中断,因为往往只有当一个任务完全写完所有结果,下一个任务才能开始读取和计算,这使得跨任务的流水线并行无法被应用;
4.现有的分布式文件系统在共享中间数据时,特别是在云原生环境下,并没有很好的处理分布式数据的位置问题,造成网络开销的浪费,从而降低端到端执行效率。
为了解决现有大数据分析工作流中存在的上述问题,我们设计和实现了分布式内存数据共享引擎Vineyard。
Vineyard从以下四个方面来应对上述几个问题:
1.为了使端到端工作流中任务之间的数据共享更加高效,Vineyard通过内存映射的方式,支持系统间零拷贝的数据共享,省去了额外的IO开销;
2.为了简化新计算引擎接入现有系统所需要的适配和开发,Vineyard对常见的数据类型,提供了开箱即用的抽象,例如Tensor、DataFrame、Graph,等等,从而不同计算引擎之间共享中间结果不再需要额外的序列化和反序列。同时,Vineyard将IO、数据迁移、快照等可复用的组件以插件的形式实现,使其能够很灵活地按需注册到计算引擎中去,降低与计算引擎本身无关的开发成本;
3.Vineyard提供一系列operators,来实现更高效灵活的数据共享。例如Pipelineoperator实现了跨任务的流水线并行,使得后续任务可以随着前序任务输出的产生,同时进行计算,提高了端到端整体效率;
4.Vineyard与Kubernetes集成,通过SchedulerPlugin,让任务的调度能够感知所需要的数据的局部性,在Kubernetes让单个任务的Pod尽可能地调度到与Pod所需的输入数据对其的机器上,来减小数据迁移需要的网络开销,提升端到端性能。
在初步的对比实验中,相比于使用HDFS来共享中间数据,对于评测任务,Vineyard能够大幅降低用于交换中间结果引入的额外开销,对于整个工作流的端到端时间有1.34倍的提升。
核心功能
接下来从Vineyard核心的设计与实现,以及Vineyard如何助力云原生环境中大数据分析任务两个方面来介绍Vineyard的核心功能。
分布式内存数据共享
Vineyard将内存中的数据表示为Object。Object可以是Local的,也可以是Global的,以分布式执行引擎Mars和Dask为例,一个DataFrame往往被拆分成很多个Chunk以利用多台机器的计算能力,每台机器上有多个Chunk,这些Chunk是Vineyard中的LocalObject,这些Chunk一起构成了一个全局的视图,即GlobalDataFrame。这个GlobalDataFrame能够直接共享给其他计算引擎,如GraphScope,作为图数据的输入。有了这些数据类型的抽象,Vineyard上的不同计算引擎之间就可以无缝地共享中间结果,将一个任务的输出直接用作下一个任务的输出。
更具体地,Vineyard中又是如何表达一个特定类型的Object,使之能够很容易地适配到不同的计算引擎中去呢?这得益于Vineyard在Object的表示上提供的灵活性。Vineyard中,一个Object包括两个部分,Metadata,以及一组Blob。Blob中存储着实际的数据,而Metadata则用于解释这些Blob的语义。例如对于Tensor,Blob是一段连续内存,存储着Tensor中所有的元素,而Metadata中记录了Tensor的类型、形状、以及行主序还是列主序等属性。在Python中,这个Object可以被解释为一个Numpy的NDArray,而在C++中,这个Object可以被解释为一个xtensor中的tensor。这两种不同编程语言的SDK中,共享这个Tensor不会带来额外的IO、拷贝、序列化/反序列化、以及类型转换的开销。
同时,Vineyard中的Metadata是可嵌套的,这使得我们通过很容易地将任何复杂的数据类型描述为Vineyard中的Object,不会限制计算引擎的表达能力。以GlobalDataFrame为例,见下图中Metadata的结构。
云原生环境中数据与任务的协同调度
对于一个真实部署的大数据分析流水线,仅仅有任务之间的数据共享是远远不够的。在云环境中,一个端到端流水线中包含的多个子任务在被Kubernetes调度时仅仅考虑了需要的资源约束,连续的两个任务的co-locate无法保证,在两个任务之间共享中间结果时仍然有数据迁移引入的网络开销,如下图,在运行TaskB时,因为两个任务的Pod没有对齐,数据分片A3、A4需要被迁移到Pod所在的Vineyard实例上。
对此,Vineyard通过CRD将集群中的数据(VineyardObjects)表示为可观测的资源,并基于Kubernetes的SchedulerFramework设计和实现了一个考虑数据局部性的调度器插件。当前一个任务TaskA完成后,从结果对象的Metadata中,调度器插件可以知道所有分片的位置,在启动下一个任务时,调度器给数据所在的节点(图中的Node1、Node2)更高的优先级,使任务TaskB也尽可能地被调度到对应的节点上,从而省去了数据迁移引入的额外开销,来改善端到端的性能。
快速上手
Vineyard集成了Helm以方便用户安装和部署:
helmrepoaddvineyard