以 Hadoop 为中心的大数据生态系统从 2006 年开源以来,一直是大部分公司构建大数据平台的选择,但这种传统选择随着人们的深入使用,出现的问题也越来越多,比如:数据开发迭代速度不够快、集群资源利用效率过低、新的开发工具集成非常复杂等。这些问题已经成为困扰企业数字化转型加速迭代和升级的主要障碍。
而传统大数据平台通常是以 Hadoop 为中心的大数据生态技术。一个 Hadoop 集群包含 HDFS 分布式文件系统和以 Yarn 为调度系统的 MapReduce 计算框架。围绕 Hadoop,有一系列的软件来帮助人们进行大数据的存储和计算,比如数据仓库 Hive、计算框架 Spark、实时消息队列 Kafka 等。
在大数据发展的初期,这样的大数据生态技术框架是能基本满足人们建设大数据平台的需要的。随着时代的发展,大数据技术使用逐步地深入,大数据开发需求变得越来越旺盛,人们对多租户环境下大数据开发的效率、大数据集群资源利用率、新(计算和存储)技术的集成速度提出了越来越高的要求,而传统大数据平台在面对这些需求时则显得有点束手无策,出现了无法克服的困难。
从 2014 年开始,以 Docker 和 Kubernetes(K8s)为代表的云原生技术蓬勃发展,云原生的社区和机构迅速壮大。现在,Kubernetes 已经成为企业搭建容器云平台的标配。
那么,高速发展的云原生技术能不能解决传统大数据平台的问题呢?答案是肯定的。本文将从大数据平台产品云原生化的实践过程,阐述一下传统大数据平台迁移到 Kubernetes 上所要经过的技术改造过程。
我们先仔细分析看下传统大数据平台的弊端。传统大数据平台的技术架构决定了依靠它本身的发展是无法克服以下这些困难:
传统大数据平台难以实现资源的隔离。多租户环境下的数据开发效率提升,需要以资源隔离的方式来保证租户之间的计算作业互相不影响,特别是不能出现某一个或几个租户独占集群资源的情况。但 Hadoop 系统本身的出发点就不是为了多租户环境而设计的,其目前的资源隔离实现也不完善。在最近的 Hadoop 版本中,在一定程度上实现了内存资源和文件资源的隔离,但是不够完整,而磁盘 I/O 和网络 I/O 的隔离还在社区讨论的过程中,短期内看不到解决的希望。
传统大数据平台难以集成新的计算和存储技术。Hadoop 系统在部署其他组件的时候,对这些组件与 HDFS 和 Yarn 的版本适配是有严格要求的。很多新的大数据组件是不适配老版本的 Hadoop 的,而升级 Hadoop 又会造成其他组件的失效。另外,部署新的组件还要考虑到 Linux 不同操作系统的兼容性所带来的额外复杂度。所以引入一个新的计算和存储组件的难度是非常高的,往往需要几天甚至是几周的时候。
Hadoop 存算合一的耦合架构决定了它的资源利用率无法提高。在一个 Hadoop 集群中,一个节点既是存储节点(datanode),也是计算节点。当存储资源不够的时候,增加节点可以进行存储扩容,但会造成计算资源的利用率下降;同样,当计算资源不够而进行扩容的时候,存储资源利用率就会下降。同时,因为对于 Yarn 的依赖,不使用 Yarn 调度的其它组件很难集成到 Hadoop 的计算框架中。所以 Hadoop 的这种耦合架构决定了它的资源利用率不可能很高。
Hadoop 集群资源无法做到快速的弹性扩容和缩容。弹性的扩容和缩容是提高集群资源利用率的有效方法。很遗憾,Hadoop 的节点扩容和缩容流程,导致这个动作无法在很快的时间内完成,尤其是缩容过程,只有当一个 datanode 的所有数据块都在其他节点完成了备份以后,该节点才能被移出集群,而由于数据备份是以较小的传输率运行在后台,往往要持续几个小时以上。
总而言之,传统大数据平台因为其结构性的缺陷导致了多租户环境下数据开发效率低、集群资源利用率不高、以及集成新技术很复杂等问题,依靠 Hadoop 生态技术框架本身的发展是不可能解决这些问题的。
既然不能够依靠 Hadoop 生态技术本身的发展来解决传统大数据平台带来的难题,那么我们就应该把注意力放到当前最新的技术发展趋势之上,也就是以容器和 Kubernetes 为代表的云原生技术。
云原生技术在 2013 年容器项目以及 2014 年 Kubernetes 项目正式发布以后,发展非常迅猛。现在,各大公有云厂商都支持 K8s,还有上百家技术公司在持续投入 K8s 的迭代和更新工作。成立于 2015 年的云原生计算基金会(CNCF),将 K8s 作为其托管的第一个项目,到目前该基金会已经托管了 123 个项目,近 200 个国家的 16 万开发者在为这些项目开发代码。更令人兴奋的是,CNCF 的生态全景图目前包含了 1000 多个云原生技术产品,覆盖了数据库、消息级流处理、调度和任务编排、存储系统等 10 多个技术领域。
对于大数据来说,2021 年应该是云原生大数据技术发展的里程碑。在这一年,有两个重大的技术进展被公布。一个是 2021 年 3 月,Apache 宣布 Spark 3.1 正式支持了 kubernetes,另外是在 2021 年 5 月,Apache Kafka 背后的商业公司 Confluent 也发布了 Confluent on Kuberneters,一个能私有发布的在 K8s 之上运行的 Kafka 生产集群系统。
这两个重要事件表明,大数据平台的云原生化已是大势所趋。按照这个趋势,Hadoop 也会逐渐迁移到 K8s 上。从技术角度来分析,常说的 Hadoop 三架马车中,计算框架 MapReduce 会被更高效的 Spark 所取代,资源调度组件 Yarn 正在被 K8s 取代,最坚挺的 HDFS 也有了云原生的对标方案。这意味着直接在 K8s 上运行所有现在的大数据工作负载已经成为了可能。
同时,从企业业务需求来看,传统大数据平台所缺乏的难以集成新组件、难以实现资源隔离、难以提供资源利用率,特别是缺乏云原生的弹性扩展能力大大阻碍了企业业务系统的发展,由于缺乏弹性扩展能力而导致的业务系统崩溃想象时有发生,而云原生大数据平台恰恰是解决这些问题的良药,简单的讲,就是云原生赋予了大数据平台原来没有的多种云化能力。
因此,无论从技术趋势还是从企业业务需求来看,大数据平台的云原生化都是一个必然的趋势。
虽然大数据平台的云原生化已经是大势所趋,但在落地实践的过程中还是有一些技术难题需求攻克。就拿 Spark 来说,虽然 Apache Spark 3.1 已经支持了 K8s,但是有几个问题还没有解决,比如 Hive SQL 作业如何以 Spark 的方式在 K8s 运行?JupyterLab 运行的 PySpark 和 Spark 程序怎么运行在 K8s 上?接下来,我们介绍下智领云是如何解决传统大数据平台云原生化的技术难题。
在传统大数据平台中,Hive 被广泛用来进行数据仓库的建设。大数据平台的云原生化一个很重要的工作就是要保留对 Hive SQL 的支持,否则原有的大量的 Hive SQL 程序需要进行迁移,风险和成本都难以把控。从语法上看,Hive SQL 和 Spark SQL 还是有很大差异的,所以我们不能简单地用 Spark SQL 来取代 Hive SQL。另一个技术选择就是修改 Hive 的底层执行引擎,让 Hive SQL 程序以 Spark 作业的方式运行在 K8s 上。这个选择看上去不错,但也面临一些技术挑战。
首先,Spark 对 K8s 的支持是 2021 年达到 GA 状态,不同的 Hive 版本、Spark 版本、K8s 版本、以及很多相关大数据组件(比如说授权组件 Apache Ranger)之间的适配还没有成熟。因此,我们经过生产集群的验证,确定了基于以下大数据组件版本的 Hive 执行引擎切换到 Spark 的解决方案。
对于 Spark,我们推荐使用最新的版本,因为新版本的 Spark 能增强对 K8s 的支持。而 Hive 从 4.0.0 版本开始,重构了 spark-client 模块的代码结构,增加了 SparkClient 抽象类,通过对该抽象类的代码扩展,我们可以实现对 K8s 的支持。但是在 Hive 代码进行扩展的过程中,要注意避免 Hive 和 Spark 针对 Kryo 库的版本冲突。
我们对 Hive 代码的改造主要是增加了抽象类 SparkClient 的一个 K8s 实现,KubernetesSubmitSparkClient 类。这个类通过创建一个 SparkSubmit 实例向 K8s 提交 Spark 任务的各种参数。如下图所示,Hive SQL 代码的执行经过了下面一系列的流程。
Hive SQL 程序是通过 Beeline 来连接 HiveServer2,而 Hive 查询工具 Hue 则是通过 JDBC 来连接 HiveServer2;
Hive SQL 语句提交到 HiveServer2 后,被解析生成一系列的 SQL 执行计划,并生成 SQL 任务;
SQL 任务会启动一个 RPC server,然后 KubernetesSubmitSparkClient 会带上 RPC server 参数,通过 K8s 的 API server 提交一个 Spark 作业;
K8s 的 scheduler 这时会启动一个 Spark Driver Pod 来和 HiveServer2 的 RPC server 进行通信,这个 Spark Driver Pod 的主要功能就是接收 RPC server 发送过来的 Hive SQL 作业进行计算,计算完成后,将结果返回给 RPC server;
Spark Driver Pod 在启动完成后,会发送启动 Spark Executor Pod 请求给 K8s APIServer, K8s 再启动若干 Spark Executor Pod,然后 Spark Driver 和 Spark Executor 建立连接,完成 Hive SQL 作业的计算。
对于 Spark 程序和 PySpark 代码的执行,我们采用的解决方案是基于 Google 开源的 Spark on K8s Operator 项目。这个开源项目更好地利用了 K8s 的一些特性,来增强在 K8s 上使用 Spark 计算引擎的易用性、灵活性、以及性能提升。
但该项目有一个缺陷,就是用户需要通过配置一个复杂的 Yaml 文件来运 Spark 作业,该 Yaml 文件需要声明 Spark 作业的所有信息,包括 Driver/Executor 的资源配置、Spark 的容器镜像版本、以及调度算法等。对于一般用户而言,这些配置信息显得过于繁琐和复杂。
为了简化 Spark 程序在 K8s 上运行的复杂配置流程,我们模仿 Apache Livy 的 API 开发了一个 Spark Job Manager Server。该服务负责管理 Spark On K8s Operator 的作业,提供作业的创建、更新、删除、查询状态、日志获取等接口。提交 Spark 程序的时候,用户不需要配置任何 Yaml 文件,只需要配置少量 Spark 作业参数,跟用 spark-submit 脚本提交方式没有区别。Spark Job Manager Server 服务会根据用户提交的参数完成 Spark 作业的 Yaml 文件渲染,将作业提交到 K8s 集群。这一操作方式极大地简化用户在 K8s 上运行 Spark 程序的复杂度。Spark 程序在 K8s 上运行的架构图可以参考下图:
需要注意的是,第 1、2、3 步都是发生在 Spark Job Manager Server,第 4 步是将 Spark 作业以 Yaml 文件的方式提交给 K8s API Server,之后的 Spark 作业执行就交给 Spark on K8s Operator 去执行了。在第 11 步,Spark Job Manager Server 会通过 API Server 获取 Spark Driver 的状态信息,从而与 Spark Driver 进行通讯以获取 Spark 作业的执行结果。
在传统大数据平台,JupyterLab 一直是数据科学家首选的交互式编程工具,广泛应用在数据的探索分析以及人工智能机器学习算法的开发上。因此,在 K8s 平台上支持 JupyterLab 交互式的 Spark 程序运行是必须要解决的问题。
目前,JupyterLab 是利用开源项目 SparkMagic Kernel 通过 Apache Livy 服务来和 Spark 集群进行通讯,实现 Spark 程序的交互式运行。但是,Apache Livy 目前的版本并不支持 K8s。针对这个问题,我们采用了 Hive 模式类似的方式,对 Apache Livy 代码进行了扩展,在 Livy 服务端创建了一个 RPC Server,然后通过 SparkSubmit 提交 Spark 任务。运行在 K8s 集群的 Spark Driver Pod 会和 RPC Server 通信,来完成 SQL 任务的交互执行。下图展示了整个流程的架构图:
Kafka on K8s 有不少开源的方案,我们选择了 Strimzi 开源的 Kafka Operator。这个项目通过 CRD 抽象描述各种 Kafka 组件的配置,以 Operator 控制协调的原理去管理 Kafka 集群组件,相对完整地实现了 Kafka 集群在 K8s 上的部署。
我们对 Strimzi Kafka Operator 的改造主要是支持安全认证和权限管理,将 Schema Registry 组件集成到 Kafka Operator,然后对开源的 Kafka 运维管理工具 AKHQ 进行改造,将其也集成到 Kafka Operator。下图展示了 Kafka 在 K8s 运行的架构图:
目前开源社区有不少成熟的项目都支持 HDFS 集群在 K8s 上发布,但是对多租户和数据安全的支持却不是很完善。我们对 HDFS on K8s 项目进行了扩展,首先是将 Hadoop 的版本升级到最新版本,并与最新的 K8s 版本进行适配。同时,我们集成了对 Https 访问、Kerberos 安全认证和 Apache Ranger 权限授权等功能的支持。
在 K8s 这样的平台上发布应用并不是一件容易的事情,应用开发者要了解 K8s 复杂的应用资源配置,比如 API 版本、资源类型、命名空间、标签、容器参数、存储参数、网络参数、重启配置等等一长串的配置。应用开发者为了掌握 K8s 基于 Yaml 文件的应用配置方式需要大量时间去学习和实践。在实践过程中,有不少开发者反映,他们 90% 的时间都在编写重复的应用配置,整个流程过于复杂。
为了解决这一问题,我们采用了 KubeVela 框架来简化 K8s 上应用发布和交付的复杂性。KubeVela 作为 K8s 的一个插件,它利用 Open Application Model 模型和 K8s 的可扩展性,通过构建应用发布的一个抽象层,来解决在 K8s 上发布应用存在的一些复杂问题,比如 Pod、端口暴露、权限、资源声明和管理、CRD 等概念,都被抽象成了以应用为视角的配置 SDK。我们通过 KubeVela 框架,将应用发布的通用定义、监控告警定义、监控面板定义、日志采集定义、以及对外端口定义等都抽象成了一个个组件,极大地简化了应用发布的复杂性,也统一了数据应用集成开发平台的应用发布规范。
大数据组件的运行需要有统一的监控、报警、日志系统来高效地进行运行状态及性能的监控、失效报警、和故障跟踪等大数据运维工作,以保证大数据生产系统的平稳运行。
在可观测性方面,我们采用了开源的 Prometheus 来进行监控指标的采集,集成了 Grafana 来配置各组件的监控面板,利用轻量级的云原生日志系统 Loki 来收集各组件的日志,并自主研发了 LogViewer 服务来快速地搜索和获取日志。下图展示了 Kafka 集群的监控面板及日志检索大屏:
通常一个大数据平台要服务多个部门、业务人员、数据开发人员,是一个典型的多租户环境。在这样的多租户环境下,我们首先要实现所有大数据(可视化)组件的单点登录,这样既能避免维护多套用户登录系统的麻烦,也能保证我们能在大数据计算和存储层能实现统一的安全认证和权限授权机制,而安全认证和权限授权机制也必须是所有计算和存储引擎共用同一套机制。
传统大数据平台在多租户环境下的一个难点就是资源隔离,它很难避免一个或少数几个租户独占资源的情况。在云原生架构下,这一问题就迎刃而解。那么,我们具体看看是怎么实现上述功能点的。
单点登录:我们基于 OpenID Connect 协议以及开源认证授权管理平台 Keycloak,对主要的大数据可视化工具(Hue、Superset、JupyterLab 等)的登录代码做了修改或扩展,实现了所有可视化工具的单点登录。
数据安全:对于数据安全,我们采用了 Kerberos 协议来实现安全认证,并基于开源授权框架的 Apache Ranger 实现了统一的大数据资源(HDFS、Hive 和 Kafka)的授权管理。对于 Spark,我们扩展了 Spark Authorizer 开源插件的代码以支持最新版本的 Spark。对于 Hive,我们修改了大量的 Apache Ranger 的代码以完成对 Hive 4.0.0 的支持。而对于 Kafka,开源的 Strimzi KafkaOperator 是不支持 Kerberos 安全认证和 Apache Ranger 的授权机制的,我们对 Strimzi Kafka Operator 的代码和配置进行了扩展,实现了 Kafka 集群的数据安全。
资源隔离:我们充分了利用了 K8s 的命名空间来实现多租户的资源管理,对于每一个机构,我们在 K8s 上分配了一个独立的命名空间,并对该命名空间进行了资源配额的管理,以确保每个机构都不会使用超过其分配份额的集群资源。
数据应用开发平台的数据安全架构如下图所示。目前,每个用户在每台虚机上都创建了一个相同的账号,并且保存了一份该用户的 Kerberos keytab,这样每个运行中 K8s 上的容器和大数据组件都可以使用这个用户 ID 和 keytab 进行安全认证。
目前,我们已经将云原生大数据平台的基本版作为一个项目开放了出来,与开发者共享 HDFS、Hive、Spark operator、和 Kafka Operator 等大数据组件的部署方式。开发者可以基于这个项目部署一个实验的大数据集群,来体验云原生大数据平台。
Github 地址:
https://github.com/linktimecloud/big-data-on-k8s
Gitee 地址:
https://gitee.com/linktimecloud/big-data-on-k8s
需要注意的是,本项目只能作为一个实验系统来运行,因为它不支持高可用、Kerberos 安全认证、以及基于 Apache Ranger 的鉴权机制。
这两年以来,我们在大数据平台云原生化这个方向做了大量尝试,实现了大数据组件在 K8s 的稳定运行以及统一的数据安全机制,使数据应用开发平台实现了完整的云原生化。接下来,我们将在以下几个方向继续探索,推动大数据平台更高效更稳定地运行在 K8s 之上。
取代 HDFS:HDFS 的主要问题是文件块元数据都在内存中,造成 NameNode 经常消耗大量的内存而且容易出现内存故障,同时对小文件的不友好,运维也比较复杂。我们计划在未来采用开源的云原生分布式文件存储系统 MinIO 来取代 HDFS。
统一 Spark 程序的运行方式:目前我们运行 Hive SQL 和 Spark SQL 采用了不同的模式,未来我们希望能用统一的 Spark On K8s Operator 模式来执行 Hive SQL 和 Spark SQL 的计算,使得 Spark 运行更稳定,也使 Spark 计算引擎的监控和运维更加高效。
Flink 等其他大数据组件的云原生化:未来我们将持续不断地将其他主要的大数据计算和存储引擎集成到 K8s 集群上,进一步提升数据应用开发平台的大数据开发体验。
Spark 访问数据的局部性问题(也称为 Data Locality 问题):当 Spark 程序和 HDFS 都运行在 K8s 之上的时候,我们需要保证 Spark executor 在读取 HDFS 文件的时候是从同一个节点上的 datanode 去读数据,而不是到其他节点的 datanode 上去读取数据。在其他节点上去读取数据有网络上的延迟,会造成计算作业大约 10% 的性能损耗。解决这个问题可能会需要引入新的 Spark 作业调度机制,或者对 Spark Driver 的源码进行修改。
作者简介
宋文欣,智领云科技联合创始人兼 CTO,美国纽约州立石溪大学计算机博士,武汉大学计算机系本科及硕士。具有二十多年软件开发,大数据及云计算经验,前 Electronic Arts 高级工程经理,ask.com Analytics 技术带头人。
钉钉总裁称非常讨厌红点和 DING 消息;Mozilla 控诉苹果、谷歌和微软锁定浏览器;特斯拉上海工人薪酬曝光:到手七八千|Q 资讯