跳转到主要内容

适用于多个Kubernetes集群的JupyterHub Spawner

项目描述

jupyterhub-multicluster-kubespawner

从同一JupyterHub启动用户pod到多个不同的Kubernetes集群!

为什么?

单个JupyterHub作为进入各种集群的“入口”非常有用。用户可以根据各种因素动态决定启动他们的笔记本(和dask集群)——更接近他们的数据、不同的云提供商、由不同的计费账户支付等。这也使得JupyterHub运营商的生活更加容易。

您可以在这里查看spawner的早期演示。

安装

jupyterhub-multicluster-kubespawner可在PyPI获取

pip install jupyterhub-multicluster-kubespawner

您还需要安装kubectl以及任何用于 认证 目标集群的工具。

云提供商 工具
Google Cloud gcloud
AWS aws
Azure az
DigitalOcean doctl

配置

您可以在jupyterhub_config.py文件中使用以下配置片段让JupyterHub使用MultiClusterKubeSpawner,尽管还需要更多配置来连接到不同的集群。

配置哲学

MultiClusterKubeSpawner试图尽可能接近Kubernetes原生,与备受推崇的kubespawner不同。它不试图在Kubernetes提供的内容上提供一个抽象层,因为我们发现这通常是一个非常薄弱的抽象。这使得JupyterHub运营商难以利用Kubernetes提供的所有强大功能,并增加了维护者的维护负担。

MultiClusterKubeSpawner 在底层使用了流行的 kubectl,这使得对于了解 Kubernetes 集群基本操作的人来说,配置变得熟悉。但另一方面,要成功配置这个启动器,需要具备一些 Kubernetes 的知识,但这种权衡似乎对每个人来说都是有益的。

设置 KUBECONFIG

由于多集群-kubespawner 需要与多个 Kubernetes 集群通信,它使用一个 kubeconfig 文件来连接 Kubernetes 集群。它会在 ~/.kube/config 中查找该文件——在生产环境中,您的文件可能存在其他位置——您可以通过设置 KUBECONFIG 环境变量来指定该文件的位置。

每个集群都由一个 上下文 表示,这是一个指向集群 Kubernetes API 端点的指针以及用于认证的凭据的组合。更多详情 这里

构建一个适用于您想使用的所有集群的 kubeconfig 文件的简单方法是首先在您的笔记本电脑上 仔细 构建,然后将该文件复制到您的部署中。

首先,将您的本地 KUBECONFIG 环境变量设置为可以随后复制的文件。

export KUBECONFIG=jupyterhub-mcks-kubeconfig

在 Google Cloud 上

  1. 创建一个 Google Cloud 服务帐户,并授予它足够的权限来访问您的 Kubernetes 集群。roles/container.developer 应该是 足够的权限。

  2. 为您的服务帐户创建一个 JSON 服务帐户密钥。这将最终用于通过 kubectl 认证到 Kubernetes 集群。您还需要将此服务帐户密钥放入您的生产 JupyterHub 环境中。

  3. GOOGLE_APPLICATION_CREDENTIALS 环境变量设置为指向此 JSON 服务帐户密钥的位置。

    export GOOGLE_APPLICATION_CREDENTIALS=<path-to-json-service-account-key>
    
  4. 在您的自定义 kubeconfig 文件中生成一个适当的条目。

    gcloud container clusters get-credentials <cluster-name> --zone=<zone>
    

当您部署您的 JupyterHub 时,请确保将环境变量 GOOGLE_APPLICATION_CREDENTIALS 设置为指向 JupyterHub 可以找到的服务帐户密钥的位置。

在 AWS 上使用 EKS

