使用 GPU 指标自动扩缩 vLLM 模型部署

本教程介绍如何使用 Cloud Run 外部指标自动扩缩 (CREMA),根据自定义 GPU 指标,使用 vLLM 自动扩缩提供 LLM 服务的 Cloud Run 服务。

虽然 Cloud Run 默认使用 CPU 利用率和并发性进行自动扩缩,但 GPU 密集型推理工作负载通常需要根据队列指标(例如正在运行的请求数或 KV 缓存利用率)进行自动扩缩。CREMA 将基于 Kubernetes 的事件驱动型自动伸缩 (KEDA) 与 Cloud Run 集成,以实现由 Prometheus 指标驱动的动态伸缩。vLLM 会公开 Prometheus 指标并将其发送到 Cloud Monitoring。

目标

在此教程中,您将学习以下操作:

费用

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

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

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

准备工作

  1. 登录您的 Google Cloud 账号。如果您是 Google Cloud新手,请 创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
  2. 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

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

  4. 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

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

  6. 启用 Cloud Run API、Parameter Manager API、Artifact Registry API、Cloud Build API、Secret Manager API 和 Cloud Monitoring API(如果尚未启用)。

    启用 API 所需的角色

    如需启用 API,您需要拥有 serviceusage.services.enable 权限。如果您创建了项目,则可能已经通过 Owner 角色 (roles/owner) 拥有此权限。否则,您可以通过 Service Usage Admin 角色 (roles/serviceusage.serviceUsageAdmin) 获取此权限。 了解如何授予角色。

    启用 API

  7. 安装并初始化 gcloud CLI。
  8. 设置本教程中使用的环境变量:
    export PROJECT_ID=PROJECT_ID
    export REGION=us-central1
    export VLLM_SERVICE_NAME=vllm-service
    export MODEL_NAME=gemma-2-2b-it
    export REPO_NAME=vllm-repo
    export BUCKET_NAME=my-vllm-models-${PROJECT_ID}
    export CREMA_SERVICE_NAME=crema-service
    将 PROJECT_ID 替换为您的 Google Cloud 项目 ID。
  9. 设置项目配置:
    gcloud config set project $PROJECT_ID
  10. 如果您还没有账号,请在 Hugging Face 上创建一个。然后,在 Hugging Face 网站上创建读取令牌。 Hugging Face 只会显示一次令牌。请将其保存在安全的位置,因为您将无法再次查看。
  11. 前往 Hugging Face 上的 gemma-2-2b-it 模型页面,并接受该模型的协议条款。

所需的角色

如需获得完成本教程所需的权限,请让您的管理员为您授予项目的以下 IAM 角色:

如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限。

您也可以通过自定义角色或其他预定义角色来获取所需的权限。

下载模型权重并将其上传到 Cloud Storage

从 Hugging Face 下载模型权重,然后将其转移到 Cloud Storage 存储桶,以便用于模型部署:

  1. 安装 Hugging Face CLI:

    pip install -U "huggingface_hub[cli]"
    
  2. 使用 Hugging Face CLI 在本地下载模型权重:

    export HF_TOKEN="HF_TOKEN"
    export LOCAL_DIR="/tmp/$MODEL_NAME"
    HF_HOME=/tmp/huggingface python -m huggingface_hub.cli.hf download google/$MODEL_NAME --token $HF_TOKEN --local-dir=$LOCAL_DIR
    

    将 HF_TOKEN 替换为您的 Hugging Face 用户访问令牌。令牌应以 hf_ 开头,后跟 35 个随机字母数字字符(例如 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)。

  3. 创建 Cloud Storage 存储桶并复制下载的权重:

    gcloud storage buckets create gs://$BUCKET_NAME \
        --project=$PROJECT_ID \
        --location=$REGION \
        --uniform-bucket-level-access
    
    gcloud storage cp -r $LOCAL_DIR gs://$BUCKET_NAME/
    

将 vLLM 容器映像推送到 Artifact Registry

