乐趣区

关于大数据:关于DataLeap中的Notebook你想知道的都在这

更多技术交换、求职机会,欢送关注字节跳动数据平台微信公众号,回复【1】进入官网交换群

DataLeap 是火山引擎数智平台 VeDI 旗下的大数据研发治理套件产品,帮忙用户疾速实现数据集成、开发、运维、治、资产、平安等全套数据中台建设,升高工作老本和数据保护老本、开掘数据价值、为企业决策提供数据撑持。

本文次要具体讲述 DataLeap 中的 Notebook,包含后期选型、技术路线、架构降级、调度计划、以及将来工作等五局部重点内容,带你具体理解 Notebook。

概述

Notebook 是一种反对 REPL 模式的开发环境。所谓「REPL」,即「读取 - 求值 - 输入」循环:输出一段代码,立即失去相应的后果,并持续期待下一次输出。它通常使得探索性的开发和调试更加便捷。在 Notebook 环境,你能够交互式地在其中编写你的代码、运行代码、查看输入、可视化数据并查看后果,应用起来非常灵活。

在数据开发畛域,Notebook 广泛应用于数据清理和转换、数值模仿、统计建模、数据可视化、构建和训练机器学习模型等方面。

然而显然,做数据开发,只有 Notebook 是不够的。

在火山引擎 DataLeap 数据研发平台,咱们提供了工作开发、公布调度、监控运维等一系列能力。咱们将 Notebook 作为一种工作类型,退出了数据研发平台,使用户既能领有 Notebook 交互式的开发体验,又能享受一站式大数据研发治理套件提供的便当。如果还不够直观的话,试想以下场景:

在交互式运行和可视化图表的加持下,你很快就调试实现了一份 Notebook。简略整顿了下代码,依据应用到的数据配置了上游工作依赖,上线了周期调度,并棘手挂了报警。之后,基本上就不必管这个工作了:不须要每天手动查看上游数据是否就绪;不须要每天来点击运行,因为调度零碎会主动帮你执行这个 Notebook;执行失败了有报警,能够间接上平台来解决;上游数据出错了,能够请他们发动深度回溯,对立修数。

选型

2019 年末,在决定要反对 Notebook 工作的时候,咱们调研了许多 Notebook 的实现,包含 Jupyter、Polynote、Zeppelin、Deepnote 等。Jupyter Notebook 是 Notebook 的传统实现,它有着极其丰富的生态以及宏大的用户群体,置信许多人都用过这个软件。

事实上,在字节跳动数据平台倒退晚期,就有了在物理机集群上对立部署的 Jupyter(基于多用户计划 JupyterHub),供外部的用户应用。思考到用户习惯和其弱小的生态,Jupyter 最终成为了咱们的抉择。

Jupyter Notebook 是一个 Web 利用。通常认为其有两个外围的概念:Notebook 和 Kernel。

  • Notebook 指的是代码文件,个别在文件系统中存储,后缀名为 ipynb。Jupyter Notebook 后端提供了治理这些文件的能力,用户能够通过 Jupyter Notebook 的页面创立、关上、编辑、保留 Notebook。在 Notebook 中,用户以一个一个 Cell 的模式编写代码,并按 Cell 运行代码。Notebook 文件的具体内容格局,可参考 The Notebook file format。
  • Kernel 是 Notebook 中的代码理论的运行环境,它是一个独立的过程。每一次「运行」动作,产生的成果是单个 Cell 的代码被运行。具体来讲,「运行」就是把 Cell 内的代码片段,通过 Jupyter Notebook 后端以特定格局发送给 Kernel 过程,再从 Kernel 承受特定格局的返回,并反馈到页面上。这里所说的「特定格局」,可参考 Messaging in Jupyter。

在 DataLeap 数据研发平台,开发过程围绕的外围是工作。用户能够在我的项目下的工作开发目录创立子目录和工作,像 IDE 一样通过目录树治理其工作。

Notebook 也是一种工作类型,用户能够启动一个独立的工作 Kernel 环境,像开发其余一般工作一样应用 Notebook。

