背景
Jupyter 是兼备优秀的编程体验和交互体验的在线 IDE,它能让你的代码不再受限于自己的电脑环境,也不只是编程语言本身,而是可以结合数学公式、Markdown、命令行等各种语法和指令,在更高性能的服务器以更灵活的交互方式,随处运行,随时可见结果。也正因为这些特性,它在教学、大数据分析、机器学习等领域都能助开发者一臂之力
在最近一年维护和使用 Jupyter 服务的过程中,我部署 Jupyter 的方式也经历了从 linux 服务器单点,到 minikube 本地部署模拟集群,再到生产环境 k8s 正式部署集群的升级过程。之所以要将 Jupyter 从单点升级成集群,主要是为了解决单点会遇到的可靠性、可扩展性和可维护性等问题
上面的问题要解决,首先需要一套可以管理集群的基础服务,这个服务就是 Kubernetes ,而借助 Jupyter 官方提供的 helm chart,几行配置几条指令,就可以部署成集群了,“理论上”整个过程可以非常顺滑
当然,上线生产之前必不可少的过程是测试。在这篇文章中,我们就以自己电脑作为基本环境,借助 minikube 和 k8s 生态下的工具, 部署一套类似生产环境的 Jupyter
事不宜迟,让我们先从了解 Jupyter 服务开始,一步步深入了解如何在本机玩转 Jupyter 集群吧
Jupyter 生态
Jupyter 在组件通信、页面交互和后台运行环境上,都具有各司其职的组件,这也形成了 Jupyter 独一无二的生态
图片来源若从最关键的用户使用场景入手,最重要的组件有以下四个:
- JupyterHub: 登录页面,只提供简单的用户信息输入框( 不过默认的 Spawner 和 Authticator 也在其源码中 )
- JupyterLab: 新一代的 Notebook 编辑器,界面功能更丰富,且支持通过插件系统扩展更多功能(如 git、资源查看器等)
- Spawner: 登录 JupyterHub 后实际运行的 IDE(主要是 Notebook 或者 JupyterLab ),IDE 可以自定义运行环境: 跑在本地、 k8s 或者 yarn 等等。一个用户 IDE 进程也可以称作 Jupyter Server
- Ipykernel: 用户运行每一个 Notebook,又会在 Spawner 运行的服务器上启动一个个负责运行代码的进程。它既是进程,也是服务器上预装的不同开发环境( 比如可选择在不同 Python 版本作为运行代码的环境 )
只是简单介绍这些组件的概念,并不能讲清楚它们之间的协同和交互机制。相信通过接下来的实践,它们各自的发挥的作用都会在我们面前清晰展现出来
环境准备
既然要在 k8s 部署 Jupyter,第一步当然需要把 k8s 先启动起来
在自己电脑启动 k8s 可选的工具有 minikube, kind, k3s, microk8s 等,它们实际启动集群的过程是差不多的。这里笔者从功能的完善程度考虑,选择了 minikube
在文章最后会提到 k3s 也可作为在电脑资源受限情况下的备选
minikube
minikube 是一个在本地快速启动 k8s 集群的工具,由 kubernetes 社区开源,支持多种虚拟化运行环境(Docker、Hyper-V、VirtualBox、podman 等)。它基于一个轻量级虚拟机 coreboot 启动,因此 minikube 也支持跨平台
minikube 可执行文件可以直接从 官方仓库 或者 官方网站 下载
Docker 镜像源配置
注: 由于 minikube 网络插件的依赖,会导致非 root 权限运行的 podman 无法启动 minikube,笔者为测试 root 的 podman,目前还是建议使用 Docker
Docker 的具体的安装方式这里就不赘述了。不过还是要提一下镜像源的配置
网易云、中科大等之前常用的比较官方的镜像源现在都用不了,其他第三方可用的镜像源可以 参考这里 或 这里, 目前可用的部分镜像源如下:- // vim /etc/docker/daemon.json
- {
- "registry-mirrors": [
- "https://docker-0.unsee.tech",
- "https://docker.1ms.run",
- "https://docker.m.daocloud.io"
- "https://docker.xuanyuan.me",
- ]
- }
复制代码注: 一切网络问题都不是等等就能好的,再等也是浪费时间,这也是在开始就要把国内镜像源这种信息告诉大家的原因
minikube 安装和启动
启动 minikube 遇到的第一个坑,就是经典的国内网络问题: 下载镜像和二进制文件时不太顺畅
首先 minikube 运行的基础系统镜像 kicbase 会按照 docker.io、 gcr.io 和 github release 的顺序尝试下载。然而这些下载地址都在外网,都可能下载不成功
其次,minikube 还会下载 kubectl, kubelet 和 kubeadm 这三个用于访问和管理 k8s 集群的可执行文件(下载后放在虚拟机内部)在设置了 image-mirror-country=cn 参数后,下载地址会被替换成 kubernetes.oss-cn-hangzhou.aliyuncs.com,但是这个地址并没有提供这些文件,所以还需要配置正确的 binary-mirror 参数
(不得不说坑是真的多啊)
正是因为有这些问题,建议通过源码编译来生成 minikube 二进制文件,方便快速修正镜像下载问题- # linux 下载 minikube
- curl -LO https://github.com/kubernetes/minikube/releases/download/v1.36.0/minikube-linux-amd64
- # windows
- curl -LO https://github.com/kubernetes/minikube/releases/download/v1.36.0/minikube-windows-amd64.exe
- # github 加速
- curl -LO https://ghfast.top/https://github.com/kubernetes/minikube/releases/download/v1.36.0/minikube-linux-amd64
- curl -LO https://ghfast.top/https://github.com/kubernetes/minikube/releases/download/v1.36.0/minikube-windows-amd64.exe
- # 安装 minikube
- sudo install minikube-linux-amd64 /usr/local/bin/minikube
- # 通过源码编译 minikube
- git clone https://github.com/kubernetes/minikube
- git checkout tags/v1.36.0
- ## 替换 docker.io/kicbase/stable 下载镜像源
- sed -i "s#"docker.io/#"docker.1ms.run/#g" pkg/drivers/kic/types.go
- ## 编译
- make
- mv out/minikube /usr/local/bin/
- # 启动 k8s 1.33.1 版本
- minikube start --kubernetes-version=v1.33.1
- # “最终版”启动 k8s 参数 (适配国内网络)
- # --v 和 --log_file: 增加日志输出,并打印到指定日志文件中,方便定位问题
- minikube start --kubernetes-version=v1.33.1 --iso-url https://ghfast.top/https://github.com/kubernetes/minikube/releases/download/v1.35.0/minikube-v1.35.0-arm64.iso --image-mirror-country=cn --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers --binary-mirror https://files.m.daocloud.io/dl.k8s.io/release --v=8 --log_file /tmp/test.log
- # 扩展: 限定运行资源、磁盘大小等
- minikube start --cpus=2 --memory=4096 --disk-size=20g ...
- # 扩展: 指定镜像下载路径
- export MINIKUBE_HOME=/opt/minikube
复制代码 顺利启动 minikube 后,我们在命令行可以通过 minikube 虚拟机内部的 kubectl 来访问集群,或者为了操作更方便可以在主机也安装 kubectl( minikube 启动时已经在本机设置好了 ~/.kube/config )- # kubectl 安装
- curl -LO https://dl.k8s.io/release/v1.33.0/bin/linux/amd64/kubectl
- curl -LO https://dl.k8s.io/release/v1.33.0/bin/windows/amd64/kubectl.exe
- # 国内源
- curl -LO https://files.m.daocloud.io/dl.k8s.io/release/v1.33.0/bin/linux/amd64/kubectl
- curl -LO https://files.m.daocloud.io/dl.k8s.io/release/v1.33.0/bin/windows/amd64/kubectl.exe
- # 或者通过 k8s 源码安装
- git clone https://github.com/kubernetes/kubernetes
- make kubectl
- mv _output/local/bin/linux/amd64/kubectl /usr/local/bin
- # 获取所有 pod
- kubectl get pods --all-namespaces
- # 使用 minikube 自带的 kubectl
- minikube kubectl -- get pods --all-namespaces
复制代码 通过 kubectl get pods -A 查看所有 pod,可以看到 minikube 启动了一套标准的 k8s 组件,简单说明每个组件的作用

