Apache Kylin Spark Cubing on Kubernetes 初探

背景介绍

相信凡是关注容器生态圈的人都不会否认,Kubernetes 已经成为容器编排调度的实际标准,不论 Docker 官方还是 Mesos 都已经支持了 Kubernetes。Spark 从2.3.0版本开始,也开始支持将任务提交至 Kubernetes 上进行计算。目前,Kylin Master 分支上的 Spark 版本也已经更新至了2.3.2,因此我决定尝试一下将 Kylin 的 Spark 任务提交至 Kubernetes 上进行计算,以验证 Kylin Spark Cubing on Kubernetes 的可行性。

什么是Kubernetes?

那么什么是 Kubernetes 呢?Kubernetes,简称K8s,是一个由 Google 开源的容器编排工具,用于自动化部署、扩展和管理容器化的应用程序。

Kubernetes 具备完善的集群管理能力,包括多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和服务发现机制、内建负载均衡器、故障发现和自我修复能力、服务滚动升级和在线扩容、可扩展的资源自动调度机制、多粒度的资源配额管理能力。还提供完善的管理工具,涵盖开发、部署测试、运维监控等各个环节。

Kubernetes这个名字源于希腊语,是舵手的意思,所以它的Logo既像一张渔网,又像一个罗盘。有意思的是 Docker 的 Logo 为驮着集装箱在大海上遨游的鲸鱼,Kubernetes 与 Docker 的关系可见一斑。

这里简单的介绍一下 Kubernetes 中的一个重要的概念,叫做 Pod。 Pod 是 Kubernetes 的最小工作单元。每个 Pod 运行着一个或多个容器,Pod 中的容器会作为一个整体被 K8s Master 调度到一个节点上运行。由于 K8s 本身的概念和组件都比较多,因此不在本文详细展开,感兴趣的同学可以查阅官方文档。

为什么要Spark run on Kubernetes?

当 Spark run on Kubernetes 时, Kubernetes 的角色是一个集群管理器,就如下图所示。

此时,Spark 能够利用 Kubernetes 的生态系统的特性以及所带来的优势。比如,可以直接利用 Kubernetes 的命名空间和资源配额对集群进行多租户设置和共享、可插拔授权和日志记录等管理功能;最重要的是,它不需要在 Kubernetes 集群上进行任何更改或新安装; 只需创建一个容器映像并为您的 Spark 应用程序设置正确的 RBAC角色即可。当您使用 Kubernetes 作为集群管理器时,您将与日志记录和监视解决方案无缝集成。

此外社区还在探索高级用例,例如管理流工作负载和利用服务网格等Istio。

如何将Spark job运行在Kubernetes上?

正确安装并配置 Spark 2.3+、和 Kubernetes(1.7+)。在安装 Kubernetes 的过程中,Docker 会从 Google 官方拉取镜像。Kubernetes 的官方镜像都是托管在 Google 自家的镜像服务上,因为众所周知的原因,在国内直接拉取几乎是不可能的,因此你得先让你的 linux 机器翻墙;还有一种解决方案是使用阿里云容器镜像服务来获取。具体方式不在此赘述,接下来直接介绍如何 run Spark job on Kubernetes。

一、制作镜像

Spark on Kubernetes 的本质是将任务打包放在由 K8s 管理的容器中运行,因此需要将任务打包成Docker image。

Spark 的 bin 目录下提供了docker-image-tool.sh脚本,用来制作 Docker image。该脚本默认会使用$SPARK_HOME/kubernetes/dockerfiles/spark/Dockerfile来构建镜像。以Spark的example程序为例,直接输入命令./bin/docker-image-tool.sh -t botcc -t spark-examplebuild。命令执行完毕后,能在 Docker 下查到这个镜像,说明构建成功,如下图。

现在我们来看一下这个Dockerfile:

原理很简单,就是将 spark 的运行时环境(jars、bin、conf等)和任务(examples)直接复制到镜像中即可。

二、上传镜像

镜像制作完毕后,使用./bin/docker-image-tool.sh -r botcc -t spark-example push命令推送至 Docker Hub 上。为什么呢?因为将 Spark 任务提交至 Kubernetes 后,Kubernetes 上的各节点会根据 repository name 和 tags 去寻找并下拉该镜像,以此来创建容器执行任务。若不上传,则 K8s 节点会因找不到该 image 而导致任务创建失败,无法执行。

三、提交Spark任务

Spark 向 K8s 提交任务时,命令与向 YARN 上提交相比略有改动,以提交 Spark example 程序中求 PI 值为例:

