Spark on Kubernetes 与 Spark on Yarn 不完全对比分析

Posted Apache Kyuubi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spark on Kubernetes 与 Spark on Yarn 不完全对比分析相关的知识,希望对你有一定的参考价值。

前言

Apache Spark 是目前应用最广泛的大数据分析计算工具之一。它擅长于批处理和实时流处理,并支持机器学习、人工智能、自然语言处理和数据分析应用。随着 Spark 越来越受欢迎,使用量越来越大,狭义上的 Hadoop (MR) 技术栈正在收缩。另外,普遍的观点和实践经验证明,除了大数据相关的工作负载,Hadoop (YARN) 不具备相应的灵活性去跟更广泛的企业技术栈融合与集成。比如去承载一些在线业务,而这正是 Kubernetes(K8s) 所擅长的领域。事实上,Kubernetes 的出现为 Spark 的改进打开了一个新世界的大门,创造了更多机遇。如果能用统一的一套集群去运行所有在线和离线的作业,也是十分吸引人的事情。

 

Spark on Kubernetes 于 Spark 2.3 [1] 版本引入开始,到 Spark 3.1 [2] 社区标记 GA,基本上已经具备了在生产环境大规模使用的条件。

 


在业内,苹果[3], 微软[4], 谷歌,网易,华为、滴滴,京东等公司都已经有内部大规模落地或者对外服务的经典成功案例。

Spark on Kubernetes 应用架构

 

从 Spark 整体计算框架层面来看,只是在资源管理层面多支持了一种调度器,其他接口都可以完全复用。一方面 Kubernetes 的引入和 Spark Standalone、YARN、 Mesos 及 Local 等组件形成了一个更为丰富的资源管理体系。

 

 

另一方面,Spark 社区在支持 Kubernetes 特性的同时,对用户 API 的兼容度也得到了最大化的保留,极大程度上方便了用户任务的迁移。比如对于一个传统的 Spark 作业而言,我们通过简单的指定 --master 参数为 yarn 或者 k8s://xxx,即可完成两个调度平台的运行时切换。其他参数诸如镜像、队列、Shuffle 本地盘等配置, yarn 和 k8s 之间都是隔离的,可以很方便地统一在配置文件中统一维护。

 


Spark on Kubernetes vs Spark on YARN


易用性分析

Spark Native API

以 spark-submit 这种传统提交作业的方式来说,如前文中提到的通过配置隔离的方式,用户可以很方便地提交到 k8s 或者 YARN 集群上运行,基本上一样的简单和易用。这种方式对于熟悉 Spark API 及生态的用户而言是十分友好的,基本上没有对 k8s 技术栈的硬性要求。

 

可以看到,如果我们忽略 K8s 或者 YARN 的底层细节,基本上还是熟悉的配方熟悉的味道。

 

Spark Operator

另外,除了这种方式, Kubernetes 在 API 上更加丰富。我们可以通过 Spark Operator[6] 的的方式, 如 kubectl apply -f <YAML file path>来创建和管理 Spark on k8s 应用。这种方式对于 Kubernetes 集群本身及用户而言无疑是最优雅的,而对没有 Kubernetes 经验的这部分 Spark 用户而言,有一定的学习成本。这种方式另一个好处是,Spark 的相关 lib 都可以通过 Docker 仓库来 Deploy,不需要单独的 Spark Client 环境来提交作业。单独的 Client 环境,容易造成版本和 Docker 不一致,增加运维成本,也会埋下引发一些不必要的线上问题的隐患。

 

Serverless SQL

当然,无论是 Spark 原生的还是 Operator 的方式,对大部分用户来说还是太原始了,不可避免的需要去感知一些底层的细节。在 Datalake/Lakehouse 场景下,数据变得民主,数据应用变得多样,很难去大范围地推广。在易用性上想更进一步,可以考虑使用 Apache Kyuubi (Incubating) [7] 来构建 Serverless Spark/SQL 服务。大部分情况下,用户都可以直接使用 BI 工具或者 SQL 来直接操作数据即可。

 

 

