全部产品
Search
文档中心

Container Service for Kubernetes:Konfigurasikan kebijakan skalabilitas elastis untuk layanan inferensi terurai PD

更新时间:Dec 27, 2025

Dalam arsitektur inferensi Large Language Model (LLM) dengan penguraian keterkaitan Prefill-Decode (PD), kebutuhan sumber daya pada tahap Prefill dan Decode sangat berbeda. Metrik tradisional seperti pemanfaatan CPU atau GPU tidak mampu memandu skalabilitas elastis secara efektif. Topik ini menggunakan framework Dynamo sebagai contoh untuk menunjukkan cara menggunakan KEDA dalam mengonfigurasi kebijakan penskalaan elastis independen bagi peran Prefill berdasarkan backlog antrian pesan NATS. Pendekatan ini memungkinkan alokasi sumber daya sesuai permintaan serta mengoptimalkan biaya dan performa layanan.

Prasyarat

Batasan

  • Solusi skalabilitas elastis dalam topik ini hanya berlaku untuk peran Prefill dalam arsitektur terurai PD. Peran Decode memerlukan kebijakan penskalaan terpisah. Kami merekomendasikan penggunaan pemanfaatan memori GPU untuk peran Decode.

  • Contoh ini didasarkan pada framework inferensi Dynamo. Jika Anda menggunakan framework yang berbeda, Anda harus menyesuaikan konfigurasi terkait, seperti nama NATS Stream dan nama Consumer.

Prosedur

Untuk layanan inferensi terurai PD yang di-deploy menggunakan RoleBasedGroup (RBG), RBG menyediakan kemampuan untuk menskalakan setiap peran secara independen. Topik ini menggunakan framework terurai PD Dynamo sebagai contoh untuk menunjukkan cara menggunakan KEDA (Kubernetes Event-driven Autoscaling) guna mengonfigurasi kebijakan penskalaan elastis terpisah untuk peran Prefill.

Dalam arsitektur terurai PD Dynamo, permintaan inferensi yang tertunda didorong sebagai pesan ke aliran dynamo_prefill_queue dalam antrian pesan NATS. Instans Prefill bertindak sebagai konsumen dan menarik pesan dari antrian tersebut untuk diproses berdasarkan kapasitasnya. Dengan demikian, jumlah pesan tertunda dalam antrian tersebut secara akurat mencerminkan beban pada peran Prefill. NATS JetStream Scaler yang disediakan oleh KEDA dapat memantau jumlah backlog pesan dalam antrian ini dan memicu penskalaan elastis guna mengontrol jumlah instans Prefill secara tepat.

Sebelum menerapkan kebijakan penskalaan elastis ini di lingkungan produksi, kami sangat menyarankan Anda melakukan uji stres menyeluruh di lingkungan staging. Hal ini membantu Anda menentukan nilai optimal untuk lagThreshold (ambang batas pesan backlog) dan pollingInterval (interval polling) sesuai beban kerja bisnis Anda. Konfigurasi yang tidak tepat dapat menyebabkan skala keluar yang tertunda sehingga memengaruhi performa layanan, atau skala keluar berlebihan yang menghamburkan sumber daya.

Langkah 1: Buat ScalingAdapter untuk peran RBG

