本教程介绍如何使用 KEDA 将 GKE 工作负载缩减至零个 Pod。在非活动期间(例如周末和非办公时间),或对于间歇性工作负载(例如定期作业),将部署缩减至零个 Pod 可以节省资源。
目标
本教程介绍以下应用场景:
- 将 Pub/Sub 工作负载缩减为零:根据 Pub/Sub 主题上排队的消息数量,按比例缩放 Pod 数量。当队列为空时,工作负载会自动缩减至零个 Pod。
- 将 LLM 工作负载缩减至零。在具有 GPU 的节点上部署 LLM 模型服务器。当服务处于空闲状态时,工作负载会自动缩减至零个 Pod。
费用
在本文档中,您将使用 Google Cloud的以下收费组件:
- GKE
- GPU resources used by GKE
- Pub/Sub
如需根据您的预计使用量来估算费用,请使用价格计算器。
完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理。
准备工作
在本教程中,您将使用 Cloud Shell 运行命令。Cloud Shell 是一种 shell 环境,用于管理在 Google Cloud上托管的资源。它预装了 Google Cloud CLI、kubectl、Helm 和 Terraform 命令行工具。如果您不使用 Cloud Shell,则必须安装 Google Cloud CLI 和 Helm。
-
如需运行此页面中的命令,请在以下开发环境之一中设置 gcloud CLI:
Cloud Shell
如需使用已设置 gcloud CLI 的在线终端,请激活 Cloud Shell:
Cloud Shell 会话会在页面底部启动,并显示命令行提示符。该会话可能需要几秒钟来完成初始化。
本地 shell
如需使用本地开发环境,请按照以下步骤操作:
- Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
Roles required to select or create a project
- Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
-
Create a project: To create a project, you need the Project Creator role
(
roles/resourcemanager.projectCreator), which contains theresourcemanager.projects.createpermission. Learn how to grant roles.
-
Verify that billing is enabled for your Google Cloud project.
-
Enable the Resource Manager, Compute Engine, GKE, Pub/Sub APIs.
Roles required to enable APIs
To enable APIs, you need the Service Usage Admin IAM role (
roles/serviceusage.serviceUsageAdmin), which contains theserviceusage.services.enablepermission. Learn how to grant roles. -
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
Roles required to select or create a project
- Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
-
Create a project: To create a project, you need the Project Creator role
(
roles/resourcemanager.projectCreator), which contains theresourcemanager.projects.createpermission. Learn how to grant roles.
-
Verify that billing is enabled for your Google Cloud project.
-
Enable the Resource Manager, Compute Engine, GKE, Pub/Sub APIs.
Roles required to enable APIs
To enable APIs, you need the Service Usage Admin IAM role (
roles/serviceusage.serviceUsageAdmin), which contains theserviceusage.services.enablepermission. Learn how to grant roles. 设置环境变量:
export PROJECT_ID=PROJECT_ID export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format 'get(projectNumber)') export LOCATION=LOCATION将
PROJECT_ID替换为您的 Google Cloud项目 ID,将LOCATION替换为应创建 GKE 集群的区域或可用区。如果您未在单次会话中完成整个教程,或者您的环境变量因某种原因而未设置,请务必再次运行此命令以再次设置变量。
创建一个启用了集群自动扩缩和 Workload Identity Federation for GKE 的 Standard GKE 集群:
gcloud container clusters create scale-to-zero \ --project=${PROJECT_ID} --location=${LOCATION} \ --machine-type=n1-standard-2 \ --enable-autoscaling --min-nodes=1 --max-nodes=5 \ --workload-pool=${PROJECT_ID}.svc.id.goog创建
ScaledObject资源以描述预期的自动扩缩行为:curl https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes-engine-samples/refs/heads/main/cost-optimization/gke-keda/cloud-pubsub/deployment/keda-pubsub-scaledobject.yaml | envsubst | kubectl apply -f -这会创建以下对象:
apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: keda-pubsub namespace: keda-pubsub spec: maxReplicaCount: 5 scaleTargetRef: name: keda-pubsub triggers: - type: gcp-pubsub authenticationRef: name: keda-auth metadata: subscriptionName: "projects/${PROJECT_ID}/subscriptions/keda-echo-read"检查 KEDA 基于
ScaledObject对象创建的HorizontalPodAutoscaler(HPA) 对象:kubectl get hpa keda-hpa-keda-pubsub -n keda-pubsub -o yaml您可以在 Kubernetes 文档中详细了解自动扩缩。
等待 KEDA 确认 Pub/Sub 订阅为空,并将 Deployment 缩减为零副本。
检查工作负载自动扩缩器:
kubectl describe hpa keda-hpa-keda-pubsub -n keda-pubsub可以看到,在命令响应中,
ScalingActive条件为 false。关联的消息显示,Pod 横向自动扩缩器确认 KEDA 已将 Deployment 缩减至零,此时它会停止运行,直到 Deployment 重新扩容为一个 Pod。Name: keda-hpa-keda-pubsub Namespace: keda-pubsub Metrics: ( current / target ) "s0-gcp-ps-projects-[...]]" (target average value): 0 / 10 Min replicas: 1 Max replicas: 5 Deployment pods: 5 current / 5 desired Conditions: Type Status Reason Message ---- ------ ------ ------- AbleToScale True ScaleDownStabilized recent recommendations were higher than current one [...] ScalingActive False ScalingDisabled scaling is disabled since the replica count of the target is zero ScalingLimited True TooManyReplicas the desired replica count is more than the maximum replica count将消息加入 Pub/Sub 主题的队列:
for num in {1..20} do gcloud pubsub topics publish keda-echo --project=${PROJECT_ID} --message="Test" done验证 Deployment 正在扩容:
kubectl get deployments -n keda-pubsub在输出中,可以看到到“Ready”列显示一个副本:
NAME READY UP-TO-DATE AVAILABLE AGE keda-pubsub 1/1 1 1 2d创建一个包含挂接了 GPU 的
g2-standard-4节点的节点池,并配置集群自动扩缩功能以提供 0 到 2 个节点:gcloud container node-pools create gpu --machine-type=g2-standard-4 \ --location=${LOCATION} --cluster=scale-to-zero \ --min-nodes 0 --max-nodes 2 --num-nodes=1 --enable-autoscaling添加官方 Ollama Helm 图表仓库,并更新本地 Helm 客户端的仓库:
helm repo add ollama-helm https://otwld.github.io/ollama-helm/ helm repo update使用 Helm 图表部署 Ollama 服务器:
helm install ollama ollama-helm/ollama --create-namespace --namespace ollama \ -f https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes-engine-samples/refs/heads/main/cost-optimization/gke-keda/ollama/helm-values-ollama.yamlhelm-values-ollama.yaml配置指定要加载的 LLM 模型、GPU 要求以及 Ollama 服务器的 TCP 端口。创建
HTTPScaledObject资源以描述预期的自动扩缩行为:kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes-engine-samples/refs/heads/main/cost-optimization/gke-keda/ollama/keda-ollama-httpscaledobject.yaml这会创建
HTTPScaledObject对象,该对象定义了以下字段:scaleTargetRef:指定 KEDA-HTTP 应将请求转发到的 Service。在此示例中,主机为ollama.ollama的所有请求都会路由到 Ollama 服务器。scaledownPeriod:指定在未收到任何请求时缩减的速度(以秒为单位)。replicas:指定要为 Ollama 部署维护的 Pod 数量下限和上限。scalingMetric:指定用于驱动自动扩缩的指标,例如本示例中的请求速率。如需了解更多指标选项,请参阅 KEDA-HTTP 文档。
kind: HTTPScaledObject apiVersion: http.keda.sh/v1alpha1 metadata: namespace: ollama name: ollama spec: hosts: - ollama.ollama scaleTargetRef: name: ollama kind: Deployment apiVersion: apps/v1 service: ollama port: 11434 replicas: min: 0 max: 2 scaledownPeriod: 3600 scalingMetric: requestRate: targetValue: 20运行以下命令,验证 KEDA-HTTP 已成功处理上一步中创建的
HTTPScaledObject:kubectl get hpa,scaledobject -n ollama输出显示
HorizontalPodAutoscaler(由 KEDA 创建)和ScaledObject(由 KEDA-HTTP 创建)资源:NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE horizontalpodautoscaler.autoscaling/keda-hpa-ollama Deployment/ollama 0/100 (avg) 1 2 1 2d NAME SCALETARGETKIND SCALETARGETNAME MIN MAX TRIGGERS AUTHENTICATION READY ACTIVE FALLBACK PAUSED AGE scaledobject.keda.sh/ollama apps/v1.Deployment ollama 0 2 external-push True False False Unknown 2d验证 Deployment 缩减至零个 Pod。
等待
scaledownPeriod字段中设置的时长,然后运行以下命令:kubectl get deployments -n ollama输出显示 KEDA 缩减了 Ollama 部署,并且没有 Pod 正在运行:
NAME READY UP-TO-DATE AVAILABLE AGE ollama 0/0 0 0 2d清理 Pub/Sub 订阅和主题:
gcloud pubsub subscriptions delete keda-echo-read gcloud pubsub topics delete keda-echo删除 GKE 集群:
gcloud container clusters delete scale-to-zero --location=${LOCATION}- 详细了解如何在 GKE 中自动扩缩 LLM 推理工作负载。
- 浏览 KEDA GitHub 代码库和文档。
设置环境
如需使用 Cloud Shell 设置您的环境,请按照以下步骤操作:
安装 KEDA
KEDA 是一个与 Kubernetes Pod 横向自动扩缩器相辅相成的组件。借助 KEDA,您可以将 Deployment 缩减到零个 Pod,以及从零个 Pod 扩容到一个 Pod。Deployment 是一个 Kubernetes API 对象,可让您运行在集群节点中分布的多个 Pod 副本。在 GKE 至少创建一个 Pod 后,系统会应用标准的 Pod 横向自动扩缩器算法。
GKE 将 Deployment 缩减到零个 Pod 后,由于没有 Pod 在运行,自动扩缩无法依赖 CPU 利用率等 Pod 指标。因此,KEDA 允许使用 Kubernetes External Metrics API 的实现来提取来自集群外部的指标。您可以使用此 API 根据 Pub/Sub 订阅中的未完成消息数量等指标进行自动扩缩。如需查看所有受支持的指标来源的列表,请参阅 KEDA 文档。
使用 Helm 或 kubectl 在集群上安装 KEDA。
Helm
运行以下命令来添加 KEDA Helm 仓库、安装 KEDA Helm 图表并向 KEDA 服务账号授予 Cloud Monitoring 的读取权限:
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda --create-namespace --namespace keda
gcloud projects add-iam-policy-binding projects/${PROJECT_ID} \
--role roles/monitoring.viewer \
--member=principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/keda/sa/keda-operator
请注意,此命令还会设置授权规则,这些规则要求集群使用 Workload Identity Federation for GKE 进行设置。
kubectl
运行以下命令,使用 kubectl apply 安装 KEDA,并向 KEDA 服务账号授予 Cloud Monitoring 的读取权限:
kubectl apply --server-side -f https://github.com/kedacore/keda/releases/download/v2.15.1/keda-2.15.1.yaml
gcloud projects add-iam-policy-binding projects/${PROJECT_ID} \
--role roles/monitoring.viewer \
--member=principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/keda/sa/keda-operator
请注意,此命令还会设置授权规则,这些规则要求集群使用 Workload Identity Federation for GKE 进行设置。
确认所有 KEDA 资源都显示在 keda 命名空间下:
kubectl get all -n keda
如需详细了解 KEDA 设计和资源,请参阅 KEDA 文档。
将 Pub/Sub 工作负载缩减至零
本部分介绍了一种工作负载,它会处理来自 Pub/Sub 订阅的每条消息并确认其完成。该工作负载会动态扩缩:随着未确认消息数量的增加,自动扩缩功能会实例化更多 Pod 以确保及时处理。
缩减至零可确保当一段时间内未收到任何消息时不会实例化任何 Pod。这样可以节省资源,因为避免了 Pod 长时间处于空闲状态。
部署 Pub/Sub 工作负载
部署处理在 Pub/Sub 主题上排队的消息的示例工作负载。为了模拟真实的工作负载,此示例程序会在确认消息之前等待 3 秒。工作负载配置为在 keda-pubsub-sa 服务账号下运行。
运行以下命令以创建 Pub/Sub 主题和订阅、配置其权限,并在 keda-pubsub 命名空间下创建启动工作负载的 Deployment。
gcloud pubsub topics create keda-echo
gcloud pubsub subscriptions create keda-echo-read --topic=keda-echo
gcloud projects add-iam-policy-binding projects/${PROJECT_ID} \
--role=roles/pubsub.subscriber \
--member=principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/keda-pubsub/sa/keda-pubsub-sa
kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes-engine-samples/refs/heads/main/cost-optimization/gke-keda/cloud-pubsub/deployment/keda-pubsub-with-workload-identity.yaml
配置缩减至零
如需将 Pub/Sub 工作负载配置为缩减至零,请使用 KEDA 定义 ScaledObject 资源,以指定部署应如何扩缩。然后,KEDA 将自动创建和管理底层 HorizontalPodAutoscaler (HPA) 对象。
触发扩容
如需促使 Deployment 扩容,请执行以下操作:
KEDA 在观察到队列不为空后会将 Deployment 扩容。
将 LLM 工作负载缩减至零
本部分介绍部署挂接 GPU 的 Ollama 服务器的大语言模型 (LLM) 工作负载。Ollama 支持运行 Gemma 和 Llama 2 等热门 LLM,并主要通过 HTTP 公开其功能。
安装 KEDA-HTTP 插件
在非活跃期间将 HTTP 服务缩减至零个 Pod 会导致请求失败,因为没有后端来处理请求。
本部分介绍如何使用 KEDA-HTTP 插件来解决此问题。KEDA-HTTP 会启动一个 HTTP 代理,该代理接收用户请求并将其转发到已配置为缩减至零的 Service。当 Service 没有 Pod 时,代理会触发 Service 进行扩容,并缓冲请求,直到 Service 扩容到至少一个 Pod。
使用 Helm 安装 KEDA-HTTP 插件。如需了解详情,请参阅 KEDA-HTTP 文档。
helm repo add ollama-helm https://otwld.github.io/ollama-helm/
helm repo update
# Set the proxy timeout to 120s, giving Ollama time to start.
helm install http-add-on kedacore/keda-add-ons-http \
--create-namespace --namespace keda \
--set interceptor.responseHeaderTimeout=120s
部署 Ollama LLM 工作负载
如需部署 Ollama LLM 工作负载,请执行以下操作:
配置缩减至零
为了将 Ollama 工作负载配置为缩减至零,KEDA-HTTP 使用 HTTPScaledObject。
触发扩容
如需促使 Deployment 扩容,请使用 KEDA-HTTP 插件设置的代理来调用 Ollama 服务。这会导致请求速率指标的值增加,并触发第一个 Pod 的创建。
由于代理未对外公开,因此请使用 kubectl 端口转发功能来访问代理。
kubectl port-forward svc/keda-add-ons-http-interceptor-proxy -n keda 8080:8080 &
# Set the 'Host' HTTP header so that the proxy routes requests to the Ollama server.
curl -H "Host: ollama.ollama" \
http://localhost:8080/api/generate \
-d '{ "model": "gemma:7b", "prompt": "Hello!" }'
curl 命令将提示“Hello!”发送到 Gemma 模型。观察响应中返回的回答令牌。如需了解 API 的规范,请参阅 Ollama 指南。
清理
为避免因本教程中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。