一般而言,大部分企业都会有很多离线的 Hive 或者 Spark 任务跑在 YARN 集群上,如何将大量的历史任务平滑地迁移到 Kubernetes 上也是让人头疼的问题。Kyuubi 的服务化方案,可以通过服务发现机制,提供负载均衡节点,在服务高可用的基础上,来平滑地过渡。对于个别异常迁移任务,我们也可以方便地 Rollback 到 老集群上保障执行,也留给我们定位问题的时间和空间。

 

 


性能对比

 

从原理上,无论是 Kubernetes 和 YARN 都只起资源调度的作用,不涉及计算模型和任务调度的变化,所以在性能上的差异应该是不显著的。从部署架构上,Spark on Kubernetes 一般选择存算分离的架构,而 YARN 集群一般和 HDFS 耦合在一起,前者会在读写 HDFS 时丧失“数据本地性”,这个由于网络带宽因素影响可能会影响性能。从存算耦合架构诞生之初经过10年左右的发展,随着网络的性能增长,各种高效的列式存储格式及压缩算法的加持,这点影响微乎其微。

 

Terasort 基准测试 (By Myself)

 

 

 

TPC-DS 基准测试(By Data mechanics)

TPC-DS 基准测试(By  AWS)

 

 

虽然这些测试结果都不是来自 TPC-DS 组织认证的官方数据,但从测试结果来自不同的机构这个因素上也有足够的说服力。我们屏蔽一些部署架构上的影响,两者的性能差距可以说是基本不存在的。

 


成本对比


将 Spark 作业迁移至 Kubernetes 集群上,可以实现离线和在线业务的混合部署,利用两种业务特征的对计算资源潮汐错峰效应,极致的情况下光靠“离/在混部”就可实现 IT 总有用成本(TCO)的 50%的节省。

 

另一方面,企业数据平台在不同的发展时期,集群所规划的存储算力比不同,导致服务器选型困难,而从存算分离的的角度,计算集群和存储集群分开扩容,也可以更加合理地控制 IT 成本。

 

此外,Spark on Kubernetes 通过 Pod 分配 Executor 模式,执行线程数(spark.executor.cores)和 Pod 的 request cpu 是分离的,可以更加细粒度的在作业级别对控制,来提升计算资源的使用效率。在我们网易的实际实践中,在不影响整体计算性能的条件下,Spark on Kubernetes 作业整体上 cpu 可以达到超 200%的超售比。

 

当然,Spark on Kubernetes 在动态资源分配(Dynamic Resource Allocation)这个特性上的缺失或者不完善,可能会造成 Spark 占着资源不使用的情况。由于这个特性直接依赖外置的 Shuffle Service 服务来实现,这时候可能就需要自行去搭建 Remote/External Shuffle Service 服务。

 

 

在 Spark on Kubernetes 场景下,基于 RSS/ESSn可实现临时存储与计算过程相互解耦。第一,消除本地存储依赖,使得计算节点可在异构节点上动态伸缩,在面对复杂物理或者虚拟环境时更加灵活的动态扩展。第二,离散式本地存储优化为集中式服务化存储,存储容量所有计算节点共享,提高存储资源利用率。第三,降低磁盘故障率,动态地减少标记为不可用计算节点,提升计算集群整体资源利用率。最后,转移临时存储的血缘关系,使其不再由 Executor Pod 计算节点维护,使得闲置 Executor Pod 可以被及时地释放回资源池,提升集群资源利用率。

 


其他对比



Spark on k8s

Spark on yarn

何时支持

Spark 2.3.0

Spark 0.6.0

大规模生产

较少

主流

调度器架构

共享状态调度架构

集中式调度器

社区活跃度

活跃

不活跃

最小调度单位

Pod

Container

日志聚合

Spark Web UI

port-forward

proxy

多租户

Namespace

queue

数据本地性

HDFS 读写 - 差

Shuffle 读写 – 好

HDFS 读写 – 理论上好,生产上一般

Shuffle 读写 - 好