Agar KEDA dapat mengontrol jumlah replika secara independen untuk peran tertentu dalam RBG, Anda harus mengaktifkan ScalingAdapter untuk peran target saat membuat RBG. Tindakan ini secara otomatis membuat resource RoleBasedGroupScalingAdapter dan mengikatnya ke peran tersebut.

  1. Buat file rbg.yaml. Pada baris 73 hingga 74, atur scalingAdapter: enable: true untuk mengaktifkan ScalingAdapter pada peran prefill dalam RBG.

    Klik untuk melihat contoh kode YAML.

    apiVersion: workloads.x-k8s.io/v1alpha1
    kind: RoleBasedGroup
    metadata:
      name: dynamo-pd
      namespace: default
    spec:
      roles:
        - name: processor
          replicas: 1
          template:
            spec:
              containers:
                - command:
                    - sh
                    - -c
                    - cd /workspace/examples/llm; dynamo serve graphs.pd_disagg:Frontend -f ./configs/qwen3.yaml
                  env:
                    - name: DYNAMO_NAME
                      value: dynamo
                    - name: DYNAMO_NAMESPACE
                      value: default
                    - name: ETCD_ENDPOINTS
                      value: http://etcd:2379
                    - name: NATS_SERVER
                      value: nats://nats:4222
                    - name: DYNAMO_RP_TIMEOUT
                      value: "60"
                  image: # The address of the Dynamo Runtime container image you built
                  name: processor
                  ports:
                    - containerPort: 8000
                      name: health
                      protocol: TCP
                    - containerPort: 9345
                      name: request
                      protocol: TCP
                    - containerPort: 443
                      name: api
                      protocol: TCP
                    - containerPort: 9347
                      name: metrics
                      protocol: TCP
                  readinessProbe:
                    initialDelaySeconds: 30
                    periodSeconds: 30
                    tcpSocket:
                      port: 8000
                  resources:
                    limits:
                      cpu: "8"
                      memory: 12Gi
                    requests:
                      cpu: "8"
                      memory: 12Gi
                  volumeMounts:
                    - mountPath: /models/Qwen3-32B/
                      name: model
                    - mountPath: /workspace/examples/llm/configs/qwen3.yaml
                      name: dynamo-configs
                      subPath: qwen3.yaml
                    - mountPath: /workspace/examples/llm/graphs/pd_disagg.py
                      name: dynamo-configs
                      subPath: pd_disagg.py
              volumes:
                - name: model
                  persistentVolumeClaim:
                    claimName: llm-model
                - configMap:
                    name: dynamo-configs
                  name: dynamo-configs
        - name: prefill
          replicas: 2
          scalingAdapter:
            enable: true
          template:
            spec:
              containers:
                - command:
                    - sh
                    - -c
                    - cd /workspace/examples/llm; dynamo serve components.prefill_worker:PrefillWorker -f ./configs/qwen3.yaml
                  env:
                    - name: DYNAMO_NAME
                      value: dynamo
                    - name: DYNAMO_NAMESPACE
                      value: default
                    - name: ETCD_ENDPOINTS
                      value: http://etcd:2379
                    - name: NATS_SERVER
                      value: nats://nats:4222
                    - name: DYNAMO_RP_TIMEOUT
                      value: "60"
                  image: # The address of the Dynamo Runtime container image you built
                  name: prefill-worker
                  resources:
                    limits:
                      cpu: "12"
                      memory: 50Gi
                      nvidia.com/gpu: "2"
                    requests:
                      cpu: "12"
                      memory: 50Gi
                      nvidia.com/gpu: "2"
                  volumeMounts:
                    - mountPath: /models/Qwen3-32B/
                      name: model
                    - mountPath: /workspace/examples/llm/configs/qwen3.yaml
                      name: dynamo-configs
                      subPath: qwen3.yaml
              volumes:
                - name: model
                  persistentVolumeClaim:
                    claimName: llm-model
                - configMap:
                    name: dynamo-configs
                  name: dynamo-configs
        - name: decoder
          replicas: 1
          template:
            spec:
              containers:
                - command:
                    - sh
                    - -c
                    - cd /workspace/examples/llm; dynamo serve components.worker:VllmWorker -f ./configs/qwen3.yaml --service-name VllmWorker
                  env:
                    - name: DYNAMO_NAME
                      value: dynamo
                    - name: DYNAMO_NAMESPACE
                      value: default
                    - name: ETCD_ENDPOINTS
                      value: http://etcd:2379
                    - name: NATS_SERVER
                      value: nats://nats:4222
                    - name: DYNAMO_RP_TIMEOUT
                      value: "60"
                  image: # The address of the Dynamo Runtime container image you built
                  name: vllm-worker
                  resources:
                    limits:
                      cpu: "12"
                      memory: 50Gi
                      nvidia.com/gpu: "2"
                    requests:
                      cpu: "12"
                      memory: 50Gi
                      nvidia.com/gpu: "2"
                  volumeMounts:
                    - mountPath: /models/Qwen3-32B/
                      name: model
                    - mountPath: /workspace/examples/llm/configs/qwen3.yaml
                      name: dynamo-configs
                      subPath: qwen3.yaml
              volumes:
                - name: model
                  persistentVolumeClaim:
                    claimName: llm-model
                - configMap:
                    name: dynamo-configs
                  name: dynamo-configs
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: dynamo-service
    spec:
      type: ClusterIP
      ports:
        - port: 8000
          protocol: TCP
          targetPort: 8000
      selector:
        rolebasedgroup.workloads.x-k8s.io/name: dynamo-pd
        rolebasedgroup.workloads.x-k8s.io/role: processor
  2. Jalankan perintah berikut untuk membuat resource.

    kubectl apply -f rbg.yaml
  3. Saat Anda membuat RBG, sistem secara otomatis membuat CustomResourceDefinition (CRD) bernama RoleBasedGroupScalingAdapter untuk peran yang memiliki ScalingAdapter diaktifkan. Sistem kemudian mengikat CRD ini ke peran tersebut. RoleBasedGroupScalingAdapter menyediakan implementasi subresource scale untuk peran yang terikat.

    • Jalankan perintah berikut untuk melihat RoleBasedGroupScalingAdapter yang dibuat secara otomatis untuk peran tertentu.

      kubectl get rolebasedgroupscalingadapter

      Output yang diharapkan:

      NAME                  PHASE   REPLICAS
      dynamo-pd-prefill     Bound   2
    • Jalankan perintah berikut untuk memeriksa status ScalingAdapter dynamo-pd-prefill.

      kubectl describe rolebasedgroupscalingadapter dynamo-pd-prefill

      Dalam output yang diharapkan, nilai Status.Phase adalah Bound. Hal ini menunjukkan bahwa ScalingAdapter telah berhasil terikat ke peran prefill dalam RBG.

      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]