其中主要的改动是--master的参数变成了K8s master的地址;其次是指定依赖的命令变成了local://,local://相当于原先的--jar,但是它指的是容器中的jar包路径,而不是host机器上的路径!除了使用local://方式指定jar包依赖外,Spark还支持使用URL的方式来指定,详情可翻阅官方文档。--name是给spark driver pod进行命名;spark.kubernete.* 则是kubernetes相关的参数,比如权限验证等,这个根据实际情况来配置。任务提交后,查看pods的状态。

当driver pod的STATUS为ContainerCreating状态时,代表K8s的各节点正在拉取指定镜像。

当driver pod的STATUS为Running时,说明K8s的各节点拉取镜像成功,并开始创建executor pods执行计算任务,如下图。

​此时你在 K8s 的各节点上执行 Docker images 命令,发现会新增一个名为 botcc/spark:spark-example 的镜像。

当driver pod的STATUS会变成Completed时,此时任务执行成功。

四、工作流

综上,总结一下K8s提交任务后的整个工作流。

一切从用户使用spark-submit命令向K8s集群提交任务开始:

K8s 接收到任务后,scheduler启动一个spark driver pod:

Driver pod会根据需要,向scheduler申请分配若干个executor pods:

调度器生成executor pods,executor pods执行计算任务:

任务执行完成后,executorpods被销毁,资源释放;driver pods保留,并存放了log,直至K8s GC或用户手动删除。

如何将Kylin的Spark job运行在 Kubernetes上?

因为 Kylin Spark Cubing 的本质也是用 spark-submit 将任务提交至 Spark 上进行计算,并将结果写入 HBase。因此根据上述 run Spark examples on K8s 的步骤,我们举一反三,将其移植到 Kylin 的 Spark 任务上,就能实现 Kylin Spark cubing on Kubernetes。

一、添加Hadoop conf

将Hadoop conf的文件添加至$SPARK_HOME/conf目录下,使Spark Job在容器中运行时能够读取到Hadoop的配置信息。默认情况下,./bin/docker-image-tool.sh脚本会将$SPARK_HOME/conf打包进docker images中。

二、添加jar包

首先要分析Kylin Spark Cubing时,在哪些步骤提交了Spark任务,这些步骤又分别使用了哪些jar包?通过在Kylin Web UI上分析log,我们可以确定在以下三步中,Kylin提交了spark作业,分别为Step 3: Extract Fact Table Distinct Columns, Step 7: Build Cube with Spark, Step8: Convert Cuboid Data to HFile。其中Step 3、Step 7、Step 8都依赖kylin-job-<version>.jar; 除此之外Step8还依赖HBase相关的jar包,以我的集群为例,依赖的有hbase-common-1.2.0-cdh5.15.1.jarhbase-server-1.2.0-cdh5.15.1.jar、hbase-client-1.2.0-cdh5.15.1.jar、hbase-protocol-1.2.0-cdh5.15.1.jar、hbase-hadoop-compat-1.2.0-cdh5.15.1.jar、htrace-core-3.2.0-incubating.jar、metrics-core-2.2.0.jar。

根据上面对Dockerfile的分析以及出于方便起见,将这些jar包复制到$SPARK_HOME/examples/jars目录下(当然路径也可以自由指定,但是需要在Dockerfile做对应的增改),将这些jar包打包至Docker images中。如下图:

三、修改EntryPoint.sh(可选)

修改 $SPARK_HOME/kubernetes/dockerfiles/spark/entrypoint.sh脚本,新增Hadoop的hosts,如下图。该步骤的目的是,在提交任务时可以用hostname代替ip地址。

四、修改kylin.properties配置文件

正如 Kylin 官网上所说的,“所有使用“kylin.engine.spark-conf.”作为前缀的 Spark 配置属性都能在$KYLIN_HOME/conf/kylin.properties 中进行管理”。因此把原先有关YARN相关的配置都注释掉,并加上spark.kubernetes相关的配置,如下图所示。

其中 kylin.engine.spark-conf.local是我自定义的一个参数,用以指定容器中所依赖的jar包的home path。


五、微改Kylin代码

我们先对比一下Kylin提交Spark任务on YARN和on K8s之间的区别:

Spark on YARN:

export HADOOP_CONF_DIR=/etc/hadoop/conf && /root/apache-kylin-version-SNAPSHOT-bin/spark/bin/spark-submit --class org.apache.kylin.common.util.SparkEntry

--conf spark.master=yarn

--conf spark.submit.deployMode=cluster

--conf spark.executor.instances=40

--conf spark.yarn.archive=hdfs://cdh1.cloudera.com:8020/kylin/spark/spark-libs.jar

