# 单机多用户 JupyterLab 环境搭建 ## 概述 在这篇短文中,我们记录了如何在使用 [Ubuntu][] `1804 LTS` 操作系统的单台服务器上,建立用户隔离的 [JupyterLab][] Web 环境。 目标是: - 操作系统的用户可以各自不受干扰的使用独立的 [JupyterLab][] - 各个用户的 [Conda][] 环境可以自动的出现在 [JupyterLab][] 的 `Kernel` 列表中 - 每个用户都可以新建自己的 [Conda][] 或者 [venv][] 环境,并在其中安装 [Conda][] 或 [pip][] 软件 - 可以在 `notebook` 中使用本机的 [NVIDIA][] `GPU` ## 准备工作 1. 为服务器安装 [Ubuntu][] `1804` 长期支持版。 如果需要使用 [NVIDIA][] `GPU`,建议安装带有完整图形界面的桌面版操作系统,以简化配置。 1. 确保服务器可以连接到互联网 1. 更新服务器上的软件包,在终端执行: ```sh sudo apt update sudo apt upgrade --auto-remove ``` 1. 如果需要使用 [NVIDIA][] GPU,应安装其驱动。 > ℹ **说明**: > > - 由于我们准备在 [Conda][] 环境中安装配置 [Jupyter][] `Kernel` ,故仅安装 `Driver`,而不在操作系统级别安装 [CUDA][]、[cuDNN][] 等软件包。 > 推荐的方式是:各用户自行在各个环境中使用 [Conda][] 分别安装这些软件包。 > - [NVIDIA][] 官方网站提供的 `Driver` 安装过程比较繁琐,涉及到 `Linux` 的 `modprobe` 与 `initramfs` 的修改,本文不予记载。 > - 所采用的 [NVIDIA][] `Driver` 版本应根据操作系统版本、 `Graphic` 设备和 [CUDA][] 版本要求决定。具体应参考 [NVIDIA][] 网站说明。 本文中,我们使用 [Apt][] 安装 [NVIDIA][] `Driver` 最新的长期支持版(以`430`为例): > ℹ **提示**: > > 我们可通过 获知 `Unix` 驱动的版本支持信息 - 字符界面安装 ```sh sudo add-apt-repository restricted sudo apt-get update sudo apt-get install nvidia-driver-430 ``` - 图形界面安装 1. 打开 “软件和更新” 系统应用 1. 在该应用的 “Ubuntu 软件” 选项卡中,选中 "设备的专有驱动(restricted)" 选项 1. 在该应用的 “附加驱动” 选项卡中,待 “搜索可用驱动” 完毕后,选择 `NVIDIA Corporation` 下最新的长期支持版驱动版本 ## 安装和配置 JupyterHub ### 安装 JupyterHub 我们将**在操作系统级安装** [JupyterHub][],并以 `root` 身份运行该软件,以便启停其它账户的 [JupyterLab][] 进程。 [JupyterHub][] 在启动时执行 `configurable-http-proxy` 进程,用它来反向代理目标用户 [JupyterLab][] 的 `HTTP` 通信。不过,它无法配置 `configurable-http-proxy` 的路径。 所以,我们还需要**在操作系统级安装** `configurable-http-proxy`。 1. 安装 [JupyterHub][] 这是一个 [Python][] 程序,且没有官方 [Apt][] 源。所以,我们用 [pip][] 安装。 1. [apt][] 安装 [Python][] 和 [pip][]: ```sh sudo apt install python3 python3-pip ``` 1. 使用 [pip][] 安装 [JupyterHub][],注意要在操作系统级安装: ```sh sudo -H pip3 install -U jupyterhub ``` 1. 安装 `configurable-http-proxy` 这是一个 [Node.js][] 程序,且没有官方 [Apt][] 源。所以,我们用 [npm][] 安装。 1. [apt][] 安装 [Node.js][] 和 [npm][]: ```sh sudo apt install nodejs npm ``` 1. [npm][] 安装 `configurable-http-proxy`,注意要在操作系统级安装: ```sh sudo npm install -g configurable-http-proxy ``` ### 配置 JupyterHub 我们采用 [JupyterHub][] 默认的 [`single-user`](https://jupyterhub.readthedocs.io/en/stable/getting-started/spawners-basics.html) 模式,其登录用户就是操作系统的账户。 但是,为了实现我们的需求,还需修改配置文件,让 [JupyterHub][] 登录进入用户的 `shell` 之后,以目标用户的身份执行 `jupyterhub-singleuser` 进程,而不是直接在当前进程的环境中直接启动。 1. 生成默认配置文件 生成默认配置文件 `jupyterhub_config.py`,将它移动到系统目录,如 `/etc/jupyterhub`,并重命名为 `config.py`: ```sh jupyterhub --generate-config sudo mkdir -p /etc/jupyterhub sudo cp jupyterhub_config.py /etc/jupyterhub/config.py ``` 1. 修改配置文件 1. 修改 `single-user-spawner` 的默认启动方式(**必须**) 这是**最为重要**的一个步骤! 我们通过该设置,让 [JupyterHub][] 以目标用户的身份登录 `bash`,然后运行 `jupyterhub-singleuser`,由这个进程此启动用户环境中的 [JupyterLab][]。 ```python c.LocalProcessSpawner.shell_cmd = ["bash", "-l", "-c"] ``` 1. 使用 [JupyterLab][] 而非 [Jupyter][] `notebook`(*可选*) ``` python c.Spawner.default_url = "/lab" ``` 1. 设置 [JupyterHub][] `Web` 服务的 `HTTP` 地址(*可选*)。应根据实际网络规划设置,如: ```python c.JupyterHub.ip = "0.0.0.0" ``` 1. 不用确保运行 [JupyterLab][] 的环境处于 Kenerl 在列表中(*可选*) ```python c.Spawner.args = ["--KernelSpecManager.ensure_native_kernel=False"] ``` ### 后台运行 JupyterHub 编辑以下文本到 [systemd][] 配置文件 `jupyterhub.service` ```ini [Unit] Description=Jupyterhub service After=syslog.target network.target [Service] ExecStart=/usr/local/bin/jupyterhub -f /etc/jupyterhub/config.py [Install] WantedBy=multi-user.target ``` 然后将它复制到 [systemd][] 系统配置目录,并修改权限: ```sh sudo cp jupyterhub.service /etc/systemd/system sudo chmod 644 /etc/systemd/system/jupyterhub.service ``` 最后,刷新服务列表并启动该服务: ```sh sudo systemctl daemon-reload sudo systemctl start jupyterhub ``` 如果需要该服务自动运行,执行: ```sh sudo systemctl enable jupyterhub ``` ## 进程调用关系示意图 ```dot digraph G { graph [ label="进程调用关系示意图" ]; browser [shape=box, color=blue]; hub [label="root:/usr/loca/bin/jupyterhub", color=red]; proxy [label="root:/usr/local/bin/configurable-http-proxy", color=red]; bash [label="root: sudo -u USER -i bash -l -c 'jupyterhub-singleuser'", color="lightblue"]; user [label="USER:jupyterhub-singleuser", color=yellowgreen]; lab [label="USER:jupyter-lab", color=green]; kernel [label="USER:python", color=green]; browser -> proxy -> lab [label=http, color=blue]; browser -> kernel [style=dashed, color=gray]; hub -> proxy [label=subprocess]; hub -> bash -> user -> lab -> kernel [label=subprocess]; } ``` ![进程调用关系示意图](https://g.gravizo.com/svg?digraph G { graph [ label="进程调用关系示意图" ]; browser [shape=box, color=blue]; hub [label="root:/usr/loca/bin/jupyterhub", color=red]; proxy [label="root:/usr/local/bin/configurable-http-proxy", color=red]; bash [label="root: sudo -u USER -i bash -l -c 'jupyterhub-singleuser'", color="lightblue"]; user [label="USER:jupyterhub-singleuser", color=yellowgreen]; lab [label="USER:jupyter-lab", color=green]; kernel [label="USER:python", color=green]; browser -> proxy -> lab [label=http, color=blue]; browser -> kernel [style=dashed, color=gray]; hub -> proxy [label=subprocess]; hub -> bash -> user -> lab -> kernel [label=subprocess]; }) ## 用户环境 每个操作系统的普通用户,都可以使用 [JupyterHub][] 的 Web 界面,通过操作系统用户名和密码登录到用户隔离的 [JupyterLab][] 进程。 但是,在此之前,我们还需要为这些用户安装软件、修改配置。 **本章节中记录的后续操作,均假定已使用目标用户身份登录。** ### 安装 Conda 首先,为这个用户安装 [Conda][]。为了安装配置过程的轻便,我们在本例中采用 `Miniconda` 而非 `Anaconda`。 执行: ```sh wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh ``` 然后按照提示,使用默认值进行安装。 当安装程序输出下面的提示时: ```sh installation finished. Do you wish the installer to initialize Miniconda3 by running conda init? [yes|no] [no] >>> ``` 输入 `yes` 安装完成后,执行以下命令或者重新登录: ```sh source ~/.bashrc ``` #### 在 Conda base 环境中安装必备软件 在 [Conda][] `base` 环境中安装 [Jupyter][], [JupyterLab][], [JupyterHub][], [nb_conda][] 等相关软件包,推荐使用 `conda-forge` 通道: ```sh conda install -n base -c conda-forge jupyter jupyterlab jupyterhub nb_conda widgetsnbextension ``` 完成后,安装 [JupyterLab][] 的 Web 扩展 `jupyterlab-manager` (*可选*): ```sh jupyter labextension install @jupyter-widgets/jupyterlab-manager ``` #### 修改 ~/.profile 配置文件 我们还需要将 [Conda][] `base` 环境的 `bin` 目录加到 `PATH` 环境变量。 这是因为,我们修改了 [JupyterHub][] 的配置,并让不同用户使用各种环境中不同路径的 `jupyterhub-singleuser`;而 [JupyterHub][] 的 `SingleUserSpawner` 在登录目标用户 `bash` 并执行 `jupyterhub-singleuser` 时,以及 `jupyterhub-singleuser` 执行 [JupyterLab][] 时, 均无法指定路径。 为此,我们将在该用户的 [Conda][] `base` 环境下安装 [JupyterHub][] 和 [JupyterLab][] ,且需确保 [JupyterHub][] 的 `SingleUserSpawner` 在登录该用户的 `bash` 时将上述 `base` 环境的执行目录自动加入到 `PATH` 环境变量。 这样,`SingleUserSpawner` 和 `jupyterhub-singleuser` 就可以搜索到执行文件了。 我们可以修改 `~/.profile` 配置,使得 [JupyterHub][] 登录到该用户的 `bash` 时,`PATH` 被修改。 方法是: - 在 `~/.profile` 的末尾加上: ```sh PATH="/home/USER/miniconda3/bin:$PATH" ``` 其中 `USER` 表示用户名;如果使用的是 `anaconda`,目录会有所不同。应根据实际情况设置。 - 也可用以下 `shell` 脚本在 `~/.profile` 的最后一行添加上述内容: ```sh echo "PATH=\"$(dirname $CONDA_EXE):\$PATH\"" | tee -a ~/.profile ``` ### 新建 Jupyter Kernel #### Conda 环境自动添加到 Kernels 列表 如果在用户的 [Conda][] 环境安装 `ipython` 与 [nb_conda][],[nb_conda][] 会自动在[Jupyter][](其所在环境也需要安装 [nb_conda][]) 的 `Kernels` 列表中添加其所在的环境。 例如,新建一个名为 `myenv` 的环境,在这个环境安装 `ipython` 和 [nb_conda][],刷新页面,就可以在`Kenerls`列表中看到这个环境了。 新建这样一个环境,命令是: ```sh conda create -n myenv ipython nb_conda ipywidgets ``` 对于已经存在的 [Conda][] 环境,可以随时安装 `ipython` 与 [nb_conda][]。一旦安装,即可被 [JupyterLab][](中的 [nb_conda][])检测到。 #### 手动添加 Python 环境到 Kernels 列表 任何 [Python][] 环境,无论是系统级、用户级、[venv][] 还是 [Conda][] 环境,凡是安装了 `ipython` 软件包的,都可以手动添加到 [Jupyter][] 的 `Kernels` 列表。 方法是在激活的目标环境中执行: ```sh python -m ipykernel install --user --name="myenv" --display-name="My environment" ``` 具体参考: ### 安装软件包 > **⚠ 警告:** > > 为了防止 `base` 环境被意外破坏,我们不应在 `base` 环境中进行工作或者安装/删除/修改软件包。 > > 推荐的方式是:**为每项工作单独维护一个 [Conda][] 环境**。 #### 使用 Conda 安装 常规的方法是通过终端使用 [conda][] 命令指定或者激活目标环境后进行安装。 也可以在 [Jupyter][] 笔记本中,使用“魔术方法”安装,如: `%conda install tqdm` > **ℹ 注意:** > > `%conda` 所使用的 [Conda][] 环境 与笔记正在使用的 `Kernel` 的是一致的。 > > 为保险起见,可执行 `%conda env list` 查看。 #### 使用 pip 安装 常规的方法是通过终端切激活目标环境,然后使用 [pip][] 命令进行安装。 也可以在 [Jupyter][] 笔记本中,使用“魔术方法”安装,如: `%pip install tqdm` > **ℹ 注意:** > > `%pip` 所使用的环境与笔记正在使用的 `Kernel` 的是一致的。 > > 为保险起见,可执行 `%pip --version` 查看。 ## 实例 ### 新建用户环境 在这个实例中,我们新建一个操作系统用户,使用 [JupyterHub][] Web GUI,以该用户身份登录;并且在这个用户名下新建一个 [Conda][] 环境,让 [JupyterLab][] 自动将该环境加入到其 `Kernels` 列表。 1. 新建用户 假定用户名为 `{{USER}}`: ```sh sudo adduser {{USER}} ``` 1. 为新建的用户安装 [Conda][] 以上一步骤中新建的 `{{USER}}` 身份登录。为了提高网速,建议使用[清华大学 Anaconda 镜像](https://mirror.tuna.tsinghua.edu.cn/help/anaconda/): 1. 下载 Miniconda: ```sh wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-latest-Linux-x86_64.sh ``` 1. 安装 Miniconda(参考 *安装 Conda* 一节): ```sh bash Miniconda3-latest-Linux-x86_64.sh ``` 1. 更新 `bash` 环境: ```sh source .bashrc ``` 1. 修改环境变量 `PATH` 和 [Conda][] 配置 1. 修改用户配置文件中的搜索路径环境变量: ```sh echo "PATH=\"$(dirname $CONDA_EXE):\$PATH\"" >> ~/.profile ``` 1. 修改 [Conda][] 的用户配置文件 `~/.condarc`,设置清华大学 `Anaconda` 镜像: ```yaml channels: - defaults show_channel_urls: true default_channels: - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r custom_channels: conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud ``` 1. `base` 环境安装 [jupyter][], [jupyterlab][], [jupyterhub][], [nb_conda][] 等软件包(推荐使用 `conda-forge` 通道): ```sh conda install -n base jupyter jupyterlab jupyterhub nb_conda widgetsnbextension -c conda-forge ``` 1. 安装 [JupyterLab][] 的 几个常用 Web 扩展(*可选*): ```sh jupyter-labextension install @jupyter-widgets/jupyterlab-manager @jupyterlab/toc ``` ### 新建 Conda 环境,安装软件包 **不**建议在 `base` 环境中直接进行工作,以避免破坏运行 [JupyterHub][] 与 [JupyterLab][] 的基础环境。 所以,我们一般会为每一项工作单独新建一个 [Conda][] 环境。 1. 新建 [Conda][] 环境 新建的环境如果具有软件包 `ipython` 和 [nb_conda][],就可以被 [Jupyter][] (其所在环境也需要安装 [nb_conda][])自动检测到。 此处,我们新建一个名为 `mypy36`、使用 [Python][] `3.6` 的环境为例: ```sh conda create --name mypy36 python=3.6 ipython nb_conda ipywidgets ``` 等一小会儿,查看页面,这个环境是不是已经出现在 `Kernels` 列表中了? 1. 安装软件包。 下面,我们在这个环境中安装一个软件包。例如,我们想要安装 [pandas](https://pandas.pydata.org/): 在 [JupyterLab][] 的 `Notebook` 的代码 `cell` 中,或者 `Console` 的命令输入框中,使用魔法命令 `%conda` 或者 `%pip` 安装。该软件包会安装到该 `Notebook` 或者 `Console` 所使用的 `Kernel` 对应环境中。 - [Conda][] 安装(在笔记的 Cell 中执行) ```sh %conda install pandas ``` - [pip][] 安装(在笔记的 Cell 中执行) ```sh %pip install pandas ``` 此外,也可以通过终端,直接使用 [conda][] 或者 [pip][] 命令行安装。安装时注意**不要搞错环境**。 > **❗ 重要:** > > 对于类似于 [tensorflow-gpu](https://pypi.org/project/tensorflow-gpu/) 这样依赖复杂的大型第三方 `C/C++ libraries`(它依赖 [CUDA][], [cuDNN][])、构建复杂、且其 [wheel][] 包无法提供完整预编译库/可执行文件的软件包,建议使用 [Conda][] 进行安装。这样可以简便的解决库依赖和环境隔离问题,避免从源代码编译。 > **ℹ 提示:** > > 如果 [pip][] 以及 [conda][] 官方源镜像的网络条件不好,可以考虑使用国内的镜像。 > > - [pypi][] 镜像推荐: > - > - > - > - [Conda][] 镜像推荐: > - ---- [APT]: https://wiki.debian.org/Apt [NVIDIA]: https://nvidia.com/ [CUDA]: https://developer.nvidia.com/cuda-downloads [cuDNN]: https://developer.nvidia.com/cudnn [NCCL]: https://developer.nvidia.com/nccl [pypi]: https://pypi.python.org/ [PyTorch]: https://pytorch.org [Ubuntu]: https://www.ubuntu.com/index_kylin [venv]: https://docs.python.org/3/library/venv.html [TensorFlow]: https://www.tensorflow.org/ [wheel]: https://pypi.org/project/wheel/ [Python]: https://python.org/ [Conda]: https://conda.io/ [Pip]: https://packaging.python.org/tutorials/installing-packages/ [Jupyter]: https://jupyter.org/ [Jupyterhub]: https://jupyter.org/hub [Jupyterlab]: https://jupyterlab.readthedocs.io/ [Node.js]: https://nodejs.org/ [npm]: https://www.npmjs.com/ [systemd]: https://www.freedesktop.org/wiki/Software/systemd/ [nb_conda]: https://docs.anaconda.com/anaconda/user-guide/tasks/use-jupyter-notebook-extensions/