Prefill-Decode (PD) デカップリングを採用した大規模言語モデル (LLM) の推論アーキテクチャでは、Prefill ステージと Decode ステージのリソース要件が大きく異なります。CPU や GPU の使用率といった従来のメトリックでは、効果的に弾性スケーリングをガイドすることはできません。このトピックでは、Dynamo フレームワークを例に、KEDA を使用して、NATS メッセージキューのバックログに基づいて Prefill ロールに独立した弾性スケーリングポリシーを設定する方法を説明します。この方法により、オンデマンドのリソース割り当てを実現し、サービスのコストとパフォーマンスを最適化します。
前提条件
Dynamo PD デカップリング推論サービスをデプロイしていること。
Alibaba Cloud Prometheus モニタリングコンポーネントをデプロイ済みであること。詳細については、「Alibaba Cloud Prometheus モニタリングへの接続と設定」をご参照ください。
ack-keda コンポーネントをデプロイしていること。詳細については、「ack-keda のデプロイ」をご参照ください。
制限事項
このトピックの弾性スケーリングソリューションは、PD デカップリングアーキテクチャの Prefill ロールにのみ適用されます。Decode ロールには、別途スケーリングポリシーが必要です。Decode ロールには、GPU メモリ使用率に基づくスケーリングを推奨します。
この例は Dynamo 推論フレームワークに基づいています。異なるフレームワークを使用する場合、NATS ストリーム名やコンシューマー名など、関連する構成を調整する必要があります。
操作手順
RoleBasedGroup (RBG) を使用してデプロイされた PD デカップリング推論サービスでは、RBG が各ロールを個別にスケーリングする機能を提供します。このトピックでは、Dynamo PD デカップリングフレームワークを例に、KEDA (Kubernetes Event-driven Autoscaling) を使用して Prefill ロールに個別の弾性スケーリングポリシーを設定する方法を説明します。
Dynamo PD デカップリングアーキテクチャでは、保留中の推論リクエストは、メッセージとして NATS メッセージキューの dynamo_prefill_queue ストリームにプッシュされます。Prefill インスタンスはコンシューマーとして機能し、その処理能力に基づいてこのキューからメッセージをプルして処理します。したがって、キュー内の保留メッセージ数は、Prefill ロールの負荷を効果的に反映します。KEDA が提供する NATS JetStream Scaler は、このキューのバックログメッセージ数を監視し、弾性スケーリングをトリガーして Prefill インスタンスの数を正確に制御できます。
この弾性スケーリングポリシーを本番環境に適用する前に、ステージング環境で十分なストレステストを実施することを強く推奨します。これにより、ビジネスワークロードに最適なlagThreshold(バックログメッセージのしきい値) とpollingInterval(ポーリング間隔) を決定できます。不適切な構成は、スケールアウトの遅延によるサービスパフォーマンスの低下や、過剰なスケールアウトによるリソースの浪費につながる可能性があります。
ステップ 1:RBG ロールの ScalingAdapter の作成
KEDA が RBG 内の特定のロールのレプリカ数を個別に制御できるようにするには、RBG の作成時にターゲットロールの ScalingAdapter を有効にする必要があります。この操作により、RoleBasedGroupScalingAdapter リソースが自動的に作成され、そのロールにバインドされます。
rbg.yamlファイルを作成します。73 行目から 74 行目で、scalingAdapter: enable: trueを設定して、RBG の prefill ロールに対して ScalingAdapter を有効にします。次のコマンドを実行してリソースを作成します。
kubectl apply -f rbg.yamlRBG を作成すると、ScalingAdapter が有効になっているロールに対して、RoleBasedGroupScalingAdapter という名前のカスタムリソース定義 (CRD) が自動的に作成されます。その後、システムはこの CRD をロールにバインドします。RoleBasedGroupScalingAdapter は、バインドされたロールに対して scale サブリソースの実装を提供します。
次のコマンドを実行して、指定されたロールに対して自動的に作成された
RoleBasedGroupScalingAdapterを表示します。kubectl get rolebasedgroupscalingadapter想定される出力:
NAME PHASE REPLICAS dynamo-pd-prefill Bound 2次のコマンドを実行して、
dynamo-pd-prefillScalingAdapter のステータスを確認します。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]
ステップ 2:メッセージキューを監視する KEDA ScaledObject の作成
ScaledObject リソースを作成し、スケーリングルールを定義して、前のステップで作成した RoleBasedGroupScalingAdapter に関連付けます。
次の内容で
scaledobject.yamlファイルを作成します。この構成では、スケーリングターゲットとしてdynamo-pd-prefillScalingAdapter を指定し、NATS メッセージキューのバックログに基づくトリガーを設定します。以下のスケーリングポリシーのパラメーター設定はデモ用です。実際のビジネスシナリオに基づいて構成を調整する必要があります。
apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: dynamo-prefill-scaledobject spec: pollingInterval: 30 # デモ用。デフォルト:30 秒 minReplicaCount: 1 # デモ用。デフォルト:0 maxReplicaCount: 6 # デモ用。デフォルト: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 サービスエンドポイント account: "$G" # NATS アカウントが設定されていない場合のデフォルト値 stream: "dynamo_prefill_queue" # Dynamo の PrefillQueue の名前 consumer: "worker-group" # Dynamo のコンシューマーの永続化名 lagThreshold: "5" # 指定された NATS キュー内の保留メッセージ数に対するスケーリングのしきい値 useHttps: "false" # HTTPS プロトコルを使用するかどうかを指定次のコマンドを実行してリソースを作成します。
kubectl apply -f scaledobject.yaml次のコマンドを実行して、KEDA
ScaledObjectリソースのステータスを確認します。kubectl describe so dynamo-prefill-scaledobject想定される出力では、
Status.ConditionsのReadyステータスがTrueになっています。KEDA は Horizontal Pod Autoscaler (HPA) リソースも自動的に作成します。HPA リソースの名前は
Status.HpaNameフィールドに記録されます。次のコマンドを実行して HPA リソースを表示できます。kubectl get hpa keda-hpa-dynamo-prefill-scaledobject
ステップ 3:(オプション) ストレステストとスケーリング効果の検証
ストレステスト用のサービスインスタンスを作成し、ベンチマークツールを使用してサービスをテストします。
ベンチマークツールとその使用方法の詳細については、「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 キューのバックログに基づいてスケールアウトを正常にトリガーしたことを示しています。名前: keda-hpa-dynamo-prefill-scaledobject 名前空間: デフォルト 参照: RoleBasedGroupScalingAdapter/dynamo-pd-prefill 最小レプリカ数: 1 最大レプリカ数: 6 RoleBasedGroupScalingAdapter pods: 6 現在 / 6 望ましい イベント: タイプ 理由 経過時間 ソース メッセージ ---- ------ ---- ---- ------- 正常 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 正常 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