VXenomac
5/14/2019 - 12:28 PM

构建多用户隔离的 Jupyter Lab 单服务器原生环境

构建多用户隔离的 Jupyter Lab 单服务器原生环境

构建多用户隔离的 Jupyter Lab 单服务器原生环境

概述

我们需要:

  • 在我们的 GPU 服务器上,建立一个用户隔离的 Jupyter 远程 Web 环境
  • 每个用户在一个独立的 Python 环境中使用 Jupyterlab
  • 每个用户都可以自由的使用 Conda 或者 Pip 安装软件

操作系统与GPU驱动加速程序

我们目前拥有 NVIDIA 1080 TI GPU 的图形卡,为了方便的使用该 GPU 硬件,和相关驱动、加速程序,以及依赖这些程序的库、框架、应用,我们做出了如下选择:

  • Ubuntu 是一个成熟且流行的选择
  • 截至成稿时,PyTorch 的预编译包所支持的 CUDA 版本是 9.0TensorFlow 的预编译包同样也只支持 CUDA 9.0。考虑到9.0版本的 CUDA 的预编译包仅支持 Ubuntu 16041704,最终选择的是支持时间更长的 Ubuntu 1604 x86_64bit , 且安装其默认图形界面(桌面版)。

主要硬件/软件环境:

  • Hardwares:
    • CPU: Intel Xeon E5 2670
    • GPU: NVIDIA GeForce GTX 1080 Ti
  • Softwares:
    • OS: Ubuntu 1604 x86_64
      • GPU Support:
        • NVDIA Dirver 410
        • CUDA 9.0
        • cuDNN 7.3 for CUDA 9.0
        • NCCL 2.3.7 for CUDA 9.0

装配好硬件和操作系统之后,按照以下的记录安装/配置步骤,进行操作。期间须保证该计算机可以畅通地连接到互联网:

更新操作系统

  1. 在终端执行:

    sudo apt update
    sudo apt upgrade --auto-remove
    
  2. 重启

安装 NVIDIA 驱动

NVidia 官方驱动的安装比较繁琐,涉及到 Linuxmodprobeinitramfs 的修改,本文不予记载。

