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 服务实在太多了,之前的架构形成了
- 布置麻烦。全量晋级 JupyterLab 较为苦楚。虽然有晋级脚本,可是经过 API 操作晋级服务,或许因为镜像构建失败等原因,会形成卡单现象,因而每次全量晋级后都是人工巡检检查晋级状况,卡住的晋级单人工点击下一步。一起因为晋级不同服务不会复用装备相同的镜像,所以有多少服务就要构建多少次镜像,当服务数量到达必定量级时,咱们的批量晋级恳求或许把内部镜像构建服务压垮。
- JupyterLab 需求不断的依据用户增加(项目增加)进行扩容,一旦预先发动好的资源池不行,就会存在新项目里有用户翻开 Notebook,需求阅历整个 JupyterLab 服务创立、环境拉起的流程,速度较慢,影响体会。并且,JupyterLab 数量巨大后,遇到 bad case 的几率增高,有些问题不易复现、十分偶发,重启/搬迁即可处理,可是在遇到的时分,用户体会受影响较大。
- 运维困难。当用户 JupyterLab 或许出现问题,为了找到对应的 JupyterLab,咱们需求先依据项目对应到 JupyterHub user,然后依据 user 找到 JupyterHub 记载的服务 id,再去 PaaS 渠道找服务,进 webshell。
- 当然,还有资源的糟蹋。虽然每个实例很小(1c1g),可是数量许多;有些项目并不总是在运用 Notebook,但 JupyterLab 仍然运转。
- 稳定性存在问题。一方面,JupyterHub 是一个单点,晋级需求先起后停,挂了有危险。另一方面,EG 入流量经过特定负载均衡战略,本身是为了使 JupyterLab 固定往一个 EG 恳求。在 EG 晋级时,JupyterLab 恳求的终端会随之改动,极端状况下有或许形成 Kernel 发动多次的状况。
依据简化运维本钱、下降架构复杂性,以及进步用户体会的考虑,2021 上半年,咱们对全体架构进行了一次改良。在新的架构中,咱们首要做了以下改善,大致简化为下图
- 移除 JupyterHub,将 JupyterLab 改为多实例无状况常驻服务,并完结对接 DataLeap 的多用户鉴权。
- 改造本来落在 JupyterLab 本地的数据存储,包含用户自定义装备、Session 保护和代码文件读写。
- 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 底层的代码,详细如下:
- 依据指定的 Kernel Manager 或 Notebook 文件里的 Kernel 类型创立对应的 Kernel Manager;
- Kernel Manger 创立 Kernel Client,并发动一个 Kernel;
- 遍历 Notebook 文件里的 Cell,调用 Kernel Client 履行 Cell 里的代码;
- 获取输出成果,按照 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 了解概况!