适用于多个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 上
-
创建一个 Google Cloud 服务帐户,并授予它足够的权限来访问您的 Kubernetes 集群。
roles/container.developer
应该是 足够的权限。 -
为您的服务帐户创建一个 JSON 服务帐户密钥。这将最终用于通过
kubectl
认证到 Kubernetes 集群。您还需要将此服务帐户密钥放入您的生产 JupyterHub 环境中。 -
将
GOOGLE_APPLICATION_CREDENTIALS
环境变量设置为指向此 JSON 服务帐户密钥的位置。export GOOGLE_APPLICATION_CREDENTIALS=<path-to-json-service-account-key>
-
在您的自定义
kubeconfig
文件中生成一个适当的条目。gcloud container clusters get-credentials <cluster-name> --zone=<zone>
当您部署您的 JupyterHub 时,请确保将环境变量 GOOGLE_APPLICATION_CREDENTIALS
设置为指向 JupyterHub 可以找到的服务帐户密钥的位置。
在 AWS 上使用 EKS
AWS 有许多有趣的选项用于认证,这里我们将指定最简单的方法(尽管可能不是最安全或“最佳实践”的方法)。
-
为使用
kubectl
认证到 AWS 创建一个 AWS IAM 用户。此用户需要一个访问密钥和访问密钥,但不需要控制台访问。 -
为此用户创建一个 访问密钥。JupyterHub 在运行时需要这些密钥来向 Kubernetes API 发送请求,并将其设置为 环境变量。
-
授予用户
eks:DescribeCluster
权限,直接授予或通过您为此目的创建的组进行授予。 -
通过编辑
aws-auth
配置映射,按照 本文档中所述 授予用户访问 Kubernetes API 的权限。 -
在您的
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_ID
和 AWS_SECRET_ACCESS_KEY
,以便它可以正确地与 Kubernetes API 通信。
在 DigitalOcean 上
-
安装 doctl
-
创建一个用于访问您的Kubernetes集群的个人访问令牌。与其他云服务提供商不同,此令牌不能限制访问范围——它授予对您整个DO账户的完全访问权限!所以请谨慎使用。
-
在您的
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更快。
“生产”安装可能使用helm和contour 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字典,以下键被识别:
display_name
:在profile选择屏幕上显示给用户的名称description
:在profile选择屏幕上显示给用户的描述spawner_override
:此profile的孵化器选项字典,确定用户Pod的启动位置及其其他属性。
以下属性在spawner_override
下受支持。
kubernetes_context
:连接到此集群时要使用的kubernetes上下文名称。您可以使用kubectl config get-contexts
获取您的KUBECONFIG
文件中可用的上下文列表。ingress_public_url
:您之前设置的入口控制器公共端点的URL。这应该格式化为URL,所以别忘了加上http://
。对于生产系统,您还应该为您的入口控制器设置HTTPS,并提供https://<域名>
。patches
:一系列修补程序(作为传递给kubectl patch
)。以下将更详细地描述。这是自定义用户Pod的主要方法,尽管也提供了一些便捷方法(如下所述)。environment
:为用户Pod设置的额外环境变量字典。image
:用于用户Pod的镜像。默认为pangeo/pangeo-notebook:latest
。mem_limit
和mem_guarantee
,如JupyterHub所理解cpu_limit
和cpu_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 中的 kind
和 metadata.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 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 74cc2f4d55c819394351a206a0a0cc553a88dedc28be06bc74b37d2d86343921 |
|
MD5 | 5740f5d2c57ec85a13e4efac9130c1e9 |
|
BLAKE2b-256 | 93ee934a93116bfc60afd8abfaf01141d66d48517fe92683157d57cc93527588 |
jupyterhub_multicluster_kubespawner-0.2-py3-none-any.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 4ab91e6e7d251098e5bd28ec405a7fe8183d2ed6a3b6d2c7f2f92c775badca41 |
|
MD5 | a812a301bb594115457637638190a08b |
|
BLAKE2b-256 | 861b768d740128ae55201e9c56040f5a1a14d3941c5bb7dea42e9d2702308079 |