HDFS集成流畅度

Spark 3.0及以上

完备

 

总结

Spark on Kubernetes 自 2018年初随 2.3.0 版本发布以来,不知不觉已经有四个年头了,而到现在的 3.2 版本,也已经历经 5 个大版本了。在社区和用户的不断打磨下已经成为了非常成熟的特性了。

 

随着 Apache Spark 开源生态不断发展,如 Apache Kyuubi 等,无论是哪个调度框架,易用性上都得到大幅提升。

 

IT 基础设施的总拥有成本(Total Cost of Ownership, TCO) 逐年上涨,一直是困扰很多企业的难题。Spark + Kubernetes 的组合的灵活性和超高性价比,给了我们更多想象的空间。

参考资料


  1. https://issues.apache.org/jira/browse/SPARK-18278

  2. https://issues.apache.org/jira/browse/SPARK-33005

  3. https://www.youtube.com/watch?v=xX2z8ndp_zg

  4. https://www.youtube.com/watch?v=hcGdW_6xTKo

  5. https://ieeexplore.ieee.org/document/9384578

  6. https://github.com/GoogleCloudPlatform/spark-on-k8s-operator

  7. https://github.com/apache/incubator-kyuubi

  8. https://aws.amazon.com/cn/blogs/containers/optimizing-spark-performance-on-kubernetes/

Spark on Kubernetes 的现状与挑战

作者:
陶克路,花名敌珐,阿里巴巴技术专家。Apache Pulsar 等开源软件 Contributor。技术领域包括大数据和云原生技术栈,目前致力于构建大数据领域业界领先的 APM 产品。



云原生时代,Kubernetes 的重要性日益凸显,这篇文章以 Spark 为例来看一下大数据生态 on Kubernetes 生态的现状与挑战。

Standalone 模式


Spark 运行在 Kubernetes 集群上的第一种可行方式是将 Spark 以 Standalone 模式运行,但是很快社区就提出使用 Kubernetes 原生 Scheduler 的运行模式,也就是 Native 的模式。关于 Standalone 模式这里就没有继续讨论的必要了。

Kubernetes Native 模式


Native 模式简而言之就是将 Driver 和 Executor Pod 化,用户将之前向 YARN 提交 Spark 作业的方式提交给 Kubernetes 的 apiserver,提交命令如下:

$ bin/spark-submit  --master k8s://https://<k8s-apiserver-host>:<k8s-apiserver-port>  --deploy-mode cluster  --name spark-pi  --class org.apache.spark.examples.SparkPi  --conf spark.executor.instances=5  --conf spark.kubernetes.container.image=<spark-image>  local:///path/to/examples.jar

Spark Operator


除了这种直接想 Kubernetes Scheduler 提交作业的方式,还可以通过 Spark Operator 的方式来提交。Operator 在 Kubernetes 中是一个非常重要的里程碑。在 Kubernetes 刚面世的时候,关于有状态的应用如何部署在 Kubernetes 上一直都是官方不愿意谈论的话题,直到 StatefulSet 出现。StatefulSet 为有状态应用的部署实现了一种抽象,简单来说就是保证网络拓扑和存储拓扑。但是状态应用千差万别,并不是所有应用都能抽象成 StatefulSet,强行适配反正加重了开发者的心智负担。

然后 Operator 出现了。我们知道 Kubernetes 给开发者提供了非常开放的一种生态,你可以自定义 CRD,Controller 甚至 Scheduler。而 Operator 就是 CRD + Controller 的组合形式。开发者可以定义自己的 CRD,比如我定义一种 CRD 叫 EtcdCluster 如下:

apiVersion: "etcd.database.coreos.com/v1beta2"kind: "EtcdCluster"metadata: name: "example-etcd-cluster"spec: size: 3 version: "3.1.10" repository: "quay.io/coreos/etcd"

提交到 Kubernetes 之后 Etcd 的 Operator 就针对这个 yaml 中的各个字段进行处理,最后部署出来一个节点规模为 3 个节点的 etcd 集群。你可以在 github 的这个 repo:https://github.com/operator-framework/awesome-operators 中查看目前实现了 Operator 部署的分布式应用。

$ helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator$ helm install incubator/sparkoperator --namespace spark-operator

这个 Operator 涉及到的 CRD 如下:

ScheduledSparkApplication|__ ScheduledSparkApplicationSpec |__ SparkApplication|__ ScheduledSparkApplicationStatus
|__ SparkApplication|__ SparkApplicationSpec |__ DriverSpec |__ SparkPodSpec |__ ExecutorSpec |__ SparkPodSpec |__ Dependencies |__ MonitoringSpec |__ PrometheusSpec|__ SparkApplicationStatus |__ DriverInfo

如果我要提交一个作业,那么我就可以定义如下一个 SparkApplication 的 yaml,关于 yaml 里面的字段含义,可以参考上面的 CRD 文档。

apiVersion: sparkoperator.k8s.io/v1beta1kind: SparkApplicationmetadata: ...spec: deps: {} driver: coreLimit: 200m cores: 0.1 labels: version: 2.3.0 memory: 512m serviceAccount: spark executor: cores: 1 instances: 1 labels: version: 2.3.0 memory: 512m image: gcr.io/ynli-k8s/spark:v2.4.0 mainApplicationFile: local:///opt/spark/examples/jars/spark-examples_2.11-2.3.0.jar mainClass: org.apache.spark.examples.SparkPi mode: cluster restartPolicy: type: OnFailure onFailureRetries: 3 onFailureRetryInterval: 10 onSubmissionFailureRetries: 5 onSubmissionFailureRetryInterval: 20 type: Scalastatus: sparkApplicationId: spark-5f4ba921c85ff3f1cb04bef324f9154c9 applicationState: state: COMPLETED completionTime: 2018-02-20T23:33:55Z driverInfo: podName: spark-pi-83ba921c85ff3f1cb04bef324f9154c9-driver webUIAddress: 35.192.234.248:31064 webUIPort: 31064 webUIServiceName: spark-pi-2402118027-ui-svc webUIIngressName: spark-pi-ui-ingress webUIIngressAddress: spark-pi.ingress.cluster.com executorState: spark-pi-83ba921c85ff3f1cb04bef324f9154c9-exec-1: COMPLETED LastSubmissionAttemptTime: 2018-02-20T23:32:27Z

提交作业。

$ kubectl apply -f spark-pi.yaml

对比来看 Operator 的作业提交方式似乎显得更加的冗长复杂,但是这也是一种更 kubernetes 化的 api 部署方式,也就是 Declarative API,声明式 API。

挑战


基本上,目前市面的大部门公司都是使用上面两种方式来做 Spark on Kubernetes 的,但是我们也知道在 Spark Core 里面对 Kubernetes 的这种 Native 方式支持其实并不是特别成熟,还有很多可以改善的地方:

1.scheduler 差异。

资源调度器可以简单分类成集中式资源调度器和两级资源调度器。两级资源调度器有一个中央调度器负责宏观资源调度,对于某个应用的调度则由下面分区资源调度器来做。两级资源调度器对于大规模应用的管理调度往往能有一个良好的支持,比如性能方面,缺点也很明显,实现复杂。其实这种设计思想在很多地方都有应用,比如内存管理里面的 tcmalloc 算法,Go 语言的内存管理实现。大数据的资源调度器 Mesos/Yarn,某种程度上都可以归类为两级资源调度器。

集中式资源调度器对于所有的资源请求进行响应和决策,这在集群规模大了之后难免会导致一个单点瓶颈,毋庸置疑。但是 Kubernetes 的 scheduler 还有一点不同的是,它是一种升级版,一种基于共享状态的集中式资源调度器。Kubernetes 通过将整个集群的资源缓存到 scheduler 本地,在进行资源调度的时候在根据缓存的资源状态来做一个 “乐观” 分配(assume + commit)来实现调度器的高性能。

