使用 KEDA 缩减至零

本教程介绍如何使用 KEDA 将 GKE 工作负载缩减至零个 Pod。在非活动期间(例如周末和非办公时间),或对于间歇性工作负载(例如定期作业),将部署缩减至零个 Pod 可以节省资源。

目标

本教程介绍以下应用场景:

  • 将 Pub/Sub 工作负载缩减为零:根据 Pub/Sub 主题上排队的消息数量,按比例缩放 Pod 数量。当队列为空时,工作负载会自动缩减至零个 Pod。
  • 将 LLM 工作负载缩减至零。在具有 GPU 的节点上部署 LLM 模型服务器。当服务处于空闲状态时,工作负载会自动缩减至零个 Pod。

费用

在本文档中,您将使用 Google Cloud的以下收费组件:

如需根据您的预计使用量来估算费用,请使用价格计算器

新 Google Cloud 用户可能有资格申请免费试用

完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理

准备工作

在本教程中,您将使用 Cloud Shell 运行命令。Cloud Shell 是一种 shell 环境,用于管理在 Google Cloud上托管的资源。它预装了 Google Cloud CLIkubectlHelmTerraform 命令行工具。如果您不使用 Cloud Shell,则必须安装 Google Cloud CLI 和 Helm。

  1. 如需运行此页面中的命令,请在以下开发环境之一中设置 gcloud CLI:

    Cloud Shell

    如需使用已设置 gcloud CLI 的在线终端,请激活 Cloud Shell:

    Cloud Shell 会话会在页面底部启动,并显示命令行提示符。该会话可能需要几秒钟来完成初始化。

    本地 shell

    如需使用本地开发环境,请按照以下步骤操作:

    1. 安装 gcloud CLI
    2. 初始化 gcloud CLI
    3. 安装 Kubernetes 软件包管理工具 Helm。
  2. 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.
  3. 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 the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  4. Verify that billing is enabled for your Google Cloud project.

  5. 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 the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  6. 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 the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  7. Verify that billing is enabled for your Google Cloud project.

  8. 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 the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  9. 设置环境

    如需使用 Cloud Shell 设置您的环境,请按照以下步骤操作:

    1. 设置环境变量:

      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 集群的区域或可用区

      如果您未在单次会话中完成整个教程,或者您的环境变量因某种原因而未设置,请务必再次运行此命令以再次设置变量。

    2. 创建一个启用了集群自动扩缩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
      

    安装 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) 对象。

    1. 创建 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"
      
    2. 检查 KEDA 基于 ScaledObject 对象创建的 HorizontalPodAutoscaler (HPA) 对象:

      kubectl get hpa keda-hpa-keda-pubsub -n keda-pubsub -o yaml
      

      您可以在 Kubernetes 文档中详细了解自动扩缩。

    3. 等待 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
      

    触发扩容

    如需促使 Deployment 扩容,请执行以下操作:

    1. 将消息加入 Pub/Sub 主题的队列:

      for num in {1..20}
      do
        gcloud pubsub topics publish keda-echo --project=${PROJECT_ID} --message="Test"
      done
      
    2. 验证 Deployment 正在扩容:

      kubectl get deployments -n keda-pubsub
      

      在输出中,可以看到到“Ready”列显示一个副本:

      NAME          READY   UP-TO-DATE   AVAILABLE   AGE
      keda-pubsub   1/1     1            1           2d
      

    KEDA 在观察到队列不为空后会将 Deployment 扩容。

    将 LLM 工作负载缩减至零

    本部分介绍部署挂接 GPU 的 Ollama 服务器的大语言模型 (LLM) 工作负载。Ollama 支持运行 GemmaLlama 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 工作负载,请执行以下操作:

    1. 创建一个包含挂接了 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
      
    2. 添加官方 Ollama Helm 图表仓库,并更新本地 Helm 客户端的仓库:

      helm repo add ollama-helm https://otwld.github.io/ollama-helm/
      helm repo update
      
    3. 使用 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.yaml
      

      helm-values-ollama.yaml 配置指定要加载的 LLM 模型、GPU 要求以及 Ollama 服务器的 TCP 端口。

    配置缩减至零

    为了将 Ollama 工作负载配置为缩减至零,KEDA-HTTP 使用 HTTPScaledObject

    1. 创建 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
      
    2. 运行以下命令,验证 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
      
    3. 验证 Deployment 缩减至零个 Pod。

      等待 scaledownPeriod 字段中设置的时长,然后运行以下命令:

      kubectl get deployments -n ollama
      

      输出显示 KEDA 缩减了 Ollama 部署,并且没有 Pod 正在运行:

      NAME     READY   UP-TO-DATE   AVAILABLE   AGE
      ollama   0/0     0            0           2d
      

    触发扩容

    如需促使 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 账号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。

    1. 清理 Pub/Sub 订阅和主题:

      gcloud pubsub subscriptions delete keda-echo-read
      gcloud pubsub topics delete keda-echo
      
    2. 删除 GKE 集群:

      gcloud container clusters delete scale-to-zero --location=${LOCATION}
      

    后续步骤