建议采用 Graphics Drivers 软件仓库提供的驱动:

  1. 在终端执行:

    sudo add-apt-repository ppa:graphics-drivers/ppa
    sudo apt-get update
    
  2. 通过图形界面的"系统设置"指定系统要使用的驱动:

    1. 打开“系统设置” -> “软件和更新” -> “附加驱动”
    2. 待“搜索可用驱动”完毕后,选择 NVIDIA Corporation 下最新的长期支持版驱动版本(访问 https://www.nvidia.com/object/unix.html 查看 Unix 驱动的版本信息)
    3. 等待驱动下载和安装,完成后重启计算机。

安装 CUDA 9.0

访问 https://developer.nvidia.com/cuda-90-download-archive ,根据具体环境选择安装方式,并按照说明进行操作。

建议的选择:

Operating SystemArchitectureDistributionVersionInstaller Type
Linuxx86_64Ubuntu16.04deb (network)

按照建议选择下载安装文件后的安装步骤:

sudo dpkg -i cuda-repo-ubuntu1704_9.0.176-1_amd64.deb
sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1704/x86_64/7fa2af80.pub
sudo apt update
sudo apt install -y cuda-9-0

⚠ 警告:

  • 一定 要安装 cuda-9-0 而不是 CUDA 9.0 官方下载页面所言,否则被安装的可能是不兼容的高版本的CUDA
  • 安装完毕后,不要 升级软件包cuda-repo-ubuntu1604,否则 CUDA 可能被升级到不兼容的高版本。

要固定 CUDA 版本,避免意料之外的升级,我们可以这样操作:

sudo apt-mark hold cuda-repo-ubuntu1604

安装 cuDNN

访问 cuDNN 下载页面 ,根据具体环境选择安装方式,并按照说明进行安装。

我们的环境需要的版本是:

要下载和安装的软件包有:

安装 NCCL

访问 NCCL 下载页面 ,根据具体环境选择安装方式,并按照说明进行安装。

我们的环境需要的版本是:

建议下载和安装该软件包的离线版:

安装和配置 Jupyterhub

安装 Python

JupyterPython 程序,需要的版本是 3.4 及以上。

我们有三种选择可用于安装 Python 3

  1. 通过 apt 安装 使用 Ubuntu 1604 默认软件仓库提供的 Python 3.5
  2. 从源码安装 Python 3.6 。目前不推荐使用 3.7,因为 TensorFlow 目前没有此版本的官方支持。
  3. 使用 Conda

其中,1, 2 两种方式是将 Python 安装到系统全局,而 3 是用户级的安装。

具体的安装方法是:

  • apt 安装:

    安装操作系统默认软件仓库提供的 Python 3

    sudo apt install -y python3 python3-setuptools python3-pip python3-dev build-essential
    

    Ubuntu 1604 默认仓库提供的 Python 3 版本是 3.5

  • 源码安装:

    Python 3.6 为例:

    1. 在操作系统上安装编译构建工具和相关依赖软件的开发包:

      sudo apt install -y build-essential libssl-dev zlib1g-dev libbz2-dev liblzma-dev libsqlite3-dev libdb-dev libgdbm-dev libncurses5-dev libreadline-dev libexpat1-dev tk-dev
      
    2. https://python.org/ 下载最新的 Python3.6 源代码压缩包(此处以3.6.6为例),解压、构建、安装:

      wget https://www.python.org/ftp/python/3.6.6/Python-3.6.6.tgz
      tar -xf Python-3.6.6
      cd Python-3.6.6
      ./configure --enable-optimizations
      make
      sudo make altinstall
      

      ⚠ 警告:

      一定 要使用 make altinstall 命令安装,而 不是 make install ,否则可能破环操作系统。

    3. 检查 Python 3.6 是否安装成功:

      $ which python3.6
      /usr/local/bin/python3.6
      
      $ python3.6 --version
      Python 3.6.6
      
    4. 确保安装了 pip 模块:

      sudo -H python3.6 -m ensurepip
      
  • Conda

    这是推荐的方式。与以上两种方式不同的是,CondaPython 安装到用户环境中,而不是整个操作系统的全局环境。

    所以,应选定一个已有的操作系统账户、或者新建一个专门的账户,专门用于安装这些软件包。假定我们使用名为 jupyterhub 的账户,如果它还不存在,可以这样新建:

    sudo adduser jupyterhub
    

    设置好这个用户的密码后,切换到该账户或者以该账户登录:

    $ su jupyterhub
    Password:
    

    安装 Conda (以 Anaconda3 5.2.0 为例,这是目前最新的 Python 3.6 版本):

    $ wget https://repo.anaconda.com/archive/Anaconda3-5.2.0-Linux-x86_64.sh
    ./Anaconda3-5.2.0-Linux-x86_64.sh
    

    按照提示,使用默认值进行安装即可。

    当安装程序询问

    >>> Do you wish the intaller to pretend the Anaconda3 install location to PATH in your /home/jupyterhub/.bashrc ? [yes|no]
    

    的时候,输入 yes

    安装完毕后,执行:

    source ~/.bashrc
    

    此时,该用户(jupyterhub)的 Python 执行文件路径应是:

    $ which python
    /home/jupyterhub/anaconda3/bin/python
    

ℹ 说明

下文中,我们将用 $PYTHON 代表以上步骤中安装的 Python 的可执行文件。小心多个 python 并存的情况。具体以实际情况为准。

就上面几种不同的安装方式而言:

  • apt 安装: $PYTHON 绝对路径是: /usr/bin/python3
  • 源码安装: $PYTHON 绝对路径是: /usr/lobal/bin/python3.6
  • Conda: $PYTHON 绝对路径是: /home/jupyter/anaconda3/bin/python

Conda 是推荐的方式

安装 Jupyter 系列程序

  • 如果 Python 是全局安装的:

    使用 Pip 安装这些软件到系统全局环境:

    sudo -H $PYTHON -m pip install jupyter jupyterlab jupyterhub
    

    ⚠ 警告:

    强烈建议使用sudo以超级用户执行安装,不要 pip install --user,否则以超级用户的身份运行 Jupyterhub 的时候,会找不到包 (当然可以修改 PYTHONPATH ,以及各种 Hack 方法解决这个问题,但不推荐)。

  • 如果 Python 是通过 Conda 安装的:

    Conda 已经带有 JupyterJupyterlab,故直接安装 Jupyterhub 到该用户的 Base 环境

    conda install -c conda-forge jupyterhub
    

安装之后,我们还需要安装程序 configurable-http-proxy操作系统全局环境。之所以需要系统级全局安装,是因为我们将要以 root 身份,以后台服务的形式运行 Jupyterhub ,且 Jupyterhub 在自动启动 configurable-http-proxy 时不具备指定路径的能力。

configurable-http-proxy 是一个 Node.js 程序,它需要的 Node.js 版本大于 Ubuntu 1604 默认仓库提供的。所以我们首先需要这样安装 Node.js 的较高版本。

  • 如果采用全局 Python:

    应使用 apt 安装 Node.js 最新稳定版(目前是 v10.x):

    curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
    sudo apt update
    sudo apt install -y nodejs
    

    验证 Node.js 的路径和版本:

    $which node
    /usr/bin/node
    
    $node --version
    v10.15.0
    

    然后通过 npm 全局安装 configurable-http-proxy:

    sudo npm install -g configurable-http-proxy
    
  • 如果使用 Conda

    上个步骤中,Conda 在安装 Jupyterhub 的时候,已经自动安装了它所需要的 Node.js 以及 configurable-http-proxyBase 环境。 这与我们以 root 在后台运行的目标相悖;且 Jupyterhubroot 执行时,无法找到 jupyterhub 用户目录中的 configurable-http-proxy 执行文件,根本无法启动。

    要解决这个问题,我们可以为 jupyterhub 用户目录中的 configurable-http-proxy 在操作系统的可执行文件目录建立一个软链接:

    sudo ln -s /home/jupyterhub/anaconda3/bin/configurable-http-proxy /usr/local/bin/configurable-http-proxy
    

jupyterlab-hub 插件

这个插件提供了额外的 JupyterlabJupyterhub 集成功能。这是一个可选功能。

  • 如果使用的是全局安装的 Python,执行安装命令:

    sudo -H $PYTHON -m jupyter labextension install @jupyterlab/hub-extension
    
  • 如果使用的是 Conda ,注意首先切换到安装 Conda 的用户(之前步骤中新建的用户jupyterhub):

    $ su jupyterhub
    Password:
    

    执行安装命令:

    $PYTHON -m jupyter labextension install @jupyterlab/hub-extension
    

配置 Jupyterhub

  1. 生成默认配置文件

    将配置文件放到系统目录 /etc 中,之后我们将在启动命令行中指定这个路径。

    $ jupyterhub --generate-config
    Writing default config to: jupyterhub_config.py
    $ sudo mkdir -p /etc/jupyterhub
    $ sudo mv -a jupyterhub_config.py /etc/jupyterhub
    
  2. 修改配置文件

    1. 修改 c.LocalProcessSpawner.shell_cmd ,默认以 Bash “登录”方式启动 sing-user 子进程:

      c.LocalProcessSpawner.shell_cmd = ['bash', '-l', '-c']
      
    2. 修改 c.Spawner.cmd ,以支持 jupyterlab-hub 插件。

      c.Spawner.cmd = ['jupyter-labhub']
      

      ℹ 说明:

      该步骤是可选的。如果不使用 jupyterlab-hub 插件,就不要修改这个配置。

启动命令行

现在,我们尝试运行 Jupyterhub 。我们将以超级用户的身份执行,按照上个步骤的配置文件,在全部IP地址监听:

sudo $PYTHON -m jupyterhub -f /etc/jupyterhub/jupyterhub_config.py --ip "0.0.0.0"

其中:

  • -f /etc/jupyterhub/jupyterhub_config.py: 指定配置文件
  • --ip "0.0.0.0": 表示在所有的 IPv4 地址上监听
  • 默认端口是 8000

ℹ 说明:

如果 Python 执行文件来自 Conda 的非系统全局安装,例子中的 $PYTHON 应提供完整的绝对路径,如 /home/jupyterhub/anaconda3/bin/python

设置后台服务

可以用 Systemd, Initd 等将这个程序设置成后台服务。此处,我们选用 Supervisor

首先安装 Supervisor

sudo apt install -y supervisor

然后为 Jupyterhub 新建服务配置文件 /etc/supervisor/conf.d/jupyterhub.conf:

echo_supervisord_conf | sudo tee /etc/supervisor/conf.d/jupyterhub.conf

修改配置文件 —— 针对上文提到的不同的 Python 安装方式,配置文件的 program 段和command 属性值分别是:

  • 通过 apt 安装的 Python:

    [program:jupyterhub]
    command=python3 -m jupyterhub -f /etc/jupyterhub/jupyterhub_config.py --ip 0.0.0.0
    
  • 从源代码安装的 Python 3.6:

    [program:jupyterhub]
    command=python3.6 -m jupyterhub -f /etc/jupyterhub/jupyterhub_config.py --ip 0.0.0.0
    
  • Conda:

    [program:jupyterhub]
    command=/home/jupyterhub/anaconda3/bin/python -m jupyterhub -f /etc/jupyterhub/jupyterhub_config.py --ip 0.0.0.0
    

ℹ 说明:

  • 可以直接使用 jupyterhub 命令,不一定要通过 python -m jupyter 启动。注意执行文件的路径和所属用户。
  • 各参数可以按照实际需求进行修改

最后重加载 supervisor 配置,并使之生效:

sudo supervisorctl reread
sudo supervisorctl update jupyterhub

要查看服务的运行情况,可以执行:

sudo supervisorctl status

用户管理

现在,Jupyterhub 已经作为服务程序,以超级用户的身份在运行了。

但我们还是无法正常访问——因为我们还没有给 Jupyterhub 添加用户。

我们采用 Jupyterhub 默认的 single-user 模式,其登录用户就是操作系统账户。

新建用户

新建操作系统账户

要新建 Jupyterhub 用户,我们首先需要新建一个操作系统账户,假定其用户名为 newuser:

sudo adduser newuser

设置好用户密码等信息后,切换到这个用户:

$ su newuser
Password:

安装 Conda

我们的目标是为每个用户提供独立的机器学习环境,故不宜使用操作系统全局 Python

为了避免互相干扰,我们的选择有:

  1. 仍使用全局 PythonJupyterhub 服务通过系统级的 jupyter-lab 或者 jupyter-labhub 命令启动用户环境。启动后,让每个用户使用 pip 以用户模式(pip install --user <pacakge>)安装软件包。
  2. 为每个用户安装独立的 PythonJupyterhub 服务通过用户级的 Conda Base 环境下的 jupyter-lab 或者 jupyter-labhub 命令启动用户环境。启动后,使用 Conda 直接在 Base 环境下安装软件包,或者使用 Conda Base 环境下的 pip 安装软件包。

考虑到 Conda 可以支持 pip,而反之不可,我们选用第二种方法。

Anaconda3 5.2.0 为例:

wget https://repo.anaconda.com/archive/Anaconda3-5.2.0-Linux-x86_64.sh
./Anaconda3-5.2.0-Linux-x86_64.sh

按照提示,使用默认值进行安装。

当安装程序询问

>>> Do you wish the intaller to pretend the Anaconda3 install location to PATH in your /home/newuser/.bashrc ? [yes|no]

的时候,输入 yes

安装完毕后,执行:

source ~/.bashrc

此时,该用户(newuser)的 Python 执行文件路径应是:

$ which python
/home/newuser/anaconda3/bin/python

下面,十分重要的一步:我们还需要修改这个用户 ~/.profile,将 CondaBase环境可执行目录附加到PATH环境变量,以便 Jupyterhub 以该账户启动子进程时可以找到 jupyter-labjupyter-labhub 可执行文件:

echo "PATH=\"/home/newuser/anaconda3/bin:$PATH\"" >> ~/.profile

安装 Jupyterhub

Conda 已经带有 JupyterJupyterlab,直接在这个用户的Conda Base 环境安装 Jupyterhub 即可:

conda install -c conda-forge jupyterhub

安装 jupyterlab-hub

jupyter labextension install @jupyterlab/hub-extension

Jupyterhub 账户配置

如果要使用登录账户白名单,在 jupyterhub_config.py 配置文件中,将用户名加入c.Authenticator.whitelist集合:

c.Authenticator.whitelist = {'newuser'}

如果要将该用户设置为管理员,在 jupyterhub_config.py 配置文件中,将用户名加入c.Authenticator.admin_users集合:

c.Authenticator.admin_users = {'newuser'}

ℹ 说明:

  • 注意将newuser换成实际的值。
  • 如果安装了 jupyterlab-hub 插件,登录后在图形界面的 hub 菜单中有额外选项。

现在,通过浏览器访问 Jupyterhub 的 Web 界面,如 http://localhost:8000/,使用操作系统账户 newuser 的账户密码登录,就可以直接使用 Jupyterlab 的全部功能了

删除用户

直接删除操作系统账户即可

多内核环境管理

此处的“内核”指的是Jupyterkernel

到目前为止,我们已经让一个新用户在全局 Python 以及 CondaBase 环境中运行了 Jupyterlab。此时,如果登录系统,所有的笔记将默认以这个Python环境的内核运行。如果需要多个不同Python版本/环境,或者其它语言的运行时,我们就得为这个用户安装多个Jupyter内核。

新建 Jupyter Kernel

要让该用户的 Jupyterlab 使用另一个新的 Conda 环境,我们需要:

  1. 新建一个 Conda 环境。例如,新建一个名为 mypy36env , Python 版本为 3.5的环境:

    conda create --name mypy35env python=3.5
    
  2. 在新的环境中安装 ipykernel

    conda activate mypy35env
    conda install pip ipykernel
    python -m ipykernel install --user --name mypy35env --display-name "Python 3.5(conda:mypy35env)"
    

现在,刷新 Jupyterlab 页面,可以看到新的 kernel

删除 Jupyter Kernel

要删除这个 Jupyter Kernel,执行:

jupyter kernelspec uninstall mypy35env

如果要连同 Conda 运行环境一同删除,执行:

conda deactivate
conda env remove -n mypy35env

安装 Python 软件包

用户通过 Jupyterlab 的笔记或者终端,使用 Condapip 安装。注意不要搞错 Condavenv 环境。

安装 Jupyterlab 插件

由于各个用户的 Jupyterlab 环境是隔离的,所以可以自行安装,不会相互干扰。

升级 Jupyter

用户和系统的 Jupyter 可以分别升级, Condapip 都可以。当然,前提是系统的 Jupyterhub 与用户的 jupyterlab 能够兼容。

安装其它软件

理论上,用户可以通过 Jupyterlab 终端,做权限允许的任何事情,安装任何软件,包括 Jupyter Kernel。

但是考虑到我们一般不为 Jupyterhub 用户配置系统级的安装权限,用户应优先考虑使用 Conda ,在本用户范围之内安装所需软件。

如果通过其它途径的安装软件,也应尽量考虑让该方案在用户权限范围之内可行。