Langkah 2: Buat KEDA ScaledObject untuk memantau antrian pesan

Buat resource ScaledObject, definisikan aturan penskalaan, dan kaitkan dengan RoleBasedGroupScalingAdapter yang telah Anda buat pada langkah sebelumnya.

  1. Buat file scaledobject.yaml dengan konten berikut. Konfigurasi ini menentukan ScalingAdapter dynamo-pd-prefill sebagai target penskalaan dan menetapkan pemicu berdasarkan backlog antrian pesan NATS.

    Pengaturan parameter dalam kebijakan penskalaan berikut hanya untuk tujuan demonstrasi. Anda harus menyesuaikan konfigurasi berdasarkan skenario bisnis aktual Anda.
    apiVersion: keda.sh/v1alpha1
    kind: ScaledObject
    metadata:
      name: dynamo-prefill-scaledobject
    spec:
      pollingInterval: 30 # For demo. Default: 30 seconds
      minReplicaCount: 1 # For demo. Default: 0
      maxReplicaCount: 6 # For demo. Default: 100
      scaleTargetRef:
        apiVersion: workloads.x-k8s.io/v1alpha1
        kind: RoleBasedGroupScalingAdapter
        name: dynamo-pd-prefill # Specify the Prefill role in the RoleBasedGroup as the scaling target
      triggers:
      - type: nats-jetstream
        metadata:
          natsServerMonitoringEndpoint: "nats.default.svc.cluster.local:8222" # NATS service endpoint
          account: "$G" # The default value when a NATS account is not set
          stream: "dynamo_prefill_queue" # The name of the PrefillQueue in Dynamo
          consumer: "worker-group" # The persistence name of the Consumer in Dynamo
          lagThreshold: "5" # The scaling threshold for the number of pending messages in the specified NATS queue
          useHttps: "false" # Specifies whether to use the HTTPS protocol
  2. Jalankan perintah berikut untuk membuat resource.

    kubectl apply -f scaledobject.yaml
  3. Jalankan perintah berikut untuk memeriksa status resource KEDA ScaledObject.

    kubectl describe so dynamo-prefill-scaledobject

    Klik untuk melihat output yang diharapkan.

    Name:         dynamo-prefill-scaledobject
    Namespace:    default
    Labels:       scaledobject.keda.sh/name=dynamo-prefill-scaledobject
    Annotations:  <none>
    API Version:  keda.sh/v1alpha1
    Kind:         ScaledObject
    Metadata:
      ...
    Spec:
      Cooldown Period:    300
      Max Replica Count:  6
      Min Replica Count:  1
      Polling Interval:   30
      Scale Target Ref:
        API Version:  workloads.x-k8s.io/v1alpha1
        Kind:         RoleBasedGroupScalingAdapter
        Name:         dynamo-pd-prefill
      Triggers:
        Metadata:
          Account:                          $G
          Consumer:                         worker-group
          Lag Threshold:                    5
          Nats Server Monitoring Endpoint:  nats.default.svc.cluster.local:8222
          Stream:                           dynamo_prefill_queue
          Use Https:                        false
        Type:                               nats-jetstream
    Status:
      Conditions:
        Message:  ScaledObject is defined correctly and is ready for scaling
        Reason:   ScaledObjectReady
        Status:   True
        Type:     Ready
        Message:  Scaling is not performed because triggers are not active
        Reason:   ScalerNotActive
        Status:   False
        Type:     Active
        Status:   Unknown
        Type:     Fallback
      External Metric Names:
        s0-nats-jetstream-dynamo_prefill_queue
      Hpa Name:                keda-hpa-dynamo-prefill-scaledobject
      Original Replica Count:  1
      Scale Target GVKR:
        Group:            workloads.x-k8s.io
        Kind:             RoleBasedGroupScalingAdapter
        Resource:         rolebasedgroupscalingadapters
        Version:          v1alpha1
      Scale Target Kind:  workloads.x-k8s.io/v1alpha1.RoleBasedGroupScalingAdapter
    Events:
      Type    Reason              Age   From           Message
      ----    ------              ----  ----           -------
      Normal  KEDAScalersStarted  3s    keda-operator  Started scalers watch
      Normal  ScaledObjectReady   3s    keda-operator  ScaledObject is ready for scaling

    Dalam output yang diharapkan, status Ready dalam Status.Conditions bernilai True.

    KEDA juga secara otomatis membuat resource Horizontal Pod Autoscaler (HPA). Nama resource HPA tersebut dicatat dalam bidang Status.HpaName. Anda dapat menjalankan perintah berikut untuk melihat resource HPA tersebut.

    kubectl get hpa keda-hpa-dynamo-prefill-scaledobject