技术路线在 Jupyter 的生态下,除了 Notebook 自身,咱们还留神到了很多其余组件。彼时,JupyterLab 正在逐步取代传统的 Jupyter Notebook 界面,成为新的规范。JupyterHub 应用宽泛,是多用户 Notebook 的版本答案。脱胎于 Jupyter Kernel Gateway(JKG)的 Enterprise Gateway(EG),提供了咱们须要的 Remote Kernel(上述的独立工作 Kernel 环境)能力。2020 上半年,咱们基于下面的三大组件,进行二次开发,在字节跳动数据研发平台公布了 Notebook 工作类型。整体架构预览如图。

JupyterLab

前端这一侧,咱们抉择了基于更现代化的 JupyterLab 进行革新。咱们刨去了它的周边视图,只留下了两头的 Cell 编辑区,嵌入了 DataLeap 数据研发的页面中。

为了和 DataLeap 的视觉格调更符合,从 2020 下半年到 2021 年初,咱们还针对性地改良了 JupyterLab 的 UI。这其中包含将整个 JupyterLab 应用的代码编辑器从 CodeMirror 对立到 DataLeap 数据研发应用的 Monaco Editor,同时还接入了 DataLeap 提供的 Python & SQL 代码智能补全性能。

额定地,咱们还开发了定制的可视化 SDK,使得用户在 Notebook 上计算失去的 Pandas Dataframe 能够接入 DataLeap 数据研发曾经提供的数据后果剖析模块,间接在 Notebook 外部做一些简略的数据探查。

JupyterHub

JupyterHub 提供了可扩大的认证鉴权能力和环境创立能力。首先,因为用户较多,因而为每个用户提供独自的 Notebook 实例不太事实。因而咱们决定,按 DataLeap 我的项目来切分 Notebook 实例,同我的项目下的用户共享一个实例(即一个我的项目实际上在 JupyterHub 是一个用户)。这也与 DataLeap 的我的项目权限体系放弃了统一。留神这里的「Notebook 实例」,在咱们的配置下,是拉起一个运行 JupyterLab 的环境。另外,因为咱们会应用 Remote Kernel,所以在这个环境内,并不提供 Kernel 运行的能力。

在认证鉴权方面,咱们让 JupyterHub 申请咱们业务后端提供的验证接口,判断登录态的用户是否具备申请的对应 DataLeap 我的项目的权限,以实现权限体系对接。在环境创立方面,咱们通过 OpenAPI 对接了字节跳动外部的 PaaS 服务,为每一个应用了 Notebook 工作的 DataLeap 我的项目调配一个 JupyterLab 实例,对应一个 PaaS 服务。

因为间接新建一个服务的流程较长,速度较慢,因而咱们还额定做了池化,事后启动一批服务,当有新我的项目的用户登入时间接调配。

Enterprise Gateway

Jupyter Enterprise Gateway 提供了在分布式集群(包含 YARN、Kubernetes 等)外部启动 Kernel 的能力,并成为了 Notebook 到集群内 Kernel 的代理。

在原生的 Notebook 体系下,Kernel 是 Jupyter Notebook / JupyterLab 中的一个本地过程;对于启用了 Gateway 性能的 Notebook 实例,所有 Kernel 相干的性能的申请,如获取 Kernel 类型、启动 Kernel、运行 Cell、中断等,都会被代理到指定的 Gateway 上,再由 Gateway 代理到具体集群内的 Kernel 里,造成了 Remote Kernel 的模式。

这样带来的益处是,Kernel 和 Notebook 拆散,不会相互影响:例如某个 Kernel 运行占用物理内存超限,不会导致其余同时运行的 Kernel 挂掉,即便他们都通过同一个 Notebook 实例来应用。

EG 自身提供的 Kernel 类型,和字节跳动外部零碎并不齐全兼容,须要咱们自行批改和增加。咱们首先以 Spark Kernel 的模式对接了字节跳动外部的 YARN 集群。Kernel 以 PySpark 的模式在 Cluster 模式的 Spark Driver 运行,并提供一个默认的 Spark Session。用户能够通过在 Driver 上的 Kernel,间接发动运行 Spark 相干代码。同时,为了满足 Spark 用户的应用习惯,咱们额定提供了在同一个 Kernel 内穿插运行 SQL 和 Scala 代码的能力。