--conf spark.yarn.queue=default

--conf spark.history.fs.logDirectory=hdfs://cdh1.cloudera.com:8020/kylin/spark-history

--conf spark.io.compression.codec=org.apache.spark.io.SnappyCompressionCodec

--conf spark.hadoop.yarn.timeline-service.enabled=false

--conf spark.executor.memory=768M

--conf spark.eventLog.enabled=true

--conf spark.eventLog.dir=hdfs://cdh1.cloudera.com:8020/kylin/spark-history

--conf spark.yarn.executor.memoryOverhead=384

--conf spark.driver.memory=800M

--conf spark.shuffle.service.enabled=true

--jars /root/apache-kylin-version-SNAPSHOT-bin/lib/kylin-job-version-SNAPSHOT.jar, /root/apache-kylin-version-SNAPSHOT-bin/lib/kylin-job-version-SNAPSHOT.jar

-className org.apache.kylin.engine.spark.SparkFactDistinct

……

Spark on K8s:

export HADOOP_CONF_DIR=/etc/hadoop/conf && /root/apache-kylin-version-SNAPSHOT-bin/spark/bin/spark-submit --class org.apache.kylin.common.util.SparkEntry

--name kylin-spark

--master k8s://https://10.1.30.85:6443

--deploy-mode cluster

--conf spark.kubernetes.container.image=botcc/spark:spark-docker

--conf spark.kubernetes.authenticate.submission.caCertFile=/root/BOT/ca.crt

--conf spark.kubernetes.authenticate.submission.clientKeyFile=/root/BOT/front-proxy-client.key

--conf spark.kubernetes.authenticate.driver.serviceAccountName=spark

--conf spark.executor.instances=40

--conf spark.history.fs.logDirectory=hdfs://cdh1.cloudera.com:8020/kylin/spark-history

--conf spark.io.compression.codec=org.apache.spark.io.SnappyCompressionCodec

--conf spark.executor.memory=768M

--conf spark.eventLog.enabled=true

--conf spark.eventLog.dir=hdfs://cdh1.cloudera.com:8020/kylin/spark-history

--conf spark.driver.memory=800M

--conf spark.shuffle.service.enabled=true

local:///opt/spark/examples/jars/kylin-job-version-SNAPSHOT.jar,local:///opt/spark/examples/jars/kylin-job-version-SNAPSHOT.jar

-className org.apache.kylin.engine.spark.SparkFactDistinct

……

因此,很自然而然地得出修改思路: 获取配置文件中的master类型,根据master的类型来生成对应格式的Spark参数;如果是提交至K8s,则生成K8s格式的命令;若提交至YARN,则生成YARN格式的命令。提交Spark任务的命令是在SparkExecutable.dowork()方法中生成的,因此在该方法下进行修改。其中,关键代码如下:

备注:由于此次是“初探”,因此我新建了一个分支,用来进行代码修改和功能验证。待日后功能完善后,再申请PR合入master。

六、效果演示

Step 3时,pod成功创建并运行:

Step7、Step 8同样也能成功创建并运行 Pod。

最终,三个Step全部执行成功:

执行一个测试查询语句,能够成功查出数据:

总结

通过实验验证,Apache Kylin 的 Spark 任务引擎,是可以完全运行在 Kubenetes 集群上的,从而将 Kubenetes 的各种益处完全赋予Kylin用户。目前 Spark on Kubernetes 还有一些有待提高的地方,例如尚不支持 external shuffle service、添加jar包依赖比较麻烦、不支持对容器中的 Spark 任务进行管理等;Spark 社区也在持续不断地改进 Spark on Kubernetes 方案,相信在不远的将来这些问题都会被解决。

目前很多企业已经或者开始将各类服务往 Kubernetes 集群上迁移,例如京东、eBay、Google等,这是未来的趋势,我们会持续对其进行关注。

参考:

1. Apache Kylin Spark engine documents

2. Kubernetes documents

3. 十分钟带你理解Kubernetes核心概念

4. Spark over Kubernetes vs yarn/hadoop ecosystem

5. WHY EVERY SPARK DEVELOPER SHOULD CARE ABOUT KUBERNETES

6. Why run Spark on Kubernetes?

作者简介

曹礼俊,开源软件、大数据、IoT爱好者;现就职于 Kyligence, 参与 Kylin 等项目的研究和开发工作。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,639评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,277评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,221评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,474评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,570评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,816评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,957评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,718评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,176评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,511评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,646评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,322评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,934评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,755评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,987评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,358评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,514评论 2 348

推荐阅读更多精彩内容