更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群
当使用 Notebook 的项目日渐增加时,火山引擎 DataLeap 研发团队发现运行中的 PaaS 服务实在太多了,之前的架构有如下缺点:
-
部署麻烦。全量升级 JupyterLab 较为痛苦。尽管有升级脚本,但是通过 API 操作升级服务,可能由于镜像构建失败等原因,会造成卡单现象。
-
JupyterLab 需要不断的根据用户增长(项目增长)进行扩容,一旦预先启动好的资源池不够,就会存在新项目里有用户打开 Notebook,需要经历整个 JupyterLab 服务创建、环境拉起的流程,速度较慢,影响体验。
-
运维困难。当用户 JupyterLab 可能出现问题,为了找到对应的 JupyterLab,我们需要先根据项目对应到 JupyterHub user,然后根据 user 找到 JupyterHub 记录的服务 id,再去 PaaS 平台找服务,进 webshell。
-
当然,还有资源的浪费。虽然每个实例很小(1c1g),但是数量很多;有些项目并不总是在使用 Notebook,但 JupyterLab 依然运行。
-
稳定性存在问题。一方面,JupyterHub 是一个单点,升级需要先起后停,挂了有风险。另一方面,EG 入流量经过特定负载均衡策略,本身是为了使 JupyterLab 固定往一个 EG 请求。在 EG 升级时,JupyterLab 请求的终端会随之改变,极端情况下有可能造成 Kernel 启动多次的情况。
基于简化运维成本、降低架构复杂性,以及提高用户体验的考虑,2021 上半年,火山引擎 DataLeap 研发团队对整体架构进行了一次改良。在新的架构中,火山引擎 DataLeap 研发团队主要做了以下改进,大致简化为下图:
-
移除 JupyterHub,将 JupyterLab 改为多实例无状态常驻服务,并实现对接火山引擎 DataLeap 的多用户鉴权。
-
改造原本落在 JupyterLab 本地的数据存储,包括用户自定义配置、Session 维护和代码文件读写。
-
EG 支持持久化 Kernel,将 Kernel 远程环境元信息持久化在远端存储(MySQL)上,使其重启时可以重连,且 JupyterLab 可以知道某个 Kernel 需要通过哪个 EG 连接。
(图:火山引擎 DataLeap 下改进版 Notebook 整体架构)
单用户的 Jupyter Notebook / JupyterLab 的鉴权相对简单(实际上 JupyterLab 直接复用了 Jupyter Notebook 的这套代码)。例如,使用默认命令启动时,会自动生成一个 token,同时自动拉起浏览器。有了 token,就可以任意地访问这个 Notebook。
事实上,JupyterHub 也是起到了维护 Token 的作用。前端会发起一个获取 Token 的 API 请求,再拿着获取的 Token 请求通过 JupyterHub proxy 到真实的 Notebook 实例。
(图:前: JupyterHub 提供的 auth 能力;后:实现了 auth 功能的 JupyterLab)
最后,由于所有用户会共享同一组 JupyterLab,火山引擎 DataLeap 研发团队还需要禁止一些接口的调用,以保证系统的安全。最典型的接口包括关闭服务(Shutdown),以及修改配置等。后续 Notebook 所需的配置,转由前端保存在浏览器内。
Jupyter Notebook 使用 File Manager (https://github.com/jupyter-server/jupyter_server/blob/main/jupyter_server/services/contents/filemanager.py)管理 Contents 相关读写,原生行为是将代码存储在本地,多个服务实例之间无法共享同一份代码,而且迁移时可能造成代码丢失。
为了避免代码丢失,火山引擎 DataLeap 研发团队的做法是,把代码按项目分别存储在 OSS 上并直接读写,同时解决了一些由于代码文件元信息丢失,并发编辑导致的其他问题。
(图:按项目分别持久化代码到 OSS)
Notebook 使用 Session 管理用户到 Kernel 的连接,例如前端通过 POST /session
接口启动 Kernel,GET /session
查看当前运行中的 Kernel。在 Session 处理方面,原生的 Notebook 使用了原生的 sqlite(in memory),见代码(https://github.com/jupyter-server/jupyter_server/blob/main/jupyter_server/services/sessions/sessionmanager.py)。
(图:Session 表)
Kernel Gateway 在启动 Kernel 时,记录了关于 Kernel 的一些元信息,包括启动参数、连接 Kernel 使用的 IP/Port 等。有了这些信息,当一个 Kernel Gateway 重启且 Remote Kernel 不关闭,就有办法重新连接上。 原本这些信息默认在内存 dict 中维护,开源仓库中有一套存储在本地文件的方案;基于这套方案,火山引擎 DataLeap 研发团队扩展了自研的存储到 MySQL 的方案。
(图:EG 访问流程示意图)
当 EG 服务本身重启或者升级时,会在进程退出之前去清除接管信息。当页面继续访问时,JupyterLab 服务将会随机分发相应请求,由其它的 EG 服务继续接管。
架构升级简化后,整套火山引擎 DataLeap 下的 Notebook 服务的稳定性获得了极大的提升。由于实现了用户无感知的升级,不仅提升了用户的使用体验,运维的成本也同时降低了。
点击跳转 大数据研发治理DataLeap 了解更多