2020 下半年,随同着云原生的浪潮,咱们还接入了字节跳动云原生 K8s 集群,为用户提供了 Python on K8s 的 Kernel。咱们还扩大了很多自定义的能力,例如反对自定义镜像,以及针对于 Spark Kernel 的自定义 Spark 参数。

稳定性方面,在过后的版本,EG 存在异步不够彻底的问题,在 YARN 场景下,单个 EG 过程甚至只能跑起来十几个 Kernel。咱们发现了这一问题,并实现了各处所需的 async 逻辑革新,保障了服务的并发能力。另外,咱们利用了字节跳动外部的负载平衡(nginx 七层代理集群)能力,部署多个 EG 实例,并指定单个 JupyterLab 实例的流量总是打到同一个 EG 实例上,实现了根本的 HA。

架构降级

当应用 Notebook 的我的项目日渐减少时,咱们发现,运行中的 PaaS 服务切实太多了,之前的架构造成了

  1. 部署麻烦。全量降级 JupyterLab 较为苦楚。只管有降级脚本,然而通过 API 操作降级服务,可能因为镜像构建失败等起因,会造成卡单景象,因而每次全量降级后都是人工巡检查看降级状态,卡住的降级单人工点击下一步。同时因为降级不同服务不会复用配置雷同的镜像,所以有多少服务就要构建多少次镜像,当服务数量达到一定量级时,咱们的批量降级申请可能把外部镜像构建服务压垮。
  2. JupyterLab 须要一直的依据用户增长(我的项目增长)进行扩容,一旦事后启动好的资源池不够,就会存在新我的项目里有用户关上 Notebook,须要经验整个 JupyterLab 服务创立、环境拉起的流程,速度较慢,影响体验。而且,JupyterLab 数量微小后,遇到 bad case 的几率增高,有些问题不易复现、十分偶发,重启 / 迁徙即可解决,然而在遇到的时候,用户体验受影响较大。
  3. 运维艰难。当用户 JupyterLab 可能呈现问题,为了找到对应的 JupyterLab,咱们须要先依据我的项目对应到 JupyterHub user,而后依据 user 找到 JupyterHub 记录的服务 id,再去 PaaS 平台找服务,进 webshell。
  4. 当然,还有资源的节约。尽管每个实例很小(1c1g),然而数量很多;有些我的项目并不总是在应用 Notebook,但 JupyterLab 仍然运行。
  5. 稳定性存在问题。一方面,JupyterHub 是一个单点,降级须要先起后停,挂了有危险。另一方面,EG 入流量通过特定负载平衡策略,自身是为了使 JupyterLab 固定往一个 EG 申请。在 EG 降级时,JupyterLab 申请的终端会随之扭转,极其状况下有可能造成 Kernel 启动屡次的状况。

基于简化运维老本、升高架构复杂性,以及进步用户体验的思考,2021 上半年,咱们对整体架构进行了一次改进。在新的架构中,咱们次要做了以下改良,大抵简化为下图

  1. 移除 JupyterHub,将 JupyterLab 改为多实例无状态常驻服务,并实现对接 DataLeap 的多用户鉴权。
  2. 革新本来落在 JupyterLab 本地的数据存储,包含用户自定义配置、Session 保护和代码文件读写。
  3. EG 反对长久化 Kernel,将 Kernel 近程环境元信息长久化在远端存储 (MySQL) 上,使其重启时能够重连,且 JupyterLab 能够晓得某个 Kernel 须要通过哪个 EG 连贯。

鉴权 & 平安

单用户的 Jupyter Notebook / JupyterLab 的鉴权绝对简略(实际上 JupyterLab 间接复用了 Jupyter Notebook 的这套代码)。例如,应用默认命令启动时,会主动生成一个 token,同时主动拉起浏览器。有了 token,就能够任意地拜访这个 Notebook。

