服务器 频道

基于K8S构建Zeppelin大数据可视化分析工具

  背景

  大多数互联网企业都提供有类似Notebook类的产品,采用交互式的方式进行数据分析、数据建模及数据可视化。主要实现大多都是基于jupyter 、Zeppelin进行定制化开发,重点会打通大数据计算、存储及底层资源管理,支持常见的机器学习和深度学习计算框架。

  目前部门这块主要的产品是数据查询平台BigQuery,主要是用于Hive sql数据分析查询,底层是Tez计算引擎跑在yarn集群上,缺乏交互式可编程分析工具,无法满足于机器学习、深度学习等迭代计算以及结果可视化的需求,需要补充Notebook 交互式编程能力以及对spark计算引擎等的支持。Apache Zeppelin 是一款大数据分析和可视化工具类似于jupyter notebook交互式代码编辑器,满足这些需求,同时相较于Juypter 其更适合于大数据企业应用。

  文章主要分几个方面进行介绍,先对Zeppelin进行介绍,接着会讲下Zeppelin 的生产实践,包括如何定制化去做K8S集群上的部署、资源的隔离、文件数据的上传和持久化。

  Zeppelin介绍

  Apache Zeppelin是一款大数据分析和可视化工具,可以让数据分析师在一个基于Web页面的笔记本中,使用不同的语言,对不同数据源中的数据进行交互式分析,并对分析结果进行可视化的工具。可以承担数据接入、数据分析、数据可视化、以及大数据建模的全流程,前端提供丰富的可视化图形库,后端支持Spark、HBase、Flink 等大数据系统,并支持Spark、Python、JDBC、Markdown、Shell 、ES等各种常用Interpreter,这使得开发者可以方便地使用SQL 在 Zeppelin 中做数据开发。

  

  主要功能和特点

  可视化交互式数据分析 ,用户通过可视化界面,交互式地输入指令、代码提交给Zeppelin编译执行。

  Notebook管理 用户通过Web页面轻松地实现Notebook应用的增加、修改、运行和删除,支持应用的快速导入导出。

  数据可视化指令、代码提交后Zeppelin返回结果给用户,如果是结构化的数据,Zeppelin提供可视化机制,通过各类图表展示数据,十分方便。

  解释器配置 用户可以配置系统内置的Spark、JDBC、Elasticsearch等解释器,支持按组管理解释器、为一个Notebook应用绑定多个解释器。

  运行任务管理 用户将Notebook应用提交给Zeppelin运行,也可以停止正在运行的任务。

  用户认证 Zeppelin提供完善的用户认证机制。

  Notebook应用一键分享 调试完毕的Notebook应用可以提供统一访问的HTTP地址给外部应用访问。

  IntelliJ 提供的big data tool插件实现Zeppelin 高效的本地数据开发和调试

  一张官方支持解释器的全景图:

  

  Zeppelin架构

  Zeppelin架构主要分三块:Zeppelin前端、Zeppelin Server、Zeppelin Interpreter;Zeppelin前端是基于AngularJS ;Zeppelin Server是一个基于Jetty的轻量级Web Server。主要负责Zeppelin服务配置信息管理、Interpreter 配置信息和生命周期管理、Note存储管理、插件机制管理以及登陆权限管理。

  Zeppelin前端和Zeppelin Server之间的通信机制主要有Rest api和WebSocket两种。Zeppelin Server和Zeppelin Interpreter是通过Thrift RPC来通信,而且他们彼此之间是双向通信,Zeppelin Server可以向Zeppelin Interpreter发送请求,Zeppelin Interpreter也可以向Zeppelin Server发送请求。

  Interpreter组件是指各个计算引擎在Zeppelin这边的适配。比如Python,Spark,Flink等等。每个Interpreter都run在一个单独的JVM进程里,这个JVM进程可以是和Zeppelin Server在同一台机器上(默认方式),也可以run在Zeppelin集群里的其他任何机器上或者K8s集群的一个Pod里。

  Interpreter支持动态加载maven格式依赖包的能力,多JVM隔离runtime依赖。Thrift-Based跨语言IPC(Inter-Process-Communication)机制(规定repl解释器集成和平台之间的数据交换的格式和时序)。抽象出repl解释器生命周期管理接口,各repl解释器受zeppelinServer端控制。

  

  Zeppelin单独的JVM进程来启动repl解释器的原因:

  zeppelin旨在提供一个开放的框架,支持多种语言和产品,每种语言和产品都是各自独立演进的,各自的运行时依赖也各不相同,甚至是相互冲突的,如果放在同一JVM中,仅解决冲突,维护各个产品之间的兼容性都是一项艰巨的任务,某些产品版本甚至是完全不能兼容的。

  大数据分析,是否具有横向扩展能力是生产可用的一项重要的衡量指标,如果将repl进程与主进程合在一起,会验证影响系统性能。

  因此,在有必要的时候,zeppelin采用独立JVM的方式来启动repl进程,并且采用Thrift协议定义了主进程ZeppelinServer与解释器进程之间的通信协议。

  Zeppelin生产实践

  Zeppelin单机多进程方式运行,不满足可扩展性、隔离性。当前大数据存储和计算的分离以及Zeppelin的核心引擎解释器Spark跑Kubernetes方案的成熟因此部署到Kubernetes上以镜像容器的方式启动,也就是水到渠成的事情,可以解决Zeppelin的扩展性、安全性、隔离性问题。生产上K8S集群也会遇到,如何去做资源隔离以及解释器空闲如何回收,要去打通大数据存储、HIVE数据访问,提供文件上传数据持久化等功能。

  部署到K8S 需要定制化解决如下问题:

  如何去对接K8S管理Zeppelin生命周期?包括Zeppelin服务的启动、状态、Zeppelin停止、资源回收?

  如何去为每个用户生成Zeppelin Server解决多租户问题,并暴露统一的服务访问入口?

  如何解决Zeppelin Server停止后,用户创建的Note数据能够持久化不随着服务的停止而删除?

  K8S部署

  K8S部署相比于单机上部署可以带来如下好处:

  

  单机多进程情况下,扩展能力有限,尤其是运行需要占用大量资源的任务如Spark 、tensorflow等,采用K8S 多进程以多POD的形式能够调度到K8S不同的NODE 这样扩展性大大增强。

  解释器运行在容器中,避免安装依赖对系统环境进行更改,并产生依赖包冲突,同时shell解释器之类的存在着文件删除、非法访问等非安全性的操作,运行依赖以镜像的方式交付,进程运行在容器中,提高了系统安全性。

  K8S部署的整体架构如下图所示:

  

  架构主要分为三部分:

  Zeppelin服务的启动,停止和状态获取,这块主要包含调用k8s Api去创建Namespace、ConfigMap、Service、RBAC、PV、PVC以及Zeppelin Server Deployment等Zeppelin启动相关的k8s对象,这里我们做了相关个性化开发,首先是通过调用JAVA K8S API去创建Zeppelin K8S对象,其次就是通过挂载NFS或S3解决 notebook 无法持久化存储问题、接着就是新增init container容器去实现多个用户demo note的拷贝操作,另外还会涉及以configmap和环境变量 实现相关动态参数的传递。

  Zeppelin 服务的访问,这块主要设计了为每个用户创建独立的namespace,并启动一个独立zeppelin server 去做多租户, 每个用户暴露不同的访问url,然后部署一个nginx 并创建暴露NodePort 类型nginx Service去代理不同用户的访问链接 ,通过nginx解析url location获取不同用户namespace及具体的location 拼装成最终不同用户反向代理的DNS地址。

  这部分改造的相对比较少,主要就是Zeppelin 如何去启动Interpreter以及之间的通信。

  打通计算和存储

  Zeppelin 支持许多大数据计算引擎,生产支持的话需要解决依赖包安装、环境变量配置、解释器配置等工作,既然是平台化的支持,应该这些功能在Zeppelin Server启动后就可以直接使用,不需要用户在做许多配置这类繁琐的工作,这块的话我们在设计开发的时候,打通底层spark 访问HDFS、新建了HIVE SQL解释器,不需要用户额外配置可以直接访问生产Hive 、HDFS并解决了权限认证、包依赖等问题。

  比如说,通过镜像内置hdfs、hive、yarn客户端,提供相应线上的配置文件,传递不同的用户认证信息解决依赖及访问权限问题,配置DNS域名解析,搞定K8S集群和大数据集群访问权限,最终能够让spark hive任务正常运行起来。

  资源隔离及回收

  问题背景:

  用户用zeppelin 跑spark任务配置多达100个executor,400G内存导致K8S集群中其他任务无法调度。

  原因分析:

  这个问题比较容易分析,主要k8s集群CPU、内存是有限制的,不可能无限扩容,当申请的资源过多,如果没有调度和隔离策略的话就会影响到其他任务的调度,另外如果k8s分配出去的资源无法回收,总有一天会有资源用尽导致其他任务无法调度的情况。

  解决方案:

  K8S官方提供了资源隔离方案,有基于节点选择的、任务亲和度的、以及namespace资源限制的,还有就是启动任务资源限制的,这块我们采取的最基本的节点标记选择,具体如下图所示:

  

  对K8S集群的计算节点打上不同的标记,规定了一些节点是notebook 公用的节点,默认创建的Zeppelin notebook会调度到这些 公用资源组节点上去,如果有些notebook比较重要就会指定到另外的资源组中,这块实现比较简单,一个就是就是为要运行的任务打上需要调度到某些标记的节点,另外就是提供管理接口,提前对k8s中的计算节点打上标记,这样k8s在进行任务调度时,会去自动过滤选择符合条件的节点。

  针对资源限制和回收的问题在启动时候增加的配置参数,规定了pod启动的资源限制以及服务空闲后多久会被回收,如下图所示:

  

  这块资源限制,主要是在调用k8s api创建pod时候 增加request 和limit 对cpu和内存进行限制。

  资源回收主要是前端选择空闲停止时间,后端服务进程获取配置参数,在Zeppelin server 与解释器交互请求的地方启动线程,不停更新交互时间,直到判断空闲超过配置时间,触发进程退出操作。

  文件上传及数据持久化

  问题背景:

  用户创建zeppelin notebook重启后notebook消失

  Zeppelin 不支持本地文件上传以及上传后的文件如何共享到不同解释器的Pod中。

  

  

  解决方案:

  这里的问题比较明显,主要是容器中所进行的文件修改并不会持久化,容器停止重新启动后又是一个新的环境,在其中创建的notebook、安装的依赖包都会随着下次启动而消失,为了能够持久的保存用户数据,就需要去挂载网络存储或者对象存储,这块的话就比较常规了为每个启动的Zeppelin Server挂载一个NFS网络存储,同时通过subpath参数为每个用户创建不同的子目录解决用户数据隔离的问题。不同用户挂载到容器中就是同一个网络存储不同的子目录。同时也提供了S3网络存储挂载方式,具体可以查看参考资料。

  另外一种情况是,用户在做数据探索和数据分析,所访问的数据要么是从网络中下载的数据,要么就是HDFS中的文件,要么就是具体的解释器自身的访问数据方式,相对于jupyter notebook ,zeppelin本身就没有提供文件上传的操作,这块我们进行了定制化提供文件上传操作,只不过这个文件上传操作稍微有点特殊,还记得之前提到zeppelin运行到k8s 中的架构interpreter是运行在不同机器节点的pod中,上传到zeppelin server中的数据要在其他解释器中能够使用,这就需要zeppelin 和其他interpreter pod也需要挂载对应的网络存储,这块我们在创建interpreter pod时就会同样增加网络存储的挂载操作。

  总结和展望

  Zeppelin notebook可以满足数据开发、数据分析及产品运营报表分析以及大数据交互式建模,通过部署到K8S集群中可以解决其扩展性、安全性、多租户等问题。并在生产实践中进行了一些定制化功能,同时积极拥抱开源社区,对Zeppelin 中的代码存在通用的问题进行了修复并提交了社区如:ZEPPELIN-5379及ZEPPLIN-5349,除此之外Zeppelin on k8s未来还有许多事情需要去解决,比如Flink on Zeppelin如何跑在K8S之中,web shell、Spark web ui 跑在K8S遇到的访问链接如何打开,zeppelin 定时调度,其他解释器如何运行在Zeppelin K8S环境中以及内部定制化需求等功能,在之后的迭代开发中会继续紧跟社区完善相关产品能力。

  参考资料

  https://zeppelin.apache.org/

  https://zhuanlan.zhihu.com/p/372250644

  https://www.yuque.com/jeffzhangjianfeng/ggi5ys/shby78

0
相关文章