自定义用户环境#

此页面包含用于增强用户体验的常见方法的说明。有关所有可配置 Helm chart 选项的列表,请参阅配置参考

用户环境是用户登录 JupyterHub 时存在的一组软件包、环境变量和各种文件。用户还可能看到提供执行专门任务界面的不同工具,例如 JupyterLab、RStudio、RISE 等。

Dockerfile 构建的 Docker 镜像将为您提供给用户的环境奠定基础。例如,该镜像将决定哪些 Linux 软件 (curl, vim ...)、编程语言 (Julia, Python, R, ...) 和开发环境 (JupyterLab, RStudio, ...) 可供使用。

要开始自定义用户环境,请参阅以下主题。

选择并使用现有的 Docker 镜像#

此 chart 使用一个最小化的默认单用户镜像,用于快速测试。您将需要为实际使用选择不同的镜像或构建自己的镜像。

Jupyter 项目维护着 jupyter/docker-stacks 仓库,其中包含可直接使用的 Docker 镜像。每个镜像都包含一组常用的科学和数据科学库及工具。他们还提供了关于如何选择合适镜像的优秀文档。

例如,要使用包含数据科学有用工具和库的 datascience-notebook 镜像,请完成以下步骤:

  1. 修改您的 config.yaml 文件以指定镜像。例如:

    singleuser:
      image:
        # You should replace the "latest" tag with a fixed version from:
        # https://hub.docker.com/r/jupyter/datascience-notebook/tags/
        # Inspect the Dockerfile at:
        # https://github.com/jupyter/docker-stacks/tree/HEAD/datascience-notebook/Dockerfile
        name: jupyter/datascience-notebook
        tag: latest
      # `cmd: null` allows the custom CMD of the Jupyter docker-stacks to be used
      # which performs further customization on startup.
      cmd: null
    

    注意

    容器镜像名称不能超过 63 个字符。

    始终使用明确的 tag,例如特定的 commit。避免使用 latest,因为当镜像的新版本发布时,可能会给用户带来几分钟的延迟、困惑或故障。

  2. 按照应用更改中列出的说明应用更改。

    如果您配置了 prePuller.hook.enabled,集群中的所有节点将在 Hub 升级之前拉取镜像,以便用户使用该镜像。镜像拉取可能需要几分钟才能完成,具体取决于镜像的大小。

  3. 如果您已登录,请从 JupyterHub 控制面板重新启动您的服务器。

注意

如果您希望用户从多个 Docker 镜像中选择环境,请参阅使用多个配置文件让用户选择其环境

选择用户界面#

JupyterLab 是 Jupyter 的新用户界面,旨在取代经典的 notebook 用户界面 (UI)。如果两者都已安装,用户已经可以在 URL 中互换 /tree/lab 来在经典 UI 和 JupyterLab 之间切换。使用 JupyterHub 1.x 及更早版本的部署默认使用经典 UI,而 JupyterHub 2.0 则将 JupyterLab 作为默认设置。

要选择默认启动的用户界面,需要设置两个自定义项:

  1. 首选的默认用户界面 (UI)

  2. 要启动的服务器程序

主要有两种 Jupyter 服务器实现(大多数部署不会看到差异,但对于某些服务器扩展可能会出现问题。如果不确定,新应用程序应选择 jupyter_server):

  1. 现代的 jupyter server,当您使用 jupyter lab 或其他近期 Jupyter 应用程序时启动,以及

  2. “经典”的旧版 notebook 服务器 (jupyter notebook)

通常,默认 UI 在 config.yaml 中通过以下方式选择:

singleuser:
  defaultUrl: ...

而默认服务器通过以下方式选择:

singleuser:
  extraEnv:
    JUPYTERHUB_SINGLEUSER_APP: "..."

具体来说,使用以下选项之一来选择现代服务器:

# this is the default with JupyterHub 2.0
singleuser:
  extraEnv:
    JUPYTERHUB_SINGLEUSER_APP: "jupyter_server.serverapp.ServerApp"

或经典 notebook 服务器:

# the default with JupyterHub 1.x
singleuser:
  extraEnv:
    JUPYTERHUB_SINGLEUSER_APP: "notebook.notebookapp.NotebookApp"

只有当配置与默认设置不同时,才需要上述配置。JupyterHub 2.0 将默认服务器从 NotebookApp 更改为 ServerApp,因此我们在此处的每个示例中都明确指定了选择,以便相同的配置在 JupyterHub 1.x 和 2.x 中产生相同的结果。这样,您的选择将在升级过程中得以保留。

默认使用 JupyterLab#

注意

这是 JupyterHub 2.0 和 Helm chart 2.0 中的默认设置。

您可以通过在您的 config.yaml 中进行以下配置来选择 JupyterLab 作为默认 UI:

singleuser:
  defaultUrl: "/lab"
  extraEnv:
    JUPYTERHUB_SINGLEUSER_APP: "jupyter_server.serverapp.ServerApp"

您也可以将 JupyterLab 设为默认 UI,而升级到较新的服务器实现。这可能有助于那些需要坚持使用旧版 UI 且其扩展可能无法在新服务器上工作的用户。

singleuser:
  defaultUrl: "/lab"
  extraEnv:
    JUPYTERHUB_SINGLEUSER_APP: "notebook.notebookapp.NotebookApp"

注意

您需要安装 jupyterlab 包(可通过 pipconda 安装)才能使其工作。jupyter/docker-stacks 仓库中的所有镜像都预装了它。

默认使用经典 notebook#

注意

这是 JupyterHub 1.x 和 helm chart 1.x 中的默认设置。

如果您还没准备好升级到 JupyterLab,特别是对于那些依赖于在 JupyterLab 中没有等效品的自定义 notebook 扩展的用户,您可以始终坚持使用旧版的 notebook 服务器 (jupyter notebook):

# the default with JupyterHub 1.x
singleuser:
  extraEnv:
    JUPYTERHUB_SINGLEUSER_APP: "notebook.notebookapp.NotebookApp"

这将启动与以前完全相同的服务器和 UI。

如果您安装了 nbclassic 包,您也可以默认使用经典 UI,并在新服务器上运行:这可能是支持用户同时使用经典和新环境的最佳方式。

singleuser:
  defaultUrl: /tree/
  extraEnv:
    JUPYTERHUB_SINGLEUSER_APP: "jupyter_server.serverapp.ServerApp"

替代界面#

还有更多提供备选 UI 选择的 Jupyter 服务器扩展,可以与 JupyterHub 一起使用。

例如,retrolab 是一个不同的 notebook 界面,它构建在 JupyterLab 之上,但对于来自经典 Jupyter UI 的用户来说可能更舒适。

要安装这样的扩展:

  1. 在您的用户容器镜像中安装该软件包(pip install retrolabconda install retrolab)。

  2. 配置默认 URL,并确保使用 ServerApp:

singleuser:
  defaultUrl: /retro/
  extraEnv:
    JUPYTERHUB_SINGLEUSER_APP: "jupyter_server.serverapp.ServerApp"

自定义现有 Docker 镜像#

如果您希望所有用户都拥有镜像中缺失的某些内容,我们建议您在 jupyter/docker-stacks 的现有 Docker 镜像之上构建一个新镜像。

下面是一个基于 minimal-notebook 镜像构建的 Dockerfile 示例。此文件可以构建成一个 Docker 镜像,推送到一个 镜像仓库,并最终在 config.yaml 中配置以供 Helm chart 使用。

FROM jupyter/minimal-notebook:latest
# Replace `latest` with an image tag from to ensure reproducible builds:
# https://hub.docker.com/r/jupyter/minimal-notebook/tags/
# Inspect the Dockerfile at:
# https://github.com/jupyter/docker-stacks/tree/HEAD/minimal-notebook/Dockerfile

# install additional package...
RUN pip install --no-cache-dir astropy

# set the default command of the image,
# if you want to launch more complex startup than the default `juptyerhub-singleuser`.
# To launch an image's custom CMD instead of the default `jupyterhub-singleuser`
# set `singleuser.cmd: null` in your config.yaml.

注意

如果您正在使用私有镜像仓库,您可能需要设置镜像凭据。有关此内容的更多详细信息,请参阅 :ref:helm-chart-configuration-reference

设置环境变量#

影响用户环境的一种方法是设置环境变量。虽然如果您自己构建 Docker 镜像,可以在镜像中进行设置,但通过 config.yaml 中提供的值来配置 Helm chart 通常更容易。

要进行此设置,请编辑您的 config.yaml应用更改。例如,此代码片段将环境变量 EDITOR 设置为值 vim

singleuser:
  extraEnv:
    EDITOR: "vim"

您可以在 config.yaml 文件中设置任意数量的静态环境变量。

用户可以通过各种方式在他们的代码中读取环境变量。例如,在 Python 中,以下代码读取一个环境变量的值:

import os
my_value = os.environ["MY_ENVIRONMENT_VARIABLE"]

关于用户存储及向其中添加文件#

理解用户存储的基本设置方式非常重要。默认情况下,每个用户将获得一个 10GB 的硬盘空间,该空间在其服务器重启之间保持持久。这个硬盘将被挂载到他们的主目录。实际上,这意味着用户写入主目录(/home/jovyan)的所有内容都将保留下来,而其他所有内容都将在服务器重启之间被重置。

服务器可以通过淘汰(culling)来关闭。默认情况下,JupyterHub 的淘汰服务配置为淘汰已闲置一小时的用户服务器。请注意,JupyterLab 会自动保存文件,只要文件位于用户的主目录内,工作就不会丢失。

注意

在 Kubernetes 中,持久卷(PersistentVolume) (PV) 代表硬盘。KubeSpawner 将创建一个持久卷声明(PersistentVolumeClaim),向云端请求一个 PV。默认情况下,删除 PVC 将导致云端删除 PV。

Docker 镜像的 $HOME 目录将对用户隐藏。为了让用户看到这些内容,您必须预填充用户的文件系统。为此,您可以在 config.yaml 中包含一些命令,这些命令将在每次用户启动服务器时运行。可以在 config.yaml 中使用以下模式:

singleuser:
  lifecycleHooks:
    postStart:
      exec:
        command: ["cp", "-a", "src", "target"]

命令的每个元素都需要是列表中的一个单独项。请注意,此命令将从用户正在运行的容器的 $HOME 位置运行,这意味着相对于 ./ 放置文件的命令将导致用户在其主目录中看到这些文件。您可以使用像 wget 这样的命令将文件放置到您喜欢的位置。

填充 notebook 用户主目录的一个简单方法是将所需文件添加到容器的 /tmp 目录,然后使用 postStart 钩子将它们复制到 /home/jovyan。此示例展示了如何使用多个命令。

singleuser:
  lifecycleHooks:
    postStart:
      exec:
        command:
          - "sh"
          - "-c"
          - >
            cp -r /tmp/foo /home/jovyan;
            cp -r /tmp/bar /home/jovyan

请记住,命令将在用户每次启动服务器时运行。因此,我们建议使用 nbgitpuller 来将您的用户文件夹与 git 仓库同步。

使用 nbgitpuller 同步文件夹#

我们建议使用 nbgitpuller 工具,在用户每次启动服务器时,将用户文件系统中的一个文件夹与一个 git 仓库同步。这种同步也可以通过让用户访问类似 https://your-domain.com/hub/user-redirect/git-pull?repo=https://github.com/data-8/materials-fa18 的链接来触发(例如,作为备用启动 URL)。

要使用 nbgitpuller,首先请确保您已在您的 Docker 镜像中安装它。完成后,您就可以从 JupyterHub 内部访问 nbgitpuller CLI。您可以通过以下配置使用 postStart 钩子运行它:

singleuser:
  lifecycleHooks:
    postStart:
      exec:
        command:
          [
            "gitpuller",
            "https://github.com/data-8/materials-fa17",
            "master",
            "materials-fa",
          ]

这将在每次用户登录时,将仓库的 master 分支同步到名为 $HOME/materials-fa 的文件夹中。有关使用此工具的更多信息,请参阅 nbgitpuller 文档

警告

如果自上次同步以来,您的用户仓库发生了变化,nbgitpuller 将尝试自动解决合并冲突。在生产环境中使用该工具之前,您应该熟悉 nbgitpuller 的合并行为

允许用户为 notebook 创建自己的 conda 环境#

有时您希望用户能够创建自己的 conda 环境。默认情况下,在 JupyterHub会话中创建的任何环境都不会在会话之间持久存在。要解决此问题,请执行以下步骤:

  1. 确保在根环境中安装了 nb_conda_kernels 包(例如,参见使用 repo2docker 构建 Docker 镜像

  2. 配置 Anaconda 将用户环境安装到 $HOME 内的文件夹中。

    为所有用户在主文件夹中创建一个名为 .condarc 的文件,并确保其中包含以下几行:

    envs_dirs:
      - /home/jovyan/my-conda-envs/
    

上述文本将使 Anaconda 将新环境安装到此文件夹,该文件夹将在会话之间保持持久。

这些环境应该在 notebook 中使用,因此一个典型的用例是:

  1. 创建一个至少包含一个内核的环境,例如,对于 Python,命令是 conda create -n myenv ipykernel scipy

  2. 现在这个环境应该可以在内核列表中找到

使用多个配置文件让用户选择其环境#

您可以为多个用户环境创建配置,并让用户在登录到您的 JupyterHub 后从中进行选择。这是通过创建多个配置文件(profiles)来实现的,每个配置文件都附加了一组配置选项,这些选项会覆盖您的 JupyterHub 的默认配置(在您的 Helm Chart 中指定)。这可以用来让用户在多个 Docker 镜像之间进行选择,选择他们希望其作业运行的硬件,或者配置默认界面,如 Jupyter Lab 与 RStudio。

每个配置都是一组用于 KubeSpawner 的选项,它定义了 Kubernetes 应如何启动新的用户服务器 pod。传递给 profileList 配置的任何配置选项都将覆盖 KubeSpawner 中的默认值(或您在 helm chart 其他地方添加的任何配置)。

配置文件存储在 singleuser.profileList 下,并定义为一组具有特定配置选项的配置文件列表。下面是一个例子:

singleuser:
  profileList:
    - display_name: "Name to be displayed to users"
      description: "Longer description for users."
      # Configuration unique to this profile
      kubespawner_override:
        your_config: "Your value"
      # Defines the default profile - only use for one profile
      default: true

上述配置将在用户启动新服务器时显示一个屏幕,其中包含有关此配置文件的信息。

下面是一个包含四个配置文件的示例,它允许用户选择他们希望使用的环境。

singleuser:
  # Defines the default image
  image:
    name: jupyter/minimal-notebook
    tag: 2343e33dec46
  profileList:
    - display_name: "Minimal environment"
      description: "To avoid too much bells and whistles: Python."
      default: true
    - display_name: "Datascience environment"
      description: "If you want the additional bells and whistles: Python, R, and Julia."
      kubespawner_override:
        image: jupyter/datascience-notebook:2343e33dec46
    - display_name: "Spark environment"
      description: "The Jupyter Stacks spark image!"
      kubespawner_override:
        image: jupyter/all-spark-notebook:2343e33dec46
    - display_name: "Learning Data Science"
      description: "Datascience Environment with Sample Notebooks"
      kubespawner_override:
        image: jupyter/datascience-notebook:2343e33dec46
        lifecycle_hooks:
          postStart:
            exec:
              command:
                - "sh"
                - "-c"
                - >
                  gitpuller https://github.com/data-8/materials-fa17 master materials-fa;

这允许用户从四个配置文件中进行选择,每个配置文件都有自己的环境(由上述配置中的每个 Docker 镜像定义)。

上述示例中的“学习数据科学”环境覆盖了 postStart 生命周期钩子。请注意,当使用 kubespawner_override 时,这些值必须符合 KubeSpawner 配置的格式。例如,在 kubespawner_override 中覆盖生命周期钩子时,配置是针对 lifecycle_hooks(蛇形命名法),而不是 lifecycleHooks(驼峰命名法),后者是直接在 singleuser 配置部分下使用的。关于这一点的进一步解释可以在这个 github issue 中找到。

依赖于用户的配置文件选项#

根据用户身份配置向用户呈现的配置文件选项也是可能的。您可以通过定义一个自定义的 pre-spawn 钩子来做到这一点,该钩子根据用户身份填充配置文件列表。有关其工作原理的一些示例,请参阅这篇 discourse 帖子

注意

您还可以通过使用 Kubespawner 的 profile_form_template 配置来控制用于配置文件选择页面的 HTML。更多信息请参阅 Kubespawner 配置参考

设置要启动的命令#

最终,单用户服务器应该启动 jupyterhub-singleuser 命令。但是,镜像可能会有一个自定义的 CMD 来执行此操作,其中可能包含一些准备步骤、添加额外的命令行参数或启动一个自定义的包装命令等。

注意

如果您的镜像在启动时有环境准备步骤,最好在镜像的 ENTRYPOINT 中完成,而不是在 CMD 中,这样覆盖命令时就不会跳过您的准备步骤。

默认情况下,zero-to-jupyterhub 将启动 jupyterhub-singleuser 命令。如果您有一个镜像(例如 jupyter/scipy-notebook 和其他 Jupyter Docker stacks)定义了一个带有启动自定义并最终启动 jupyterhub-singleuser 的 CMD,您可以选择启动镜像的默认 CMD,方法是设置:

singleuser:
  cmd: null

或者,您可以将明确的自定义命令指定为字符串或字符串列表:

singleuser:
  cmd:
    - /usr/local/bin/custom-command
    - "--flag"
    - "--other-flag"

注意

Docker 有 ENTRYPOINTCMD,k8s 称之为 commandargs。zero-to-jupyterhub 始终尊重镜像的 ENTRYPOINT,设置 singleuser.cmd 只会覆盖 CMD。

禁用特定的 JupyterLab 扩展#

有时您希望在 JupyterHub 上默认临时禁用某个 JupyterLab 扩展,而无需重新构建您的 docker 镜像。这可以通过 singleuser.extraFiles 和 JupyterLab 的 page_config.json 轻松实现。

JupyterLab 的 page_config.json 允许您通过在运行 jupyter --paths 时列出的任何目录内的 labconfig 目录下放置 JSON 文件来设置页面配置。我们只需使用 singleuser.extraFiles 来提供这个文件!

singleuser:
  extraFiles:
    lab-config:
      mountPath: /etc/jupyter/labconfig/page_config.json
      data:
        disabledExtensions:
          jupyterlab-link-share: true

这将禁用 link-share labextension,无论是在 JupyterLab 还是 RetroLab 中。您可以使用 jupyter labextension list 找到扩展的名称及其当前状态。

jovyan@jupyter-yuvipanda:~$ jupyter labextension list
JupyterLab v3.2.4
/opt/conda/share/jupyter/labextensions
        jupyterlab-plotly v5.4.0 enabled OK
        jupyter-matplotlib v0.9.0 enabled OK
        jupyterlab-link-share v0.2.4 disabled OK (python, jupyterlab-link-share)
        @jupyter-widgets/jupyterlab-manager v3.0.1 enabled OK (python, jupyterlab_widgets)
        @jupyter-server/resource-usage v0.6.0 enabled OK (python, jupyter-resource-usage)
        @retrolab/lab-extension v0.3.13 enabled OK

如果同一个镜像在多个 hub 之间共享,而您希望某些 hub 禁用某些扩展,这将非常有用。