事实上,JupyterHub 也是起到了保护 token 的作用。前端会发动一个获取 token 的 API 申请,再拿着获取的 token 申请通过 JupyterHub proxy 到实在的 Notebook 实例。而咱们间接为 Jupyter Notebook 减少了 Auth 的性能,实现了在 JupyterLab 单实例上实现这套鉴权(此时,应用了 DataLeap 服务签发的 Token)。

最初,因为所有用户会共享同一组 JupyterLab,咱们还须要禁止一些接口的调用,以保证系统的平安。最典型的接口包含敞开服务(Shutdown),以及批改配置等。后续 Notebook 所需的配置,转由前端保留在浏览器内。

代码 & Session 长久化

Jupyter Notebook 应用 File Manager 治理 Contents 相干读写(对咱们而言次要是 Notebook 代码文件),原生行为是将代码存储在本地,多个服务实例之间无奈共享同一份代码,而且迁徙时可能造成代码失落。

为了防止代码失落,咱们的做法是,把代码按我的项目别离存储在 OSS 上并间接读写,同时解决了一些因为代码文件元信息失落,并发编辑导致的其余问题。例如,当多个页面拜访同一份代码文件时,都会从 OSS 获取最新的 code,当用户存储时,前端会获取最新的代码文件,比拟该文件的批改工夫同前端存储的是否统一,如果不同,则阐明有其它页面存储过,会提醒用户抉择笼罩或是复原。

Notebook 应用 Session 治理用户到 Kernel 的连贯,例如前端通过 POST /session 接口启动 Kernel,GET /session 查看以后运行中的 Kernel。在 Session 解决方面,原生的 Notebook 应用了原生的 sqlite(in memory),见代码。只管咱们并不明确这么做的意义何在(毕竟原生的 Notebook 重启,所有都没了),但咱们顺着这个原生的表构造继续前进,引入了 sqlalchemy 对接多种数据库,将 Session 数据搬到了 MySQL。

另一方面,因为咱们启动的 Kernel,有一部分波及 Spark on YARN,启动速度并不现实,因而晚期咱们减少了性能,若某个 path 已有正在启动的 Kernel,则等其启动结束而不是再启动一个新的。这个性能原先应用内存中的 set 实现,当初也移植到了数据库上,通过 sqlalchemy 来拜访。

Kernel 长久化 & 拜访

在 Remote Kernel 的场景下,一个 JupyterLab 须要晓得它的某个 Kernel 具体在哪个 EG 上。在之前一个我的项目一个 JupyterLab 的状态下,咱们通过负载平衡简略解决这个问题:即一个 Server 总是只拜访同一个 Gateway。然而当 JupyterLab 成为无状态服务时,用户并非固定只拜访一个 JupyterLab,也就不能保障总拜访用户 Kernel 所在的 EG。

另一个状况是,当 JupyterLab 或 EG 重启时,其上的 Kernel 都会敞开。当咱们降级相干服务时,总是须要告诉用户筹备重启 Kernel。因而,为了实现降级对用户无感,咱们在 EG 这层开发了长久化 Kernel 的个性。

Kernel Gateway 在启动 Kernel 时,记录了对于 Kernel 的一些元信息,包含启动参数、连贯 Kernel 应用的 IP/Port 等。有了这些信息,当一个 Kernel Gateway 重启且 Remote Kernel 不敞开,就有方法从新连贯上。本来这些信息默认在内存 dict 中保护,开源仓库中有一套存储在本地文件的计划;基于这套计划,咱们扩大了自研的存储到 MySQL 的计划。

在多实例的场景下,每一个 EG 实例仍然会接管的各自的一部分 Kernel,并记录每个 Kernel 由谁接管(探活、Cull Idle、连贯应用等)。在其敞开前,须要革除接管信息,以便下次启动或其余实例启动时捞起。

为了缩小 client(失常是 JupyterLab) 任意拜访 EG 的状况,一方面咱们沿用了负载平衡的策略,另一方面 JupyterLab 在申请 Kernel 相干操作前,会先申请 EG 一次,由 EG 决定 JupyterLab 具体申请哪一个 EG IP/Port。

当 EG 服务自身重启或者降级时,会在过程退出之前去革除接管信息。当页面持续拜访时,JupyterLab 服务将会随机散发相应申请,由其它的 EG 服务持续接管。

