使用 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.

    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.

    Enable the APIs

  6. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    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.

    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}
        

      后续步骤