AWS 有许多有趣的选项用于认证,这里我们将指定最简单的方法(尽管可能不是最安全或“最佳实践”的方法)。

  1. 为使用 kubectl 认证到 AWS 创建一个 AWS IAM 用户。此用户需要一个访问密钥和访问密钥,但不需要控制台访问。

  2. 为此用户创建一个 访问密钥。JupyterHub 在运行时需要这些密钥来向 Kubernetes API 发送请求,并将其设置为 环境变量

  3. 授予用户 eks:DescribeCluster 权限,直接授予或通过您为此目的创建的组进行授予。

  4. 通过编辑 aws-auth 配置映射,按照 本文档中所述 授予用户访问 Kubernetes API 的权限。

  5. 在您的 KUBECONFIG 文件中生成一个适当的条目。

    export AWS_ACCESS_KEY_ID=<access-key-id>
    export AWS_SECRET_ACCESS_KEY=<access-key-secret>
    aws eks update-kubeconfig --name=<cluster-name> --region=<aws-region>
    

当您部署您的 JupyterHub 时,您需要在 JupyterHub 进程本身上设置环境变量 AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY,以便它可以正确地与 Kubernetes API 通信。

在 DigitalOcean 上

  1. 安装 doctl

  2. 创建一个用于访问您的Kubernetes集群的个人访问令牌。与其他云服务提供商不同,此令牌不能限制访问范围——它授予对您整个DO账户的完全访问权限!所以请谨慎使用。

  3. 在您的 KUBECONFIG 文件中生成一个适当的条目。

    export DIGITALOCEAN_ACCESS_TOKEN=<your-digitalocean-access-token>
    doctl kubernetes cluster kubeconfig save <cluster-name>
    

当您部署JupyterHub时,需要在JupyterHub进程本身上设置环境变量DIGITALOCEAN_ACCESS_TOKEN,以便它能够正确地与Kubernetes API通信。

设置目标集群

每个目标集群都需要安装一个入口控制器,以便孵化器可以与其通信。这提供了一个公共IP,JupyterHub可以使用它来代理流量到该集群上的用户Pod。

任何入口服务提供商都可以使用,尽管当前建议使用Project Contour,因为它在检测路由更改方面比更流行的nginx-ingress更快。

“生产”安装可能使用helmcontour helm图表。但为了快速开始,您也可以仅配置kubectl以指向正确的kubernetes集群,并运行kubectl apply --wait -f https://projectcontour.io/quickstart/contour.yaml。成功后,您可以使用kubectl -n projectcontour get svc envoy获取入口控制器的公共IP。这里的EXTERNAL-IP值可以传递给集群的ingress_public_url配置选项。

设置profile_list

登录后,每个用户都将获得一个可以选择的profile列表。每个profile可以指向不同的Kubernetes集群,以及其他自定义设置,如要使用的镜像、RAM / CPU数量、GPU使用情况等。

列表中的每个项都是一个Python字典,以下键被识别:

  1. display_name:在profile选择屏幕上显示给用户的名称
  2. description:在profile选择屏幕上显示给用户的描述
  3. spawner_override:此profile的孵化器选项字典,确定用户Pod的启动位置及其其他属性。

以下属性在spawner_override下受支持。

  1. kubernetes_context:连接到此集群时要使用的kubernetes上下文名称。您可以使用kubectl config get-contexts获取您的KUBECONFIG文件中可用的上下文列表。
  2. ingress_public_url:您之前设置的入口控制器公共端点的URL。这应该格式化为URL,所以别忘了加上http://。对于生产系统,您还应该为您的入口控制器设置HTTPS,并提供https://<域名>
  3. patches:一系列修补程序(作为传递给kubectl patch)。以下将更详细地描述。这是自定义用户Pod的主要方法,尽管也提供了一些便捷方法(如下所述)。
  4. environment:为用户Pod设置的额外环境变量字典。
  5. image:用于用户Pod的镜像。默认为pangeo/pangeo-notebook:latest
  6. mem_limitmem_guarantee,如JupyterHub所理解
  7. cpu_limitcpu_guarantee,如JupyterHub所理解

以下是一个简单的示例