收益

架构降级简化后,整套 Notebook 服务的稳定性取得了极大的晋升。因为实现了用户无感知的降级,不仅晋升了用户的应用体验,运维的老本也同时升高了。部署的老本也极大地升高,包含算力、人力的节俭。因为剥离了外部依赖,咱们得以将这套架构部署在各种私有云、私有化场景。

调度计划

在后面,咱们重点关注了怎么将 Jupyter 这套利用嵌入到 DataLeap 数据研发中。这只笼罩了咱们 Notebook 工作的页面调试性能。实际上,同时作为一个调度零碎,咱们还须要关怀怎么调度一个 Notebook 工作。

首先,是和所有其余工作类型雷同的局部:当 Notebook 工作所配置的上游依赖工作全副运行结束,开始拉起本次 Notebook 工作的运行。咱们会依据工作的版本创立一个工作的快照,咱们称之为工作实例,并将其提交到咱们的执行器中。

对于 Notebook 工作,在实例运行前,咱们会依据 Notebook 工作对应的版本,从 OSS 拷贝一份 Notebook 代码文件,用于执行。在具体的执行流程中,咱们应用了 Jupyter 生态中的 nbconvert 来实现在没有 Jupyter 利用的前提下在后盾运行这份 Notebook 文件,并将运行后失去的后果 Notebook 文件传回 OSS。nbconvert 的工作原理比较简单,且复用了 Jupyter 底层的代码,具体如下:

  1. 依据指定的 Kernel Manager 或 Notebook 文件里的 Kernel 类型创立对应的 Kernel Manager;
  2. Kernel Manger 创立 Kernel Client,并启动一个 Kernel;
  3. 遍历 Notebook 文件里的 Cell,调用 Kernel Client 执行 Cell 里的代码;
  4. 获取输入后果,依照 nbformat 指定的 schema 填入 NotebookNode,并保留。

下图是调度执行 Notebook 的 Kernel 运行流程和通过调试走 EG 的 Remote Kernel 运行流程比照。能够看出,它们的链路并没有实质上的区别,只不过是在调度执行时,不须要交互式的 Kernel 通信,以及 EG 的这些 Kernel Launcher 应用了 embed_kernel 在同过程内启动 Kernel 而已。走到最底层,它们都是应用了 ipykernel 的(其余语言 kernel 同理)。

将来工作

Notebook 工作已成为字节跳动外部应用较为高频的工作类型。在火山引擎,咱们也能够购买 DataLeap,即一站式大数据研发治理套件,开明交互式剖析的版本,应用到 DataLeap 的 Notebook 工作。

有的时候,咱们发现,咱们有比 Jupyter 社区快半步的中央:比方基于 asyncio 异步优化的 EG;比方给 Notebook 减少 Auth 能力。但社区的倒退也很快:比方社区将 Jupyter 后端相干的代码实现,对立收敛到了 jupyter_server;比方 EG 作者提出的 Kernel Provider 计划,令 jupyter_server 能够间接反对 Remote Kernel。

因而咱们并未就此止步。目前,这套 Notebook 服务和 DataLeap 数据研发的其余前后端服务,仍存在着割裂。将来,咱们心愿精简架构,实现彻底的整合,使 Notebook 并非以嵌入的模式交融在 DataLeap 的产品中,而是使其原生就在 DataLeap 数据研发中被反对,带来更好的性能,同时又保留所有 Jupyter 生态带来的弱小性能。

另一方面,随着 DataLeap 数据研发平台对流式数据开发的反对,咱们也心愿借助 Notebook 实现用户对流式数据的摸索、调试、可视化等性能的需要。置信不久的未来,Notebook 可能实现流批一体化,来服务更加宽泛的用户群体。

对于咱们

火山引擎大数据研发治理套件 DataLeap 一站式数据中台套件,帮忙用户疾速实现数据集成、开发、运维、治理、资产、平安等全套数据中台建设,帮忙数据团队无效的升高工作老本和数据保护老本、开掘数据价值、为企业决策提供数据撑持。

点击跳转 大数据研发治理套件 DataLeap 理解更多

退出移动版