图片来源
- CoreDNS: k8s 集群内部的 DNS 服务,负责提供 k8s 内服务的访问地址,前身是 kube-dns,现在发展成 CNCF(Cloud Native Community Foundation 云原生基金会) 中的独立项目
- ETCD: k8s 的元数据服务,记录了当前集群的所有节点、配置、服务、密钥等等信息
- kube-apiserver: 提供k8s资源( pod, deployment, service 等)的 restful api 接口
- kube-controller-manager: 通过 api 对集群资源进行管理和控制,在资源出现异常的时候自动恢复
- kube-proxy: 在每个 k8s 节点(node)上运行,负责 监测 apiserver 收到的用户请求,并将请求转发到具体的 Pod 进行处理
- kube-scheduler: 负责将 Pending 状态的 pod 分配到最合适的 node 上运行
- storage-provisioners: 在服务需要挂载存储目录的时候,自动从本地分配一块目录空间给服务使用
dashboard
dashboard 是 kubernetes 官方提供的 k8s 集群看板,通过它能够看到集群的基本状态、资源、日志等,尽管功能简单,还是建议在首次启动时候打开它,方便后续查看 pod 日志定位问题
minikube 内置 dashboard, 执行 minikube dashboard 即可打开
helm
在不使用 k8s,只用 docker 如何启动,最简单的方案通常会用到 docker compose,在 docker-compose.yaml 配置文件中编写集群每个节点部署的服务和配置,比如 flink 的官方示例,一个 jobmanager 和一个 taskmanager 组成的集群
但这种方式对于生产环境部署大规模集群的目标,就有点力所不及了。自动发现和恢复故障节点、自动扩缩容、动态升级和配置变更等方面的支持,还是 k8s 更擅长
当然 k8s 这套生态的门槛也比较高,相关的组件和配置也更多,如何在 k8s 也像 docker compose 一样能丝滑一键启动集群呢?Helm 就是我们的答案
Helm 是 CNCF 社区孵化的集群服务编排和部署工具,它将一个服务在 k8s 部署需要的服务以及依赖的资源,抽象成了可以通过 golang template 批量生成的配置,配置的格式也统一为 yaml
从 2016 年发展至今,Helm 已经成为了在 k8s 平台管理服务发行包和发布服务的主流标准
Helm 的几个概念这里也简单介绍下,稍做了解就好
- chart: 服务定义,包括服务名、服务版本、依赖 k8s 版本
- value: 启动服务所需所有资源的配置默认值,可在启动时加上 -f new.yaml 覆盖默认配置文件
- template: k8s 相关资源( deployment, service, pvc, configmap 等)的配置模板,和 values 结合生成完整的资源清单
- release: chart 可以发布到 k8s 的版本
- repo: 用于存放所有 chart release 打包文件的服务器
图片来源大部分支持云原生的开源组件,官方都会提供 Helm chart,让我们只修改少量的配置,即可完成集群部署
Helm 的安装也非常简单,只需下载一个二进制文件- # linux
- curl -LO https://get.helm.sh/helm-v3.18.2-linux-amd64.tar.gz
- # mac
- brew install helm@stable
- curl -LO https://get.helm.sh/helm-v3.18.2-darwin-arm64.tar.gz
- # windows
- choco install kubernetes-helm
- https://get.helm.sh/helm-v3.18.2-windows-amd64.zip
复制代码 Jupyter
环境准备工作终于万事俱备,各位久等,我们终于要来启动 Jupyter 服务了
这里我们需要用到 Jupyter 官方提供的 chart zero-to-jupyterhub-k8s
启动服务
- # 在本地添加 jupyter 官方 repo
- helm repo add jupyterhub https://hub.jupyter.org/helm-chart
- helm repo update
- # 启动
- # vim start.sh
- RELEASE=jhub
- NAMESPACE=jhub
- VERSION=4.2.0
- # 彻底清理上次启动 Jupyter 的所有资源
- helm uninstall ${RELEASE} -n ${NAMESPACE}
- kubectl delete daemonsets,replicasets,services,deployments,pods,jobs,rc,ingress,persistentvolumes,persistentvolumeclaims --all --namespace=jhub
- # 将 Jupyter 组件安装到 jhub 的命名空间(namespace)下
- helm upgrade --cleanup-on-fail \
- --install $RELEASE jupyterhub/jupyterhub \
- --namespace $NAMESPACE \
- --create-namespace \
- --timeout 600s \
- --version $VERSION
复制代码 成功启动后查看 jhub 这个 namespace 下相关的 pod 如下:
默认方式启动后,再通过执行 minikube service proxy-public -n jhub --url ,即可随机开放一个端口以供访问 JupyterHub 登录页面了
Hub 默认使用 DummyAuthenticator 作为用户登录校验器 ( values.yaml 中的 hub.config.JupyterHub.authenticator_class 默认值 ),所以我们可以使用任意用户名 + 任意密码登录
登录后,默认的界面不是最经典的 Notebook 而是功能更现代更丰富的 JupyterLab( JupyterHub 在 2.0 版本后就将用户登录后默认跳转 url 指向了 /lab,参考配置 : c.Spawner.default_url )
Jupyter on k8s
相较于直接通过容器或者运行 JupyterLab 命令启动的 Jupyter,这套 Jupyter helm chart 在哪里做了优化呢?我们可以从启动的 Pod 中一步步探究竟
- hub: JupyterHub,提供登录页面
- continuous-image-puller: 在新的 node 加入 k8s 集群的时候,自动在该 node 拉取 Jupyter 所需要的镜像,避免用户在登录后因拉取 Jupyter 服务镜像长时间等待
- proxy: JupyterHub 的上层代理, 即 configurable-http-proxy
- user-scheduler: 通过 k8s 的调度策略让 Jupyter 相关的 Pod 尽量分配到同个节点,资源更紧凑
- singleuser.storage: 每个用户首次启动时都会自动申请一个 pv, 后续用户重新登录,重启 notebook 也能使用之前已经保存的文件
- singleuser.cpu & singleuser.memory: 单个 Notebook 的资源限制, 默认对 cpu 无限制,内存最多 1G
功能概述
在 JupyterLab 界面你能看到的几个基本功能有 Notebook 、Console 和 Terminal 等
具体功能有机会可以单开一篇详细介绍,这里只提几点注意的
- 你在其中一个 Jupyter Server 中编写的 Notebook,启动其他镜像也能看到,因为是同一个目录
- 打开 Terminal 并执行 pwd 你会看到每个用户的主目录都是 /home/jovyan ,这是因为 Notebook 镜像设置了默认用户就是 jovyan ,但因为每个用户都会申请独立的存储挂载到 Notebook,所以实际目录是分开的
- Notebook 快捷键和 ipykernel 语法非常强大,强烈建议在深度使用 Notebook 之前先了解这些技巧
自定义配置
那么到这里我们已经通过 minikube + helm 在本地成功启动了类生产环境启动的一个 Jupyter 集群了,不妨先喝杯茶
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |