自定义用户存储#

在本指南中,我们将“存储”描述为“卷” - 磁盘上用户数据所在的目录。

Kubernetes 处理持久卷的创建和分配,在幕后它使用云提供商的 API 来发出正确的命令。因此,我们关于卷的大部分讨论将描述 Kubernetes 对象。

JupyterHub 使用 Kubernetes 来管理用户存储。有两个主要的 Kubernetes 对象参与将存储分配给 Pod

  • 一个 PersistentVolumeClaim (PVC) 指定了所需的存储类型。它的配置在您的 config.yaml 文件中指定。

  • 一个 PersistentVolume (PV) 是用户数据实际所在的卷。它由 Kubernetes 使用 PVC 中的详细信息创建。

作为 Kubernetes 对象,它们可以使用标准的 kubectl 命令查询(例如,kubectl --namespace=<your-namespace> get pvc

在 JupyterHub 中,每个用户都拥有自己的 PersistentVolumeClaim 对象,代表与其帐户关联的数据。当新用户启动其 JupyterHub 服务器时,将为该用户创建一个 PersistentVolumeClaim。此声明告诉 Kubernetes 需要什么样的存储(例如,ssd 与 hd)以及需要多少存储。Kubernetes 检查是否为该用户存在 PersistentVolume 对象(由于这是一个新用户,因此不存在)。如果不存在 PV 对象,则 Kubernetes 将使用 PVC 为用户创建一个新的 PV 对象。

现在用户有了 PV,Kubernetes 接下来必须将该 PV 附加(或“挂载”)到用户的 Pod(运行用户代码)。完成此操作后,用户将可以在 JupyterHub 中访问其 PV。请注意,所有这些都在用户登录时在幕后自动完成。

PersistentVolumeClaimPersistentVolume 不会被删除,除非 PersistentVolumeClaim 被 JupyterHub 管理员显式删除。当用户关闭其服务器时,其用户 Pod 会被删除,其卷会从 Pod 中分离,PVCPV 对象仍然存在。将来,当用户再次登录时,JupyterHub 会检测到用户拥有预先存在的 PVC,并将简单地将其附加到其新的 Pod,而不是创建新的 PVC

此过程如何出现故障?#

当 Kubernetes 使用 PVC 创建新的用户 PV 时,它会向 Kubernetes 所运行的云提供商的底层 API 发送命令。有时,对特定 PV 的请求可能会失败 - 例如,如果您的帐户已达到可用磁盘空间的限制。

另一个常见问题是集群中节点上可同时挂载的卷数限制。请查看您的云提供商以了解您请求的存储资源限制的详细信息。

注意

一些云提供商对每个节点可以挂载的磁盘数量有限制。由于 JupyterHub 为每个用户分配一个磁盘用于持久存储,因此这限制了在任何时间点可以运行在节点上的用户数量。如果您需要用户拥有持久存储,并且您最终遇到了此限制,则必须使用更多节点来容纳每个用户的磁盘。在这种情况下,我们建议为每个节点分配更少的资源(例如 RAM),因为您将在单个节点上容纳更少的用户。

配置#

大多数存储配置是在集群级别完成的,并且不是 JupyterHub 独有的。但是,有些部分是,我们将在此演示如何配置它们。

请注意,除非销毁旧的 PVC,否则不会为现有用户创建新的 PVC。如果您通过 config.yaml 更新用户的 PVC 配置,那么任何用户将为他们创建新的 PVC,但用户不会。要强制升级旧用户的存储类型,您需要手动删除他们的 PVC(例如 kubectl --namespace=<your-namespace> delete pvc <pvc-name>)。这将删除用户的所有数据,因此我们建议您先备份他们的文件系统,如果您想保留他们的数据。

删除用户的 PVC 后,他们下次登录时,将根据您更新的 PVC 规范为他们创建一个新的 PVC

配置的存储类型#

一个 StorageClass 对象用于确定为您的用户配置什么类型的 PersistentVolume。大多数流行的云提供商都有一个标记为默认的 StorageClass。您可以通过以下方式找到您的默认 StorageClass

kubectl get storageclass

并查找其名称旁边有 (default) 的对象。

要更改为您的用户配置的 PersistentVolume 类型,

  1. 按照 Kubernetes 文档 创建一个新的 StorageClass 对象

  2. config.yaml 中指定您刚刚创建的 StorageClass 的名称

    singleuser:
      storage:
        dynamic:
          storageClass: <storageclass-name>
    
  3. 执行 helm upgrade

请注意,这只会影响登录的新用户。我们建议您在用户开始大量使用您的集群之前执行此操作。

我们将在下面提供流行云提供商的示例,但通常会参考 Kubernetes 文档。

Google Cloud#

在 Google Cloud 上,默认的 StorageClass 将配置标准的 Google 持久磁盘。这些磁盘运行在硬盘上。为了获得更高的性能,您可能需要使用 SSD。要使用 SSD,您可以通过将以下 yaml 放入一个新文件中来创建一个新的 StorageClass。我们建议使用描述性的名称,例如 storageclass.yaml,我们将在下面使用它

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: jupyterhub-user-ssd
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd
allowedTopologies:
  - matchLabelExpressions:
      - key: failure-domain.beta.kubernetes.io/zone
        values:
          - <your-cluster-zone>

<your-cluster-zone> 替换为您创建集群的区域(您可以使用 gcloud container clusters list 找到它)。

接下来,通过在命令行中运行 kubectl apply -f storageclass.yaml 来创建此对象。有关各个字段含义的更多信息,请参阅 Kubernetes 文档。最重要的字段是 parameters.type,它指定您要使用的存储类型。有两个选项:

  • pd-ssd 使 StorageClass 配置 SSD。

  • pd-standard 将配置非 SSD 磁盘。

创建此 StorageClass 后,您可以在您的 config.yaml 中使用以下内容配置 JupyterHub 的 PVC 模板

singleuser:
  storage:
    dynamic:
      storageClass: jupyterhub-user-ssd

请注意,对于 storageClass:,我们使用上面在 metadata.name 中指定的名称。

配置的存储大小#

您可以在您的 config.yaml 中的 PVC 中设置 JupyterHub 请求的存储大小。

singleuser:
  storage:
    capacity: 2Gi

这将为每个用户请求一个 2Gi 的卷。默认情况下,每个用户请求一个 10Gi 的卷。

我们建议您使用 IEC 二进制前缀(Ki、Mi、Gi 等)来指定您需要的存储量。 2Gi(IEC 二进制前缀)是 (2 * 1024 * 1024 * 1024) 字节,而 2G(SI 十进制前缀)是 (2 * 1000 * 1000 * 1000) 字节。

关闭每个用户的持久存储#

如果您不希望用户拥有任何持久存储,可以将其关闭。编辑 config.yaml 文件并将存储类型设置为 none

singleuser:
  storage:
    type: none

接下来 应用更改

应用更改后,新用户将不再被分配持久 $HOME 目录。任何当前运行的用户仍然可以访问他们的存储,直到他们的服务器重新启动。您可能需要使用 kubectl 手动删除当前用户的 PVCs 以回收可能已分配的任何云磁盘。您可以使用以下命令获取当前的 PVC 列表:

kubectl --namespace=<your-namespace> get pvc

然后,您可以使用以下命令删除不需要的 PVCs

kubectl --namespace=<your-namespace> delete pvc <pvc-name>

请记住,删除某个人的 PVCs 将删除他们所有的数据,因此请谨慎操作!

附加存储卷#

如果您已经在 JupyterHub 之外创建了 PersistentVolumePersistentVolumeClaim,则可以将它们挂载到用户 Pod 中。例如,如果您有一个名为 jupyterhub-shared-volume 的共享 PersistentVolumeClaim,则可以将其挂载到所有用户 Pod 中的 /home/shared

singleuser:
  storage:
    extraVolumes:
      - name: jupyterhub-shared
        persistentVolumeClaim:
          claimName: jupyterhub-shared-volume
    extraVolumeMounts:
      - name: jupyterhub-shared
        mountPath: /home/shared

请注意,如果您想将卷挂载到多个 Pod 中,则该卷必须支持合适的 访问模式