拉取模型部署容器映像,并将其推送到 Artifact Registry 中的制品库:

  1. 在 Artifact Registry 中创建 Docker 代码库:

    gcloud artifacts repositories create $REPO_NAME \
        --repository-format=docker \
        --location=$REGION \
        --description="vLLM Docker Images"
    
  2. 向注册表验证本地 Docker 守护程序的身份:

    gcloud auth configure-docker ${REGION}-docker.pkg.dev
    
  3. 拉取 vLLM 映像,为其添加标记,然后将其推送到 Artifact Registry:

    docker pull docker.io/vllm/vllm-openai:latest
    docker tag docker.io/vllm/vllm-openai:latest ${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/vllm-openai:latest
    docker push ${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/vllm-openai:latest
    

部署 CREMA 自动扩缩器服务

在部署自动扩缩器服务之前,请为 CREMA 配置服务账号角色和参数清单。

创建自定义服务账号

创建一个具有使用已配置资源所需的最低权限的自定义服务账号。 此服务账号充当自动扩缩器的身份。运行以下命令以创建 CREMA 服务账号:

export CREMA_SA="crema-autoscaler@${PROJECT_ID}.iam.gserviceaccount.com"

gcloud iam service-accounts create crema-autoscaler \
    --description="Service account for Cloud Run CREMA to read metrics and scale workloads" \
    --display-name="CREMA Autoscaler System"

向自定义服务账号授予其他权限

如需扩缩服务,请为自定义服务账号授予以下权限:

  1. 向您的 CREMA 服务账号授予从 Parameter Manager 读取数据的权限:

    gcloud projects add-iam-policy-binding $PROJECT_ID \
        --member="serviceAccount:$CREMA_SA" \
        --role="roles/parametermanager.parameterViewer"
    
  2. 向您的 CREMA 服务账号授予扩缩服务的权限:

    gcloud projects add-iam-policy-binding $PROJECT_ID \
        --member="serviceAccount:$CREMA_SA" \
        --role="roles/run.developer"
    
  3. 向您的 CREMA 服务账号授予服务账号用户角色:

    gcloud projects add-iam-policy-binding $PROJECT_ID \
        --member="serviceAccount:$CREMA_SA" \
        --role="roles/iam.serviceAccountUser"
    
  4. 向您的 CREMA 服务账号授予查看指标的权限:

    gcloud projects add-iam-policy-binding $PROJECT_ID \
        --member="serviceAccount:$CREMA_SA" \
        --role="roles/monitoring.viewer"
    

创建并注册 CREMA 配置

在 CREMA 配置清单中定义伸缩阈值和规则,并在 Parameter Manager 中注册该清单:

  1. 将以下配置保存为 my-crema-config.yaml。此配置会在运行的请求数 (vllm:num_requests_running) 超过 2 时触发伸缩:

    apiVersion: crema/v1
    kind: CremaConfig
    spec:
      pollingInterval: 15
      triggerAuthentications:
        - metadata:
            name: adc-trigger-auth
          spec:
            podIdentity:
              provider: gcp
      scaledObjects:
        - spec:
            scaleTargetRef:
              name: projects/PROJECT_ID/locations/us-central1/services/vllm-service
            minReplicaCount: 1
            maxReplicaCount: 5
            triggers:
              - type: prometheus
                authenticationRef:
                  name: adc-trigger-auth
                metadata:
                  serverAddress: https://monitoring.googleapis.com/v1/projects/PROJECT_ID/location/global/prometheus
                  metric: vllm:num_requests_running
                  query: sum(vllm:num_requests_running)
                  threshold: '2'
    
  2. 在 Parameter Manager 中注册配置文件:

    gcloud parametermanager parameters create crema-config \
        --location=global \
        --parameter-format=YAML
    
    gcloud parametermanager parameters versions create 1 \
        --location=global \
        --parameter=crema-config \
        --payload-data-from-file=my-crema-config.yaml
    

部署 CREMA 服务

将 CREMA 映像部署为 Cloud Run 上的内部后台服务:

gcloud run deploy $CREMA_SERVICE_NAME \
    --image=us-central1-docker.pkg.dev/cloud-run-oss-images/crema-v1/autoscaler:1.0 \
    --region=$REGION \
    --service-account="$CREMA_SA" \
    --no-allow-unauthenticated \
    --no-cpu-throttling \
    --cpu=1 \
    --memory=1Gi \
    --min-instances=1 \
    --max-instances=1 \
    --ingress=internal \
    --base-image=us-central1-docker.pkg.dev/serverless-runtimes/google-24/runtimes/java25 \
    --set-env-vars="CREMA_CONFIG=projects/$PROJECT_ID/locations/global/parameters/crema-config/versions/1,OUTPUT_SCALER_METRICS=True"

配置 vLLM 服务权限

向默认的 Compute Engine 服务账号授予从 Cloud Storage 导出指标和读取模型权重的权限:

  1. 检索您的项目编号:

    export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
    
  2. 向您的服务账号授予写入指标的权限:

    gcloud projects add-iam-policy-binding $PROJECT_ID \
        --member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
        --role="roles/monitoring.metricWriter"
    
  3. 向您的服务账号授予从 Cloud Storage 读取模型权重的权限:

    gcloud projects add-iam-policy-binding $PROJECT_ID \
        --member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
        --role="roles/storage.objectViewer"
    

部署带有 OpenTelemetry 边车的 vLLM 服务

将主 vLLM 服务容器部署到 Cloud Run,并从 Cloud Storage 装载模型权重。由于在 Cloud Run 上部署带边车容器的多容器服务需要声明性 YAML 服务规范,因此您需要配置主 vLLM 引擎以及 OpenTelemetry 边车容器收集器,以抓取和导出 vLLM 指标:

  1. 将以下多容器部署规范保存为 vllm-service.yaml:

    apiVersion: serving.knative.dev/v1
    kind: Service
    metadata:
      name: vllm-service
      labels:
        cloud.googleapis.com/location: us-central1
      annotations:
        run.googleapis.com/scalingMode: manual
        run.googleapis.com/manualInstanceCount: "1"
    spec:
      template:
        metadata:
          annotations:
            run.googleapis.com/execution-environment: gen2
            run.googleapis.com/cpu-throttling: "false"
            run.googleapis.com/gpu-zonal-redundancy-disabled: "true"
            autoscaling.knative.dev/minScale: "1"
        spec:
          containerConcurrency: 80
          nodeSelector:
            run.googleapis.com/accelerator: nvidia-l4
          volumes:
            - name: gcs-volume
              csi:
                driver: gcsfuse.run.googleapis.com
                volumeAttributes:
                  bucketName: my-vllm-models-PROJECT_ID
          containers:
            # Primary container: vLLM serving engine
            - name: vllm-container
              image: us-central1-docker.pkg.dev/PROJECT_ID/vllm-repo/vllm-openai:latest
              ports:
                - containerPort: 8080
              resources:
                limits:
                  cpu: "4"
                  memory: 16Gi
                  nvidia.com/gpu: "1"
              args:
                - "--model"
                - "/gcs/gemma-2-2b-it"
                - "--port"
                - "8080"
                - "--max-model-len"
                - "2048"
                - "--chat-template"
                - "{% for msg in messages %}{{ msg['content'] }}{% endfor %}"
              volumeMounts:
                - name: gcs-volume
                  mountPath: /gcs
              startupProbe:
                httpGet:
                  path: /health
                  port: 8080
                periodSeconds: 10
                failureThreshold: 24
    
            # Sidecar container: OpenTelemetry Collector
            - name: otel-collector
              image: otel/opentelemetry-collector-contrib:latest
              resources:
                limits:
                  cpu: "1"
                  memory: 1Gi
              args:
                - |
                  --config=yaml:
                  receivers:
                    prometheus:
                      config:
                        scrape_configs:
                          - job_name: 'vllm'
                            scrape_interval: 10s
                            metrics_path: '/metrics'
                            static_configs:
                              - targets: ['localhost:8080']
                  processors:
                    resourcedetection:
                      detectors: [gcp]
                      timeout: 2s
                    transform:
                      metric_statements:
                        - context: datapoint
                          statements:
                            - set(attributes["exported_location"], attributes["location"])
                            - delete_key(attributes, "location")
                            - set(attributes["exported_cluster"], attributes["cluster"])
                            - delete_key(attributes, "cluster")
                            - set(attributes["exported_namespace"], attributes["namespace"])
                            - delete_key(attributes, "namespace")
                            - set(attributes["exported_job"], attributes["job"])
                            - delete_key(attributes, "job")
                            - set(attributes["exported_instance"], attributes["instance"])
                            - delete_key(attributes, "instance")
                  exporters:
                    googlemanagedprometheus:
                  service:
                    pipelines:
                      metrics:
                        receivers: [prometheus]
                        processors: [resourcedetection, transform]
                        exporters: [googlemanagedprometheus]
    
  2. 将现有服务配置替换为多容器清单:

    gcloud run services replace vllm-service.yaml
    

检查 CREMA 服务日志

  1. 在 Google Cloud 控制台中,前往 Cloud Run 页面。
  2. 选择您的 crema-service。
  3. 点击日志标签页,并验证指标轮询周期是否处于活跃状态:

    [INFO] [METRIC-PROVIDER] Starting metric collection cycle
    [INFO] [METRIC-PROVIDER] Successfully fetched scaled object metrics ...
    [INFO] [METRIC-PROVIDER] Sending scale request ...
    [INFO] [SCALER] Received ScaleRequest ...
    [INFO] [SCALER] Current instances ...
    [INFO] [SCALER] Recommended instances ...
    

运行负载测试

如需测试自动扩缩,请运行负载测试脚本以向 vLLM 服务发送并发请求:

  1. 在工作目录中,创建一个名为 load-test.sh 的文件,并添加以下代码:

    #!/bin/bash
    
    export SERVICE_URL=$(gcloud run services describe $VLLM_SERVICE_NAME --region $REGION --format='value(status.url)')
    
    echo "Launching 5 parallel heavy requests to trigger autoscaling..."
    
    for i in {1..5}; do
        curl -s -X POST "${SERVICE_URL}/v1/chat/completions" \
            -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
            -H "Content-Type: application/json" \
            -d "{
                \"model\": \"/gcs/${MODEL_NAME}\",
                \"messages\": [{\"role\": \"user\", \"content\": \"Write an exceptionally long, detailed, and exhaustive essay about the entire history of the universe from the Big Bang to the modern day.\"}]
            }" > /dev/null &
    done
    
    echo "All 5 requests dispatched. Waiting for requests to complete..."
    wait
    echo "Done."
    
  2. 使脚本可执行并运行负载测试:

    chmod +x load-test.sh
    ./load-test.sh
    
  3. 重新检查 crema-service 日志和 Cloud Run 指标信息中心,验证建议的实例数量是否会随着排队的请求而增加。

在 Cloud Monitoring 中探索 vLLM 指标

运行负载测试后,探索流量如何在 Cloud Monitoring 中影响模型部署指标:

  1. 在 Google Cloud 控制台中,前往 Cloud Monitoring 中的 Metrics Explorer 页面。

    转到 Metrics Explorer

  2. 点击选择指标。

  3. 依次展开 Prometheus Target > Vllm,然后选择以 /gauge 结尾的任何可用指标。例如,选择 prometheus/vllm:num_requests_running/gauge 可查看在负载测试期间的有效请求数。

如 vLLM 生产指标文档中所述,导出到 Cloud Monitoring 的其他 vLLM 指标包括:

  • prometheus/vllm:num_requests_waiting/gauge:队列中等待 vLLM 引擎处理的请求数量。
  • prometheus/vllm:num_requests_running/gauge:在模型批次中执行的请求数。
  • prometheus/vllm:gpu_cache_usage_perc/gauge:已使用的 GPU KV 缓存内存百分比。
  • prometheus/vllm:num_requests_swapped/gauge:由于内存压力,KV 缓存被交换到主机 CPU 内存的请求数量。

虽然本教程是根据 vllm:num_requests_running 进行扩缩,但您可以在 CREMA 配置中使用任何这些 vLLM 指标,以根据队列大小、KV 缓存利用率或请求交换为工作负载自定义自动扩缩规则。

与将所有请求同等对待的标准 HTTP 请求并发指标不同,vLLM 的内部指标考虑了不同提示长度的动态 GPU 内存占用空间。基于 vllm:num_requests_running 的扩缩可帮助您根据实际 GPU 负载主动进行扩缩。这样一来,在服务器被迫将请求排入 vllm:num_requests_waiting 之前,系统会保持一个有效的容量缓冲区,从而保护用户免受严重的首 token 延迟时间 (TTFT) 峰值的影响。

清理

为避免您的 Google Cloud 账号产生额外费用,请删除您在本教程中部署的所有资源。

删除项目

如果您为本教程创建了一个新项目,请删除项目。 如果您使用的是某个现有项目,并且需要保留此项目但不保留在本教程中所做的更改,请删除为本教程创建的资源。

为了避免产生费用,最简单的方法是删除您为本教程创建的项目。

要删除项目,请执行以下操作:

  1. 在 Google Cloud 控制台中,前往管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除。
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

删除教程资源

  1. 删除您在本教程中部署的 Cloud Run 服务。Cloud Run 服务在收到请求之前不会产生费用。

    如需删除 Cloud Run 服务,请运行以下命令:

    gcloud run services delete SERVICE-NAME

    将 SERVICE-NAME 替换为服务的名称。

    您还可以通过Google Cloud 控制台删除 Cloud Run 服务。

  2. 移除您在教程设置过程中添加的 gcloud 默认区域配置:

     gcloud config unset run/region
    
  3. 移除项目配置:

     gcloud config unset project
    
  4. 删除分配给 Parameter Manager 的 CREMA 配置:

    gcloud parametermanager parameters delete crema-config \
        --location=global \
        --quiet
    
  5. 删除为 CREMA 创建的自定义服务账号:

    gcloud iam service-accounts delete $CREMA_SA \
        --quiet
    
  6. 删除包含模型的 Cloud Storage 存储桶:

    gcloud storage rm --recursive gs://$BUCKET_NAME
    
  7. 删除在本教程中创建的其他 Google Cloud 资源:

后续步骤