Langkah 3: (Opsional) Lakukan uji stres dan verifikasi efek penskalaan

  1. Buat instans layanan untuk uji stres dan gunakan tool benchmark untuk menguji layanan tersebut.

    Untuk informasi selengkapnya tentang alat benchmark dan cara menggunakannya, lihat vLLM Benchmark.
    1. Buat file benchmark.yaml.

      Klik untuk melihat contoh kode YAML.

      apiVersion: apps/v1
      kind: StatefulSet
      metadata:
        labels:
          app: llm-benchmark
        name: llm-benchmark
      spec:
        selector:
          matchLabels:
            app: llm-benchmark
        template:
          metadata:
            labels:
              app: llm-benchmark
          spec:
            hostNetwork: true
            dnsPolicy: ClusterFirstWithHostNet
            containers:
            - command:
              - sh
              - -c
              - sleep inf
              image: # The Dynamo container image used to deploy the inference service
              imagePullPolicy: IfNotPresent
              name: llm-benchmark
              resources:
                limits:
                  cpu: "8"
                  memory: 40Gi
                requests:
                  cpu: "8"
                  memory: 40Gi
              volumeMounts:
              - mountPath: /models/Qwen3-32B
                name: llm-model
            volumes:
            - name: llm-model
              persistentVolumeClaim:
                claimName: llm-model
    2. Jalankan perintah berikut untuk membuat instans layanan uji stres.

      kubectl create -f benchmark.yaml
    3. Setelah instans berjalan, jalankan perintah berikut di dalam instans tersebut untuk melakukan uji stres:

      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 
  2. Selama uji stres, buka terminal baru dan jalankan perintah berikut untuk mengamati event penskalaan HPA.

    kubectl describe hpa keda-hpa-dynamo-prefill-scaledobject

    Dalam output yang diharapkan, bidang Events mencatat event SuccessfulRescale. Hal ini menunjukkan bahwa KEDA telah berhasil memicu skala keluar berdasarkan backlog antrian 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
  3. Secara bersamaan, amati perubahan jumlah replika untuk RoleBasedGroupScalingAdapter.

    kubectl describe rolebasedgroupscalingadapter dynamo-pd-prefill

    Dalam output yang diharapkan, nilai Spec.Replicas dan Status.Replicas meningkat dari nilai awalnya ke nilai setelah diskala keluar, misalnya menjadi 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