Kubernetes 的默认调度器在某种程度上并不能很好的 match Spark 的 job 调度需求,对此一种可行的技术方案是再提供一种 custom scheduler,比如 Spark on Kubernetes Native 方式的参与者之一的大数据公司 Palantir 就开源了他们的 custom scheduler,github repo: https://github.com/palantir/k8s-spark-scheduler。

2.集群规模瓶颈。

基本上现在可以确定的是 Kubernetes 会在集群规模达到五千台的时候出现瓶颈,但是在很早期的时候 Spark 发表论文的时候就声称 Spark Standalone 模式可以支持一万台规模。Kubernetes 的瓶颈主要体现在 master 上,比如用来做元数据存储的基于 raft 一致性协议的 etcd 和 apiserver 等。对此在刚过去的 2019 上海 KubeCon 大会上,阿里巴巴做了一个关于提高 master 性能的 session: 了解 Kubernetes Master 的可扩展性和性能,感兴趣的可以自行了解。

3.Pod 驱逐(Eviction)问题。

在 Kubernetes 中,资源分为可压缩资源(比如 CPU)和不可压缩资源(比如内存),当不可压缩资源不足的时候就会将一些 Pod 驱逐出当前 Node 节点。国内某个大厂在使用 Spark on kubernetes 的时候就遇到因为磁盘 IO 不足导致 Spark 作业失败,从而间接导致整个测试集都没有跑出来结果。如何保证 Spark 的作业 Pod (Driver/Executor) 不被驱逐呢?这就涉及到优先级的问题,1.10 之后开始支持。但是说到优先级,有一个不可避免的问题就是如何设置我们的应用的优先级?常规来说,在线应用或者 long-running 应用优先级要高于 batch job,但是显然对于 Spark 作业来说这并不是一种好的方式。

4.作业日志。

Spark on Yarn 的模式下,我们可以将日志进行 aggregation 然后查看,但是在 Kubernetes 中暂时还是只能通过 Pod 的日志查看,这块如果要对接 Kubernetes 生态的话可以考虑使用 fluentd 或者 filebeat 将 Driver 和 Executor Pod 的日志汇总到 ELK 中进行查看。

5.Prometheus 生态。

Prometheus 作为 CNCF 毕业的第二个项目,基本是 Kubernetes 监控的标配,目前 Spark 并没有提供 Prometheus Sink。而且 Prometheus 的数据读取方式是 pull 的方式,对于 Spark 中 batch job 并不适合使用 pull 的方式,可能需要引入 Prometheus 的 pushgateway。

 结语


被称为云上 OS 的 Kubernetes 是 Cloud Native 理念的一种技术承载与体现,但是如何通过 Kubernetes 来助力大数据应用还是有很多可以探索的地方。欢迎交流。

参考:


  1. https://spark.apache.org/docs/latest/running-on-kubernetes.html

  2. https://medium.com/palantir/spark-scheduling-in-kubernetes-4976333235f3

  3. https://github.com/palantir/k8s-spark-scheduler

  4. Kubecon: 了解 Kubernetes Master 的可扩展性和性能

  5. Kubecon: SIG-Scheduling Deep Dive

  6. https://github.com/operator-framework/awesome-operators

  7. https://github.com/coreos/etcd-operator

  8. https://github.com/GoogleCloudPlatform/spark-on-k8s-operator

 



新福利:

上周获奖名单:ZRTX

Spark on Kubernetes 的现状与挑战

Spark on Kubernetes 的现状与挑战




猜你喜欢

1、

2、

3、

4、


以上是关于Spark on Kubernetes 与 Spark on Yarn 不完全对比分析的主要内容,如果未能解决你的问题,请参考以下文章

直播 | TalkingData的Spark On Kubernetes实践

基于 Kubernetes 的企业级大数据平台,EMR on ACK 技术初探

如何实现Spark on Kubernetes?

「SDS极客」Spark On Kubernetes存算分离的最佳实践

Spark in action on Kubernetes - 存储篇

异常-User class threw exception: java.lang.IllegalStateException: Cannot call methods on a stopped Spa