本文来自 eBay 软件工程师、Apache Kyuubi PPMC Member王斐在Apache SeaTunnel & Kyuubi 联结 Meetup的分享,介绍了Apache Kyuubi(Incubating)的根本架构和应用场景,eBay基于本身的需要对Kyuubi所做的加强,以及如何基于Kyuubi构建Unified & Serverless Spark Gateway。
Kyuubi是什么
首先介绍一下Kyuubi。Kyuubi是一个分布式的Thrift JDBC/ODBC server,反对多租户和分布式等个性,能够满足企业内诸如ETL、BI报表等大数据场景的利用。我的项目由网易数帆发动,曾经进入Apache基金会孵化,目前的次要方向是依靠自身的架构设计,围绕各类支流计算引擎,打造一个Serverless SQL on Lakehouse服务,目前反对的引擎有Spark、Flink、Trino(也就是Presto)。我明天的主题是围绕Kyuubi和Spark, 对于其它计算引擎这里不再开展。
对于Spark,Kyuubi有HiveServer2的API,反对Spark多租户,而后以Serverless的形式运行。HiveServer2是一个经典的JDBC服务,Spark社区也有一个相似的服务叫做Spark Thrift Server。这里介绍一下Spark Thrift Server和Kyuubi的比照。
Spark Thrift Server能够了解为一个独立运行的Spark app,负责接管用户的SQL申请, SQL的编译以及执行都会在这个app外面去运行,当用户的规模达到肯定的级别,可能会有一个单点瓶颈。
对于Kyuubi,咱们能够看左边这张图,有一个红色的用户和一个蓝色的用户,他们别离有一个红色的Spark app和一个蓝色的Spark app,他们的SQL申请进来之后,SQL的编译和执行都是在对应的app之上进行的,就是说Kyuubi Server只进行一次SQL申请的直达,把SQL间接发送给背地的Spark app。
对于Spark Thrift Server来讲,它须要保留后果以及状态信息,是有状态的,所以不能反对HA和LB。而Kyuubi不保留后果,简直是一个无状态的服务,所以Kyuubi反对HA和LB,咱们能够减少Kyuubi Server的个数来满足企业的需要。所以说Kyuubi是一个更好的Spark SQL Gateway。
Kyuubi的架构分为两层,一层是Server层,一层是Engine层。Server层和Engine层都有一个服务发现层,Kyuubi Server层的服务发现层用于随机抉择一个Kyuubi Server,Kyuubi Server对于所有用户来共享的。Kyuubi Engine层的服务发现层对用户来说是不可见的,它是用于Kyuubi Server去抉择对应的用户的Spark Engine,当一条用户的申请进来之后,它会随机抉择一个Kyuubi Server,Kyuubi Server会去Engine的服务发现层抉择一个Engine,如果Engine不存在,它就会创立一个Spark Engine,这个Engine启动之后会向Engine的服务发现层去注册,而后Kyuubi Server和Engine之间的再进行一个Internal的连贯,所以说Kyuubi Server是所有用户共享,Kyuubi Engine是用户之间资源隔离。
Kyuubi反对一些Engine的共享级别,它是基于隔离和资源之间的均衡。在eBay咱们次要应用到了USER 和CONNECTION级别。首先对于CONNECTION级别,对于用户的每次连贯都会发明一个新的app,也就是一个Kyuubi Engine,实用于ETL场景,ETL的workload比拟高,须要一个独立的app去执行;对于USER级别,咱们能够看到这里有两个user,一个叫Tom,一个叫Jerry,Tom的两个client连贯Kyuubi Server,会连贯到同一个属于Tom的Kyuubi Engine,USER级别实用于ad-hoc场景,就是对于同一个用户所有的连贯都会到同一个Kyuubi Engine去执行,而对Jerry的所有申请都会到Jerry的Kyuubi Engine去执行。
对USER共享级别Kyuubi做了一些增强,引入了一个Engine POOL的概念,就像编程外面的线程池一样,咱们能够创立一个Engine的pool,pool外面有编号,比如说这里Tom创立了两个pool,叫做pool-a和pool-b,编号为pool-a-0,pool-a-1,如果说在客户端申请的时候间接指定这个pool的名字,Kyuubi server会从这个pool外面去随机抉择一台Engine执行;如果Tom在申请的时候不仅指定pool的名字,还指定了这个Engine在pool外面的索引,比如说这里指定pool-b-0,Kyuubi Server会从这个pool-b外面抉择编号为0的Engine去做计算。对应的参数为kyuubi.engine.share.level.subdomain
.
这在eBay外面为BI工具集成提供了很大的便当,因为eBay,每个分析师团队可能用同一个账号去执行数据分析,BI工具会依据用户的IP去创立一个Kyuubi Engine,因为每个分析师须要的参数配置可能是不一样的,比如说他们的内存的配置是不一样的,BI工具就能够创立一个这样的engine pool,而后保留用户的IP和所创立Engine 索引的一个mapping,而后在这个用户的申请过去的时候,依据BI工具保留的IP映射关系,去找到该用户所创立的Engine,或者是说一个团队外面很多人应用一个pool,能够预创立许多Spark app,让这一个组外面的人能够随机抉择一个Engine去做执行,这样能够加大并发度。 同时也能够作为USER共享级别上面的一个标签用于标注该引擎的用处,比如说咱们能够给beeline场景和java JDBC利用应用场景创立USER共享级别下的不同engine pool,在不同应用场景下应用不同的engine pool, 相互隔离。
后面提到了不同的Engine共享级别,有的是为每个连贯创立一个Spark App,有的是为一个用户创立一个或者多个Kyuubi Engine,你可能会放心资源节约,这里讲一下Kyuubi Engine对资源的动静治理。首先,一个Kyuubi Engine,也就是一个Spark app,分为Spark driver和Spark executor,对于executor,Spark自身就有一个executor dynamic allocation机制,它会依据以后Spark job的负载来决定是否向集群申请更多的资源,或者是说将目前已申请的资源返还给集群。所说咱们在Kyuubi Server层加一些限度,比方强制关上这个executor dynamic allocation,并且把在闲暇时候最小的executor数量设为0,也就是说当一个app十分闲暇的时候只有一个driver带运行,避免浪费资源。除了executor层的动静回收机制,Kyuubi 为driver层也加了资源回收机制。对于CONNECTION分享级别,Kyuubi Engine只有在以后连贯才应用,当连贯敞开的时候Spark driver会间接被回收掉。对USER级别的共享,Kyuubi 有一个参数kyuubi.session.engine.idle.timeout
来管制engine的最长闲暇工夫,比如说咱们将闲暇工夫设置为12小时,如果12个小时之内都没有申请连贯到这个Spark app上,这个Spark app就会主动完结,防止资源节约。
Kyuubi的应用场景
上面讲一下Use Case。目前Kyuubi反对了SQL语言和Scala语言的执行,也能够把SQL和Scala写在一起去跑。因为SQL是一种十分用户敌对的语言,它能够让你不必理解Spark外部的原理,就能够应用简略的SQL语句去查问数据,然而它也有肯定的局限性;而Scala语言须要肯定的门槛,但它十分的灵便,咱们能够去写代码或者去操纵一些Spark DataFrame API。
举一个例子,就是能够在一个SQL文件或者一个notebook外面去混合编程。首先用SQL语句创立了一张训练的数据表,在创立表之后通过SET语句把语言模式设为Scala,而后开始用Scala去写代码,这里是用一个kMeans把训练数据进行解决,解决完之后把输入保留在一张表外面,再把语言模式切换到SQL,持续用SQL去解决。这样十分不便,咱们能够联合SQL、Scala的长处,基本上能够解决数据分析外面的大部分的case。咱们也在Kyuubi JDBC外面提供了一个十分敌对的接口,能够间接调用KyuubiStatement::ExecuteScala
去执行Scala语句。
Kyuubi在eBay的实际
eBay需要背景
咱们Hadoop team治理了很多个Hadoop集群,这些集群散布在不同的数据中心,有不同的用处,有一套对立的基于KDC和LDAP的权限校验。
刚开始引入Kyuubi的时候,咱们就在想要为每个集群都部署一个Kyuubi服务吗?如果这样咱们可能要部署三四个Kyuubi服务,在降级的时候须要进行反复操作,治理起来很不不便,所以咱们就想是否可能用一套Kyuubi来服务多个Hadoop集群。
eBay对Kyuubi的加强
下图就是咱们为这个需要所做的一些加强。首先,因为咱们是反对KDC和LDAP认证的,咱们就让Kyuubi同时反对Kerberos和Plain类型的权限认证,并对Kyuubi Engine的启动、Kyuubi的Beeline做了些优化,而后咱们扩大了一些Kyuubi的thrift API反对上传下载数据。针对后面说的要用一个Kyuubi去拜访多个Hadoop集群,咱们加了一个cluster selector的概念,能够在连贯的时候指定参数,让申请路由到对应的集群。还有就是咱们也在欠缺RESTfull API,曾经为Kyuubi反对了SPNEGO和BASIC 的RESTfull API权限认证。此外咱们也在做RESTfull API去跑SQL Query 和 Batch job的一些工作。图中打编号的是曾经回馈社区的一些PR。
这里讲一下2、3、4,对Kyuubi的Thrift RPC的一些优化。首先因为Thrift RPC自身是针对HiveServer2来设计的,HiveServer2/Spark Thriftserver2外面建设一个连贯是十分快的。而在Kyuubi外面建设一个连贯的话,首先要连贯到Kyuubi Server,Kyuubi Server要等到和远端的Kyuubi Engine建设连贯实现之后,能力把后果返回给客户端。
如果Kyuubi Engine一开始不存在,而且在启动Kyuubi Engine的时候因为资源问题,或者是有一些参数设置不对,比如说他设置了有效的spark.yarn.queu
,导致呈现谬误的话,两头可能会有一分钟或者说几十秒的提早,客户端要始终等,在等的过程中也没有任何的log返回给客户端。咱们就针对这个做了一些异步的OpenSession,将OpenSession分为两局部,第一步是连贯到Kyuubi Server,Kyuubi Server再异步启动一个LaunchEngine Operation,之后立刻把Kyuubi Server连贯给客户端,这样客户端能够做到一秒钟就能够连贯到Kyuubi Server。然而他的下条语句以及进来之后,会始终等到这个Engine初始化好之后才开始运行。其实这也是咱们的PM的一个需要,即便第一条语句运行工夫长一点也没关系,然而连贯是肯定要很快,所以咱们就做了这样一个工作。
因为Hive Thrift RPC是一个广泛应用而且十分用户敌对的RPC,所以咱们在不毁坏它的兼容性的状况下基于Thrift RPC做了一些扩大。首先对于ExecuteStatement这种申请及返回后果,它会在API外面返回一个OperationHandle,再依据OperationHandle获取以后Operation的状态和日志。因为咱们后面曾经把OpenSession拆成了OpenSession加上一个LaunchEngine Operation,所以咱们想把LaunchEngine Operation的一些信息,通过OpenSession request这个configuration map把它返回去,咱们是把一个OperationHandler分为两局部,一部分是guid,另一部分是secret,放到OpenSessionResp 的configuration Map外面。
而后在拿到OpenSessionResp之后就能够依据这个configuration拼出Launch Engine Operation对应的OperationHandler,而后再依据它去拿这个LaunchEngine的日志和状态。
上面是一个成果,咱们在建设Kyuubi连贯的时候能够实时晓得spark-submit的过程中到底产生了什么。比如说用户将spark.yarn.queue
设置错了,或者说因为资源问题始终在期待,都能够分明的晓得这两头产生了什么,不须要找平台保护人员去看日志,这样既让用户感到极为敌对,也缩小了平台保护人员的efforts。
构建Unified & Serverless Spark Gateway
后面说到要用一个Kyuubi服务来服务多个集群,咱们就基于Kyuubi构建了一个Unified & Serverless Spark Gateway。Unified是说咱们只有一个endpoint,咱们是部署在Kubernetes之上的,应用Kubernetes的LB作为Kyuubi Server的服务发现,endpoint的模式就是一个Kubernetes的LB加上一个端口,比如说kyuubi.k8s-lb.ebay.com:10009
,要服务多个集群,咱们只须要在JDBC URL外面加上一个参数kyuubi.session.cluster
,指定cluster name,就能够让他的申请到指定的集群去执行。对于权限校验咱们也是用Unified的,同时反对Kerberos和LDAP权限校验。对于functions(性能)也是Unified的,同时反对Spark-SQL、Spark-Scala以及ETL Spark Job的提交。
对于Serverless, Kyuubi Server部署在Kubernetes之上,是Cloud-native的,而且Kyuubi Server反对HA和LB,Kyuubi Engine反对多租户,所以对于平台保护人员来说老本非常低。
这是咱们大略的一个部署,对于多集群咱们引入了Cluster Selector的概念,为每个集群都配了一个Kubernetes ConfigMap文件,在这个文件外面有这个集群所独有的一些配置,比方这个集群的ZooKeeper的配置,集群的环境变量,会在启动Kyuubi Engine的时候注入到启动的过程外面。
每个集群的super user的配置也是不一样的,所以咱们也反对了对各个集群进行super user的校验。目前Kyuubi反对HadoopFSDelegation token和HiveDelegation token的刷新,能够让Spark Engine在没有keytab的状况上来长运行,而不必放心token过期的问题。咱们也让这个性能反对了多集群。
用户一个申请进来的过程是这样的:首先他要指定一个Cluster Selector,Kyuubi Server(on Kubernetes)依据这个Selector去找到对应的集群,连贯集群的ZooKeeper,而后查找在ZooKeeper外面有没有对应的Spark app, 如果没有就提交一个app到YARN下面(Spark on YARN),Engine在启动之后会向ZooKeeper注册,Kyuubi Server和Kyuubi Engine通过ZooKeeper找到Host和Port并创立连贯。
Kyuubi从一开始就反对Thrift/JDBC/ODBC API, 目前社区也在欠缺RESTFul API. eBay也在做一些欠缺RESTFul API的工作,咱们给RESTful API加了权限校验的反对,同时反对SPNEGO(Kerberos)和BASIC(基于明码)的权限校验。咱们打算给Kyuubi减少更多的RESTful API。目前已有的是对于sessions的API,比方能够关掉session,次要用于来治理Kyuubi的sessions。咱们筹备加一些针对SQL以及Batch Job的API。对于SQL就是能够间接通RESTful API提交一条SQL Query,而后能够拿到它的后果以及log。对于Batch Job就是能够通过RESTful API提交一个一般的应用JAR来运行的Spark app,而后能够拿到Spark app的ApplicationId,还有spark-submit的log,这样能够让用户更加不便地应用Kyuubi实现各种罕用的Spark操作。
eBay的收益
对用户来说,他们可能十分不便地应用Spark服务,能够应用Thrift、JDBC、ODBC、RESTful的接口,它也是十分轻量的,不须要去装置Hadoop/Spark binary,也不须要治理Hadoop和Spark的Conf,只须要用RESTful或者Beeline/JDBC的模式去连贯就好。
对咱们平台开发团队来说,咱们有了一个中心化的Spark服务,能够提供SQL、Scala的服务,也能够提供spark-submit的服务,咱们能够不便地治理Spark版本,不须要将Spark安装包分发给用户去应用,能够更好地实现灰度降级,让Spark版本对于用户透明化,让整个集群应用最新的Spark,也节俭集群的资源,节俭公司的老本。此外,平台的保护也是比拟不便的,老本很低,因为咱们只须要保护一个Kyuubi服务来服务多个Hadoop集群。
作者:王斐,eBay 软件工程师,Apache Kyuubi PPMC Member
附视频回放及PPT下载:
Apache Kyuubi 在 eBay 的实际 -王斐
延长浏览:
Apache Kyuubi 在 T3 出行的深度实际
Who is using Apache Kyuubi (Incubating)?
Kyuubi我的项目主页
Kyuubi代码仓库