Set Up Autoscaling for Inference Services with KEDA

介绍

在生产环境中部署机器学习模型面临诸多挑战,其中最关键的是确保推理服务能够高效且可靠地应对不同流量水平。AI 工作负载的不可预测性——流量可能会剧烈波动,且资源需求会根据输入序列长度、令牌生成长度或并发请求数等因素变化——往往使传统的自动扩缩容方法难以满足需求。

仅依赖 CPU 或内存指标可能导致资源过度配置和浪费,或者资源不足而影响用户体验。同样,GPU 利用率既可能表示高效使用,也可能表示资源饱和。因此,业界针对 LLM 的自动扩缩容最佳实践已转向更具针对性的工作负载指标。

本指南将介绍如何通过结合 KEDA(Kubernetes 事件驱动自动扩缩容)和 vLLM 导出的自定义应用指标,设置 KServe 的自动扩缩容。该组合使推理服务能够基于实际工作负载信号而非通用基础设施指标进行扩缩容。

INFO

KEDA 扩展了标准的 Kubernetes Horizontal Pod Autoscaler (HPA),允许应用根据多种事件源(包括 Prometheus 指标)从零扩展到 N 个实例,再缩回。它引入了一个开放且可扩展的框架,使 KServe 能够基于几乎任何与 AI 模型性能相关的信号进行扩缩容。

前提条件

  • 已安装 Alauda AI Operator 插件。
  • 已安装 KEDA Operator 插件。
  • 使用 vLLM Serving 运行时的 Standard 模式 InferenceService
  • 集群中已安装并可访问 Prometheus

授权 KServe 访问 KEDA 资源

在继续之前,应用以下 RBAC 资源,允许 kserve-controller-manager 管理 KEDA 对象(如 ScaledObjectTriggerAuthentication 等):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: kserve-keda-manager-role
rules:
- apiGroups:
  - keda.sh
  resources:
  - "*"
  verbs:
  - "*"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kserve-keda-manager-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: kserve-keda-manager-role
subjects:
- kind: ServiceAccount
  name: kserve-controller-manager
  namespace: kserve
[安装顺序]

如果 KEDA 是在 Alauda AI 之后安装的,应用上述 RBAC 后,请重启 kserve 命名空间中的 kserve-controller-manager Pod,以便其发现 KEDA CRD:

kubectl rollout restart deployment kserve-controller-manager -n kserve

操作步骤

停止正在运行的 InferenceService

在进行更改之前,先停止正在运行的 InferenceService,以避免现有 HPA 与新的 KEDA 管理的扩缩容器之间发生冲突。添加以下注解以停止服务:

kubectl annotate inferenceservice <your-isvc-name> -n <your-namespace> \
  serving.kserve.io/stop='true'
WARNING

如果正在运行的 InferenceService 已存在 HPA 资源,未先停止服务直接切换到 KEDA 会导致资源冲突。

创建 Prometheus TriggerAuthentication

KEDA 需要在与 InferenceService 相同的命名空间中创建 TriggerAuthentication 资源,以便认证 Prometheus。

Prometheus 凭据存储在 cpaas-system 命名空间的 kube-prometheus-alertmanager-basic-auth 平台 Secret 中。运行以下命令将其复制到你的命名空间:

kubectl create secret generic prom-basic-auth-secret \
  --namespace=<your-namespace> \
  --from-literal=username=$(kubectl get secret kube-prometheus-alertmanager-basic-auth \
    -n cpaas-system -o jsonpath='{.data.username}' | base64 -d) \
  --from-literal=password=$(kubectl get secret kube-prometheus-alertmanager-basic-auth \
    -n cpaas-system -o jsonpath='{.data.password}' | base64 -d)

然后创建引用该 Secret 的 TriggerAuthentication

apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: prom-basic-auth
  namespace: <your-namespace>
spec:
  secretTargetRef:
    - parameter: username
      name: prom-basic-auth-secret
      key: username
    - parameter: password
      name: prom-basic-auth-secret
      key: password
TIP

以下名称必须在所有资源中保持一致:

  • prom-basic-auth-secret — Secret 名称,必须与 TriggerAuthentication 中的 secretTargetRef.name 匹配。
  • prom-basic-authTriggerAuthentication 名称,必须与 InferenceService 规范中的 authenticationRef.authenticationRef.name 匹配。

配置 InferenceService 以使用 KEDA

服务停止后,更新 InferenceService 清单,添加 KEDA 自动扩缩容配置:

apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: <your-isvc-name>
  namespace: <your-namespace>
  annotations:
    serving.kserve.io/deploymentMode: Standard
    serving.kserve.io/autoscalerClass: keda
spec:
  predictor:
    maxReplicas: 2
    autoScaling:
      metrics:
        - type: External
          external:
            authenticationRef:
              authModes: basic
              authenticationRef:
                name: prom-basic-auth
            metric:
              backend: prometheus
              query: >
                sum(vllm:num_requests_running{isvc_name="<your-isvc-name>",namespace="<your-namespace>"})
              serverAddress: http://prometheus-operated.cpaas-system.svc.cluster.local:9090
            target:
              type: Value
              value: '1'
    # ... 你的 predictor 其他配置
  1. 禁用内置的 KServe HPA,将扩缩容委托给 KEDA。
  2. 设置最大副本数。为使服务能根据流量自动扩展,确保该值大于 1(例如 2)。
  3. 引用持有 Prometheus 认证凭据的 TriggerAuthentication 资源。将 prom-basic-auth 替换为你的实际名称。
  4. 返回当前负载的 PromQL 查询,结果为单个数值。替换 <your-model-name><your-namespace> 为实际值。
  5. Prometheus 实例的内部地址,例如 http://prometheus-operated.cpaas-system.svc.cluster.local:9090
  6. 每个副本的目标值。KEDA 通过计算 ceil(metricValue / value) 来确定期望副本数。

vLLM 指标用于自动扩缩容

vLLM 指标由平台自动采集。选择合适的指标是设置中最关键的部分——Prometheus 查询必须返回一个单一数值,准确反映模型当前负载。

常用的 vLLM 指标包括:

指标名称描述
vllm:num_requests_running模型当前正在处理的请求数
vllm:num_requests_waiting排队等待处理的请求数
vllm:gpu_cache_usage_percGPU KV 缓存当前使用百分比
vllm:e2e_request_latency_seconds_bucket端到端请求延迟直方图
vllm:time_per_output_token_seconds_bucket令牌间延迟(每输出令牌时间,TPOT)

使用 sum() 聚合函数确保查询返回所有 Pod 的单一值。例如,基于等待请求数进行扩缩容:

sum(vllm:num_requests_waiting{isvc_name="<your-isvc-name>", namespace="<your-namespace>"})

该查询汇总所有预测器 Pod 中的待处理请求数,为 KEDA 提供单一的聚合信号。

验证配置

应用更新后的 InferenceService 后,KServe 会自动为你创建 KEDA ScaledObject。验证配置是否生效:

# 查看 KServe 创建的 ScaledObject
kubectl get scaledobject -n <your-namespace>

# 查看 KEDA 管理的 HPA
kubectl get hpa -n <your-namespace>

# 实时监控副本数变化
kubectl get hpa -n <your-namespace> -w

HPA 输出将显示当前指标值、扩缩容阈值以及当前/期望副本数。随着推理流量增加,TARGETS 值会上升,副本数会自动扩展。