本教程介绍如何使用节点自动预配功能来扩缩多租户 Google Kubernetes Engine (GKE) 集群以及如何使用 Workload Identity 控制租户对 Cloud Storage 存储分区等资源的访问权限。本指南面向开发者和架构师;它假定您具备 Kubernetes 和 GKE 的基础知识。如果您需要简介,请参阅 GKE 概览。
集群多租户通常用于降低费用或标准化跨租户的操作。要全面节省费用,则应调整集群大小,以便高效地使用集群资源。此外,您还应在集群自动扩缩时,确保所添加的集群节点大小合适,从而最大限度地减少资源浪费。
在本教程中,您将使用节点自动预配功能来扩缩集群。节点自动预配功能可以通过添加最适合待处理工作负载的集群节点来帮助优化集群资源用量,从而控制费用。
目标
- 创建已启用节点自动预配功能和 Workload Identity 的 GKE 集群。
- 为多租户设置集群。
- 将作业提交到集群以展示节点自动预配功能如何创建和销毁优化大小的节点。
- 使用污点和标签来指示节点自动预配功能为每个租户创建专用的节点池。
- 使用 Workload Identity 控制对租户专属资源(如 Cloud Storage 存储分区)的访问权限。
费用
在本文档中,您将使用 Google Cloud 的以下收费组件:
准备工作
- 登录您的 Google Cloud 账号。如果您是 Google Cloud 新手,请创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
在 Google Cloud 控制台中,激活 Cloud Shell。
Cloud Shell 会话随即会在 Google Cloud 控制台的底部启动,并显示命令行提示符。Cloud Shell 是一个已安装 Google Cloud CLI 且已为当前项目设置值的 Shell 环境。该会话可能需要几秒钟时间来完成初始化。
- 在 Cloud Shell 中,为 GKE 和 Cloud Build API 启用 API:
gcloud services enable container.googleapis.com \ cloudbuild.googleapis.com
此操作可能需要几分钟才能完成。
准备环境
在本部分中,您将获取本教程所需的代码,并使用整个教程中使用的值来设置您的环境。
在 Cloud Shell 中,定义本教程使用的环境变量:
export PROJECT_ID=$(gcloud config get-value project)
克隆包含本教程代码的 GitHub 代码库:
git clone https://github.com/GoogleCloudPlatform/solutions-gke-autoprovisioning
切换到代码库目录:
cd solutions-gke-autoprovisioning
使用您的 Google 项目 ID 更新 Kubernetes YAML 作业配置文件:
sed -i "s/MY_PROJECT/$PROJECT_ID/" manifests/bases/job/base-job.yaml
提交 Cloud Build 作业以构建容器映像:
gcloud builds submit pi/ --tag gcr.io/$PROJECT_ID/generate-pi
该映像是一个 Go 程序,可生成 pi 的近似值。您稍后使用此容器映像。
Cloud Build 将映像导出到项目的 Container Registry 中。
创建 GKE 集群
在本部分中,您将创建一个启用了节点自动预配功能和 Workload Identity 的 GKE 集群。请注意以下关于集群创建过程的详细信息:
- 您可以为集群指定 CPU 和内存限制。节点自动预配功能在从集群添加或移除节点时遵循这些限制。如需了解详情,请参阅 GKE 文档中的启用节点自动预配功能。
- 您可以指定自动预配节点池中的节点使用的默认服务账号和范围。使用这些设置,您可以控制预配节点的访问权限。如需了解详情,请参阅 GKE 文档中的为自动预配的节点设置身份默认。
- 您需要设置自动扩缩配置文件来优先处理利用率。此配置文件告知集群自动扩缩器,以快速缩小集群,从而最大限度地减少未使用的资源。这有助于提高批量或以作业为中心的工作负载的资源效率。该设置适用于集群中的所有节点池。
- 您可以指定工作负载池来启用 Workload Identity。
使用以下命令创建集群:
创建服务账号:
gcloud iam service-accounts create nap-sa
此服务账号由自动预配的节点使用。
授予新的服务账号从 Container Registry 使用的 Cloud Storage 存储分区拉取映像的权限:
gsutil iam ch \ serviceAccount:nap-sa@$PROJECT_ID.iam.gserviceaccount.com:objectViewer \ gs://artifacts.$PROJECT_ID.appspot.com
创建已启用节点自动预配功能和 Workload Identity 的 GKE 集群:
gcloud container clusters create multitenant \ --release-channel=regular \ --zone=us-central1-c \ --num-nodes=2 \ --machine-type=n1-standard-2 \ --workload-pool=${PROJECT_ID}.svc.id.goog \ --autoscaling-profile=optimize-utilization \ --enable-autoprovisioning \ --autoprovisioning-service-account=nap-sa@${PROJECT_ID}.iam.gserviceaccount.com \ --autoprovisioning-scopes=\ https://www.googleapis.com/auth/devstorage.read_write,\ https://www.googleapis.com/auth/cloud-platform \ --min-cpu 1 \ --min-memory 1 \ --max-cpu 50 \ --max-memory 256 \ --enable-network-policy \ --enable-ip-alias
设置默认集群名称和计算区域:
gcloud config set container/cluster multitenant gcloud config set compute/zone us-central1-c
为多租户设置集群
当您运行多租户软件即服务 (SaaS) 应用时,通常应该分离您的租户。分离租户有助于最大限度地减少来自破解租户所造成的任何损害。此外,它还可以甚至跨租户分配集群资源,并跟踪每个租户消耗了多少资源。Kubernetes 不能保证租户之间完全安全地隔离,但是它确实提供了足以满足特定用例的功能。如需详细了解 GKE 多租户功能,请参阅 GKE 文档中的概览和最佳做法指南部分。
在示例应用中,您将创建两个租户:tenant1
和 tenant2
。您可以将每个租户及其 Kubernetes 资源分隔到各自的命名空间中。您可以创建一个简单的网络政策,以通过防止来自其他命名空间的通信来强制执行租户隔离。稍后,您可以使用节点污点和 nodeSelector
字段来防止不同租户的 Pod 被调度到同一节点上。您可以通过在专用节点上运行租户工作负载来提供其他分离程度。
您可以使用 Kustomize 管理您提交到集群的 Kubernetes 清单。Kustomize 可让您组合和自定义 YAML 文件,以实现多种目的。
为
tenant1
创建命名空间、服务账号和网络政策资源:kubectl apply -k manifests/setup/tenant1
输出如下所示:
namespace/tenant1-ns created serviceaccount/tenant1-ksa created networkpolicy.networking.k8s.io/tenant1-deny-from-other-namespaces created
为
tenant2
创建集群资源:kubectl apply -k manifests/setup/tenant2
验证节点自动预配的行为
GKE 集群由一个或多个节点池组成。节点池中的所有节点具有相同的机器类型,这意味着它们拥有相同数量的 CPU 和内存。如果您的工作负载资源需求是可变的,则您可能会受益于拥有多个在集群中具有不同机器类型的节点池。通过这种方式,集群自动扩缩器可以添加最合适的类型的节点,从而提高资源效率并因此降低费用。但是,维护多个节点池会增加管理开销。如果要在专用节点池中执行租户工作负载,则在多租户集群中可能也不可行。
您可以改用节点自动预配来扩展集群自动扩缩器。启用节点自动预配功能后,集群自动扩缩器可以根据待处理 Pod 的规范自动创建新节点池。因此,集群自动扩缩器可以创建最合适的类型节点,但您不必自行创建或管理节点池。使用节点自动预配功能,您的集群可以在不超额预配的情况下高效自动扩缩,这有助于降低费用。
此外,如果待处理 Pod 具有工作负载分离限制,节点自动预配功能可以创建满足限制条件的节点。通过这种方式,您可以使用节点自动预配功能自动创建仅由单个租户使用的节点池。
在本部分中,您将向集群提交各种作业以验证节点自动预配的行为。这些作业使用您之前创建的 generate-pi
映像。
提交简单作业
首先,向集群提交一个简单的作业。作业未指定任何特定于租户的限制。集群中有足够的空闲容量来处理作业的 CPU 和内存请求。因此,您希望将作业安排到默认节点池中的现有节点之一。未预配其他节点。
列出集群中的节点池:
gcloud container node-pools list
您会看到一个默认池。
将作业的配置打印到控制台:
kubectl kustomize manifests/jobs/simple-job/
输出如下所示:
apiVersion: batch/v1 kind: Job metadata: name: pi-job spec: ...
该配置未指定任何节点污点或选择器。
提交作业:
kubectl apply -k manifests/jobs/simple-job/
监视集群中的节点池:
watch -n 5 gcloud container node-pools list
您仍会看到一个默认池。未创建新节点池。
大约 30 秒后,按
Control+C
停止监控节点池。监视集群中的节点:
kubectl get nodes -w
您不会看到正在创建任何新节点。
观看 1 分钟后,按
Control+C
停止观看。列出集群中的作业:
kubectl get jobs --all-namespaces
输出如下所示:
NAMESPACE NAME COMPLETIONS DURATION AGE default pi-job 1/1 14s 21m
Completions
列中的1/1
值表示总共 1 个作业中有 1 个作业已经完成。
提交具有租户专用限制条件的作业
在本部分中,您将提交另一个作业,以确认节点自动预配功能是否遵循工作负载分离限制。作业配置包括特定于租户的节点选择器和特定于租户的容忍设置。作业只能安排在具有与选择器的键值对匹配的标签的节点上。容忍设置与节点污点结合使用,这还可以限制可以将哪些作业安排到节点上。节点自动预配的最佳做法是包含节点选择器和工作负载分离容忍设置。
此作业无法安排到默认节点池,因为该池没有满足选择器限制条件的任何节点。因此,节点自动预配功能会创建具有满足选择器要求的节点标签的新节点池。节点自动预配功能还会向与作业配置中的容忍设置匹配的节点添加特定于租户的污点。只有具有相匹配的容忍设置的 Pod 可以被安排到池中的节点上,这样您就可以进一步拆分租户工作负载。
列出集群中的节点池:
gcloud container node-pools list
您会看到一个默认池。
将作业的配置打印到控制台:
kubectl kustomize manifests/jobs/one-tenant/
该配置包括特定于租户的节点选择器要求和容忍设置。输出如下所示:
apiVersion: batch/v1 kind: Job metadata: name: tenant1-pi-job spec: ...
提交作业:
kubectl apply -k manifests/jobs/one-tenant/
监视集群中的节点池:
watch -n 5 gcloud container node-pools list
一段时间后,您将看到新的节点池。输出如下所示:
NAME MACHINE_TYPE DISK_SIZE_GB default-pool n1-standard-2 100 nap-n1-standard-1-15jwludl n1-standard-1 100
节点池名称带有
nap-
前缀,这表明它是由节点自动预配功能创建的。节点池名称还包括池中节点的机器类型,例如n1-standard-1
。监视集群中的节点:
kubectl get nodes -w
大约一分钟后,您会在列表中看到一个新节点。节点名称包含
nap-
节点池的名称。新节点的初始状态为Not Ready
。一段时间后,新节点的状态会更改为Ready
,这表示节点现在可接受待处理工作。如需停止监控节点,请按
Control+C
。列出节点污点:
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
您会看到,新节点有一个键值对
tenant: tenant1
的NoSchedule
污点。因此,只有具有与tenant: tenant1
对应的容忍设置的 Pod 可以安排到节点上。监控集群中的作业:
kubectl get jobs -w --all-namespaces
一段时间后,您会看到
tenant1-pi-job
已完成1/1
,这表示它成功完成。如需停止监控作业,请按
Control+C
。监视集群中的节点池:
watch -n 5 gcloud container node-pools list
一段时间后,您会看到
nap-
池已被删除,而且集群再次只有一个默认节点池。节点自动预配功能已删除nap-
节点池,因为没有更多与池的限制条件匹配的待处理工作。要停止监控节点池,请按
Control+C
。
提交两个具有租户限制的较大作业
在本部分中,您将提交两个具有租户专用限制的作业,同时还会增加每个作业的资源请求。同样,由于节点选择器的限制条件,这些作业不能安排到默认节点池中。由于每个作业都有自己的选择器限制条件,因此节点自动预配功能会创建两个新的节点池。通过这种方式,您可以使用节点自动预配功能将租户作业分开。由于作业与前一个作业相比具有更多的资源请求,因此节点自动预配功能创建的节点池会具有比上一次更大的机器类型。
列出集群中的节点池:
gcloud container node-pools list
您会看到一个默认池。
打印合并的配置:
kubectl kustomize manifests/jobs/two-tenants/
配置包括两个单独的作业,每个作业都有一个特定于租户的节点选择器和容忍设置,以及增加的资源请求。
输出如下所示:
apiVersion: batch/v1 kind: Job metadata: name: tenant1-larger-pi-job spec: ...
提交作业:
kubectl apply -k manifests/jobs/two-tenants/
监视集群中的节点池:
watch -n 5 gcloud container node-pools list
一段时间后,您会看到另外两个节点池。输出如下所示:
NAME MACHINE_TYPE DISK_SIZE_GB default-pool n1-standard-2 100 nap-n1-standard-2-6jxjqobt n1-standard-2 100 nap-n1-standard-2-z3s06luj n1-standard-2 100
节点池名称带有
nap-
前缀,这表明它们是由节点自动预配功能创建的。节点池名称还包括池中节点的机器类型,例如n1-standard-2
。如需停止监控节点,请按
Control+C
。监视集群中的节点:
kubectl get nodes -w
大约一分钟后,您会在列表中看到两个新节点。节点名称包含其关联的
nap-
节点池的名称。新节点最初处于Not Ready
状态。一段时间后,新节点的状态会更改为Ready
,这表示节点现在可接受待处理工作。如需停止监控节点,请按
Control+C
。列出节点污点:
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
您会看到新节点有
NoSchedule
污点,一个有键值对tenant: tenant1
,另一个有tenant: tenant2
。只有具有相应租户容忍设置的 Pod 可以被安排到节点上。监控集群中的作业:
kubectl get jobs -w --all-namespaces
一段时间后,您发现
tenant1-larger-pi-job
和tenant2-larger-pi-job
发生了变化,以分别完成1/1
,这表示作业已成功完成。如需停止监控作业,请按
Control+C
。监视集群中的节点池:
watch -n 5 gcloud container node-pools list
一段时间后,您会看到两个
nap-
池均已被删除,而且集群再次只有一个默认节点池。节点自动预配功能已删除nap-
节点池,因为没有更多与池限制条件匹配的待处理工作。要停止监控节点池,请按
Control+C
。
控制对 Google Cloud 资源的访问权限
除了维护集群内租户的分离之外,您通常还希望控制租户对 Google Cloud 资源(例如 Cloud Storage 存储分区或 Pub/Sub 主题)的访问权限。例如,每个租户可能需要一个不应被其他租户访问的 Cloud Storage 存储分区。
使用 Workload Identity,您可以在 Kubernetes 服务账号与 Google Cloud 服务账号之间创建映射。然后,您可以为 Google Cloud 服务账号分配适当的 Identity and Access Management (IAM) 角色。通过这种方式,您可以强制执行最小特权原则,以便租户作业可以访问其分配的资源,但不能访问其他租户拥有的资源。
设置 GKE Workload Identity
配置 Kubernetes 服务账号与您创建的 Google Cloud 服务账号之间的映射。
为
tenant1
创建 Google Cloud 服务账号:gcloud iam service-accounts create tenant1-gsa
向 Kubernetes 服务账号授予
tenant1
IAM 权限,以便使用tenant1
的相应 Google Cloud 服务账号:gcloud iam service-accounts add-iam-policy-binding \ tenant1-gsa@${PROJECT_ID}.iam.gserviceaccount.com \ --role roles/iam.workloadIdentityUser \ --member "serviceAccount:${PROJECT_ID}.svc.id.goog[tenant1-ns/tenant1-ksa]"
通过 Google Cloud 服务账号注释 Kubernetes 服务账号,完成服务账号之间的映射:
kubectl annotate serviceaccount tenant1-ksa -n tenant1-ns \ iam.gke.io/gcp-service-account=tenant1-gsa@${PROJECT_ID}.iam.gserviceaccount.com
提交写入 Cloud Storage 存储分区的作业
在本部分中,您将确认作为特定 Kubernetes 服务账号执行的作业可以使用其映射的 Google Cloud 服务账号的 IAM 权限。
为
tenant1
创建一个新的 Cloud Storage 存储分区:export BUCKET=tenant1-$PROJECT_ID gsutil mb -b on -l us-central1 gs://$BUCKET
您可以使用项目 ID 作为存储分区名称的后缀,以使名称具有惟一性。
更新作业的配置文件以使用 Cloud Storage 存储分区:
sed -i "s/MY_BUCKET/$BUCKET/" \ manifests/jobs/write-gcs/bucket-write.yaml
向
tenant1
服务账号授予读写存储分区中的对象的权限:gsutil iam ch \ serviceAccount:tenant1-gsa@$PROJECT_ID.iam.gserviceaccount.com:objectAdmin \ gs://$BUCKET
打印作业配置:
kubectl kustomize manifests/jobs/write-gcs/
输出如下所示:
apiVersion: batch/v1 kind: Job metadata: name: tenant1-pi-job-gcs spec: ...
新存储分区名称将作为参数传递给
generate-pi
容器,并且作业指定相应的tenant1-ksa
Kubernetes 服务账号。提交作业:
kubectl apply -k manifests/jobs/write-gcs/
如上一部分中所述,节点自动预配功能会创建新节点池和新节点来执行作业。
监控作业的 Pod:
kubectl get pods -n tenant1-ns -w
在这种情况下,您要监控 Pod 而不是监控节点池。您会看到 Pod 在不同状态下的转换几分钟后,状态会更改为
Completed
。此状态表示作业已成功完成。要停止观察,请按
Control+C
。确认文件已被写入 Cloud Storage 存储分区:
gsutil ls -l gs://$BUCKET
您看到一个文件。
要进行清理,删除作业:
kubectl delete job tenant1-pi-job-gcs -n tenant1-ns
您将在下一部分中重新提交此作业。
撤消 IAM 权限
最后,您将确认撤消 Google Cloud 服务账号的 IAM 权限,阻止映射的 Kubernetes 服务账号访问 Cloud Storage 存储分区。
撤消 Google Cloud 服务账号对 Cloud Storage 存储分区的写入权限:
gsutil iam ch -d \ serviceAccount:tenant1-gsa@$PROJECT_ID.iam.gserviceaccount.com:objectAdmin \ gs://$BUCKET
提交与上一步相同的作业:
kubectl apply -k manifests/jobs/write-gcs/
再次查看该作业的 Pod 状态:
kubectl get pods -n tenant1-ns -w
几分钟后,状态会更改为
Error
,这表示作业失败。此错误是正常的,因为此作业将作为 Kubernetes 服务账号执行,而该服务账号会映射到 Google Cloud 服务账号,而该账号反过来不再拥有 Cloud Storage 存储分区的写入权限。如需停止监控 Pod,请按
Control+C
。列出存储分区中的文件:
gsutil ls -l gs://$BUCKET
您会在存储分区中看到一个文件;新文件尚未写入。
清理
避免产生费用的最简单的方法是删除您为本教程创建的 Google Cloud 项目。
删除项目
- 在 Google Cloud 控制台中,进入管理资源页面。
- 在项目列表中,选择要删除的项目,然后点击删除。
- 在对话框中输入项目 ID,然后点击关闭以删除项目。
删除 GKE 集群
如果您不想删除项目,请删除 GKE 集群:
gcloud container clusters delete multitenant
后续步骤
- 详细了解 GKE 多租户。
- 探索集群自动扩缩器。
- 探索有关 Google Cloud 的参考架构、图表和最佳做法。查看我们的 Cloud Architecture Center。