在Prefill-Decode(PD)分离的LLM推理架构中,Prefill和Decode阶段的资源需求差异巨大,传统的CPU/GPU利用率指标无法有效指导弹性伸缩。本方案以Dynamo框架为例,介绍如何利用KEDA,根据NATS消息队列的积压情况,为Prefill角色配置独立的弹性伸缩策略,实现资源按需分配,优化服务成本与性能。
前提条件
已部署阿里云Prometheus监控组件。具体操作,请参见使用阿里云Prometheus监控。
已部署ack-keda组件。具体操作,请参见部署ack-keda。
使用限制
本文的弹性伸缩方案仅针对PD分离架构中的 Prefill 角色。Decode角色的弹性伸缩需配置独立的策略(Decode建议用GPU显存利用率)。
本文示例基于Dynamo推理框架,如果您使用其他框架,相关配置(如NATS Stream名称、Consumer名称)需相应调整。
操作步骤
针对通过RoleBasedGroup(RBG)部署的PD分离推理服务,RBG提供了按角色独立扩缩容的能力。本文将以Dynamo PD分离框架为例,演示利用KEDA(Kubernetes Event-driven Autoscaling)为PD分离推理服务中的Prefill角色单独配置弹性伸缩策略。
在Dynamo的PD分离架构中,待处理的推理请求会作为消息推送至NATS消息队列的dynamo_prefill_queue
流中。Prefill实例作为消费者,根据自身处理能力从此队列拉取消息进行处理。因此,队列中待处理(Pending)的消息数量能有效反映Prefill角色的负载压力。KEDA提供的NATS JetStream Scaler可以监控此队列的积压消息数,并据此触发弹性伸缩,精准调控Prefill实例的数量。
在生产环境中应用此弹性伸缩策略前,强烈建议在测试环境中进行充分的压力测试,以确定最适合您业务负载的lagThreshold
(积压消息阈值)和pollingInterval
(轮询间隔)。不合理的配置可能导致扩容不及时影响服务性能,或过度扩容造成资源浪费。
步骤一:为RBG角色创建ScalingAdapter
为了让KEDA能够独立控制RBG中特定角色的副本数,需要在创建RBG时,为目标角色开启ScalingAdapter,会自动创建与其绑定的RoleBasedGroupScalingAdapter资源。
创建
rbg.yaml
文件,通过73-74行的scalingAdapter: enable: true
设置为所创建的RBG中prefill角色开启ScalingAdapter。执行以下命令创建资源。
kubectl apply -f ./rbg.yaml
在创建RBG时,系统会自动为开启了 ScalingAdapter 的角色创建一个名为 RoleBasedGroupScalingAdapter 的自定义资源,并将其与该角色进行绑定。通过 RoleBasedGroupScalingAdapter为绑定的角色提供 Scale 子资源的实现能力。
执行以下命令,查看为指定角色自动创建的
RoleBasedGroupScalingAdapter
。kubectl get rolebasedgroupscalingadapter
预期输出:
NAME PHASE REPLICAS dynamo-pd-prefill Bound 2
执行以下命令,确认
dynamo-pd-prefill
ScalingAdapter的状态。kubectl describe rolebasedgroupscalingadapter dynamo-pd-prefill
预期输出中,
Status.Phase
应为Bound
,表明该ScalingAdapter已成功与所创建的RBG中prefill角色完成绑定。Name: dynamo-pd-prefill Namespace: default Labels: <none> Annotations: <none> API Version: workloads.x-k8s.io/v1alpha1 Kind: RoleBasedGroupScalingAdapter Metadata: Creation Timestamp: 2025-07-25T06:10:37Z Generation: 2 Owner References: API Version: workloads.x-k8s.io/v1alpha1 Block Owner Deletion: true Kind: RoleBasedGroup Name: dynamo-pd UID: 5dd61668-79f3-4197-a5db-b778ce460270 Resource Version: 1157485 UID: edbb8373-2b9c-4ad1-8b6b-d5dfff71e769 Spec: Replicas: 2 Scale Target Ref: Name: dynamo-pd Role: prefill Status: Phase: Bound Replicas: 2 Selector: rolebasedgroup.workloads.x-k8s.io/name=dynamo-pd,rolebasedgroup.workloads.x-k8s.io/role=prefill Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulBound 25s RoleBasedGroupScalingAdapter Succeed to find scale target role [prefill] of rbg [dynamo-pd]
步骤二:创建KEDA ScaledObject监控消息队列
创建ScaledObject
资源,定义伸缩规则,将其关联到上一步创建的RoleBasedGroupScalingAdapter
。
创建
scaledobject.yaml
文件,内容如下。该配置指定了伸缩对象为dynamo-pd-prefill
ScalingAdapter,并设置了基于NATS消息队列积压数量的触发器。以下伸缩策略中的参数配置仅作为演示参考,实际配置请根据真实业务场景进行调整。
apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: dynamo-prefill-scaledobject spec: pollingInterval: 30 # For demo. 默认: 30 秒 minReplicaCount: 1 # For demo. 默认: 0 maxReplicaCount: 6 # For demo. 默认: 100 scaleTargetRef: apiVersion: workloads.x-k8s.io/v1alpha1 kind: RoleBasedGroupScalingAdapter name: dynamo-pd-prefill #指定伸缩对象为RoleBasedGroup中的Prefill角色 triggers: - type: nats-jetstream metadata: natsServerMonitoringEndpoint: "nats.default.svc.cluster.local:8222" #NATS service endpoint account: "$G" #当Nats未设置账户时的默认值 stream: "dynamo_prefill_queue" #Dynamo中PrefillQueue名称 consumer: "worker-group" #Dynamo中Consumer的持久化名称 lagThreshold: "5" #Nats指定队列中处于Pending的消息数量伸缩阈值 useHttps: "false" #是否使用Https协议
执行以下命令创建资源。
kubectl apply -f ./scaledobject.yaml
执行以下命令,确认KEDA
ScaledObject
资源状态。kubectl describe so dynamo-prefill-scaledobject
预期输出中,
Status.Conditions
的Ready
状态应为True
。同时,KEDA会自动创建一个HPA资源,其名称记录在
Status.HpaName
字段中,可执行以下命令查看。kubectl get hpa keda-hpa-dynamo-prefill-scaledobject
步骤三:(可选)压测并验证扩缩容效果
创建用于压测的服务实例,使用benchmark工具,对服务进行压测。
benchmark压测工具的详细介绍及使用方式,请参见vLLM Benchmark。
创建
benchmark.yaml
文件。执行命令创建压测的服务实例。
kubectl create -f benchmark.yaml
等待实例成功运行后,在实例中执行以下命令进行压测:
python3 $VLLM_ROOT_DIR/benchmarks/benchmark_serving.py \ --backend openai-chat \ --model /models/Qwen3-32B/ \ --served-model-name qwen \ --trust-remote-code \ --dataset-name random \ --random-input-len 1500 \ --random-output-len 100 \ --num-prompts 320 \ --max-concurrency 32 \ --host dynamo-service \ --port 8000 \ --endpoint /v1/chat/completions
在压测期间,新开一个终端,执行以下命令观察HPA的扩缩容事件。
kubectl describe hpa keda-hpa-dynamo-prefill-scaledobject
预期输出中,可以看到
Events
字段记录了SuccessfulRescale
事件,表明KEDA已根据NATS队列的积压情况成功触发了扩容。Name: keda-hpa-dynamo-prefill-scaledobject Namespace: default Reference: RoleBasedGroupScalingAdapter/dynamo-pd-prefill Min replicas: 1 Max replicas: 6 RoleBasedGroupScalingAdapter pods: 6 current / 6 desired Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulRescale 2m1s horizontal-pod-autoscaler New size: 4; reason: external metric s0-nats-jetstream-dynamo_prefill_queue(&LabelSelector{MatchLabels:map[string]string{scaledobject.keda.sh/name: dynamo-prefill-scaledobject,},MatchExpressions:[]LabelSelectorRequirement{},}) above target Normal SuccessfulRescale 106s horizontal-pod-autoscaler New size: 6; reason: external metric s0-nats-jetstream-dynamo_prefill_queue(&LabelSelector{MatchLabels:map[string]string{scaledobject.keda.sh/name: dynamo-prefill-scaledobject,},MatchExpressions:[]LabelSelectorRequirement{},}) above target
同时,可以观察
RoleBasedGroupScalingAdapter
的副本数变化。kubectl describe rolebasedgroupscalingadapter dynamo-pd-prefill
预期输出中,
Spec.Replicas
和Status.Replicas
的值会从初始值增加到扩容后的值(例如6)。Name: dynamo-pd-prefill Namespace: default API Version: workloads.x-k8s.io/v1alpha1 Kind: RoleBasedGroupScalingAdapter Metadata: Owner References: API Version: workloads.x-k8s.io/v1alpha1 Block Owner Deletion: true Kind: RoleBasedGroup Name: dynamo-pd Spec: Replicas: 6 Scale Target Ref: Name: dynamo-pd Role: prefill Status: Last Scale Time: 2025-08-04T02:08:10Z Phase: Bound Replicas: 6 Selector: rolebasedgroup.workloads.x-k8s.io/name=dynamo-pd,rolebasedgroup.workloads.x-k8s.io/role=prefill Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulBound 6m9s RoleBasedGroupScalingAdapter Succeed to find scale target role [prefill] of rbg [dynamo-pd] Normal SuccessfulScale 4m40s RoleBasedGroupScalingAdapter Succeed to scale target role [prefill] of rbg [dynamo-pd] from 1 to 4 replicas Normal SuccessfulScale 4m25s RoleBasedGroupScalingAdapter Succeed to scale target role [prefill] of rbg [dynamo-pd] from 4 to 6 replicas