c.MultiClusterKubeSpawner.profile_list = [
    {
        "display_name": "Google Cloud in us-central1",
        "description": "Compute paid for by funder A, closest to dataset X",
        "spawner_override": {
            "kubernetes_context": "<kubernetes-context-name-for-this-cluster">,
            "ingress_public_url": "http://<ingress-public-ip-for-this-cluster>"
        }
    },
    {
        "display_name": "AWS on us-east-1",
        "description": "Compute paid for by funder B, closest to dataset Y",
        "spawner_override": {
            "kubernetes_context": "<kubernetes-context-name-for-this-cluster">,
            "ingress_public_url": "http://<ingress-public-ip-for-this-cluster>",
            "patches": {
                "01-memory": """
                    kind: Pod
                    metadata:
                        name: {{key}}
                    spec:
                        containers:
                        - name: notebook
                        resources:
                            requests:
                                memory: 16Mi
                    """,
            }
        }
    },
]

使用patches进行自定义

为了尽可能地实现 Kubernetes 本地化,我们使用 Kubernetes 官方工具 战略合并补丁(strategic merge patch),允许 JupyterHub 运维人员为每个用户定制资源。这样,运维人员可以精细地控制每个用户启动的资源,而不需要为每个可能的定制修改此启动器的维护者花费大量精力。

幕后,使用 kubectl patch 命令将每个用户生成的 Kubernetes 资源初始列表与一些自定义配置合并,然后再传递给 kubectl apply 命令。这些配置是通过自定义 patches traitlet 来设置的。这些补丁可以通过设置 c.MultiClusterKubeSpawner.patches 为所有配置文件或仅针对特定配置文件设置 spawner_override 下的 patches 来实现。

patches 是一个字典,键仅用于排序,值是一个字符串,在经过模板替换后应是一个有效的 YAML 对象。资源根据 YAML 中的 kindmetadata.name 键的值进行合并。kubectl 知道何时将项目添加到列表或根据适当属性合并它们的属性。

要修补用户 pod,添加一些额外的注解并请求 GPU,可以设置以下内容

c.MultiClusterKubernetesSpawner.patches = {
    "01-annotations": """
    kind: Pod
    metadata:
        name: {{key}}
        annotations:
            something-else: hey
    """,
    "02-gpu": """
    kind: Pod
    metadata:
        name: {{key}}
    spec:
        containers:
        - name: notebook
          resources:
            limits:
                nvidia.com/gpu: 1
    """,
}

这些值首先通过 jinja2 模板 展开,然后再传递给 kubectl patch。`{{key}}` 展开为资源的名称,您应使用它进行所有修改。在 02-gpu 补丁中,kubectl 知道将其与现有的笔记本容器合并,而不是创建新的容器或替换所有现有值,因为它知道已存在一个具有 name 属性设置为 notebook 的容器。因此,它将此补丁中提供的值与容器现有的配置合并。

请阅读 kubectl 文档 以了解战略合并补丁的工作原理。

使用 resources 的每个用户额外的 Kubernetes 资源

您还可以通过设置 resources 配置为每个用户创建任意额外的 Kubernetes 资源。它是一个字典,键用于排序,值在通过 jinja2 模板展开后应该是有效的 YAML。

例如,以下配置为每个用户创建一个 Kubernetes 角色和角色绑定(Role and RoleBinding),允许他们(不安全地!)运行 dask-kubernetes 集群。

c.MultiClusterKubernetesSpawner.resources = {
    "10-dask-role": """
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: {{key}}-dask
    rules:
    - apiGroups:
      - ""
      resources:
      - pods
      verbs:
      - list
      - create
      - delete
    """,
    "11-dask-rolebinding": """
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: {{key}}-dask
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: {{key}}-dask
    subjects:
    - apiGroup: ""
      kind: ServiceAccount
      name: {{key}}
    """,
)

这利用了默认情况下,MultiClusterUserSpawner 已经为每个 pod 创建了 Kubernetes 服务帐户(Service Account) 的事实,并赋予它创建、列出和删除 pod 的足够权限。

项目详细信息


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解有关 安装软件包 的更多信息。

源分发

jupyterhub-multicluster-kubespawner-0.2.tar.gz (15.9 kB 查看哈希)

上传时间

构建分发

jupyterhub_multicluster_kubespawner-0.2-py3-none-any.whl (16.9 kB 查看哈希值)

上传时间 Python 3

由以下机构支持