A4 VM에서 Dynamo 및 SGLang을 사용하여 PD 분리 방식으로 GKE에서 Llama 3.1 405B 서빙

이 튜토리얼에서는 Google Cloud의 Google Kubernetes Engine(GKE)에서 FP8로 양자화된 4,050억 개의 파라미터 명령 조정 모델인 Llama 3.1 405B(nvidia/Llama-3.1-405B-Instruct-FP8)를 배포하고 서빙하는 방법을 보여줍니다. 이 배포에서는 Cluster Toolkit, 총 16개의 NVIDIA B200 GPU가 있는 2개의 A4 (a4-highgpu-8g) 가상 머신 (VM) 인스턴스, NVIDIA Dynamo, 단일 호스트 프리필 디코딩 (PD) 분리가 있는 SGLang 서빙 프레임워크를 사용합니다.

이 튜토리얼에 설명된 배포 구성에서는 Cluster Toolkit을 사용하여 일반 모드에서 GKE 클러스터를 프로비저닝합니다. 클러스터에는 GPUDirect 원격 직접 메모리 액세스 (RDMA) 네트워킹을 통해 연결된 a4-highgpu-8g 노드 2개가 포함되어 있습니다. 그런 다음 Grove 갱 예약을 사용하여 KAI Scheduler 및 NVIDIA Dynamo 플랫폼을 설치합니다. 마지막으로 텐서 동시 로드 및 FP8 KV 캐시를 사용하여 단일 호스트 8 GPU 작업자 2개에서 프리필과 디코딩을 분리하는 DynamoGraphDeployment 커스텀 리소스를 배포합니다.

목표

  • Cluster Toolkit을 설치하고 일반 모드에서 2노드 A4 GKE 클러스터를 프로비저닝합니다.
  • 클러스터에 Grove 갱 예약과 함께 KAI 스케줄러 및 NVIDIA Dynamo 플랫폼을 설치합니다.
  • 단일 호스트 A4 프리필 워커 1개와 단일 호스트 A4 디코딩 워커 1개를 사용하여 PD 분리 SGLang 추론 서버를 배포합니다.
  • Dynamo 프런트엔드 라우터에 채팅 완성 요청을 전송합니다.
  • 서빙 벤치마크 워크로드를 실행합니다.
  • 배포된 리소스를 정리하세요.

비용

이 문서에서는 비용이 청구될 수 있는Google Cloud구성요소를 사용합니다.

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요.

시작하기 전에

튜토리얼 환경을 설정합니다.

  1. Google Cloud CLI를 설치하고 초기화하고 kubectl 및 Helm (버전 3.10 이상)을 설치합니다. Cloud Shell을 사용하는 경우 이러한 도구가 사전 설치됩니다.
  2. Cloud Shell이 아닌 Linux 또는 macOS 워크스테이션에서 이 튜토리얼을 실행하는 경우 Terraform 및 Git와 같은 필수 Cluster Toolkit 종속 항목을 설치합니다.
  3. Google Cloud 프로젝트에서 Kubernetes Engine API, Compute Engine API, Cloud Storage API, Identity and Access Management API, Cloud Resource Manager API, Service Usage API를 사용 설정합니다.

    API 사용 설정

Hugging Face 액세스 토큰 가져오기

이 튜토리얼을 사용하려면 Hugging Face 액세스 토큰이 필요합니다. Hugging Face에서 무료 계정에 가입할 수 있습니다. 계정이 있으면 액세스 토큰을 생성합니다.

  1. Hugging Face에 오신 것을 환영합니다 페이지에서 계정 아바타를 클릭하고 액세스 토큰을 선택합니다.
  2. 액세스 토큰 페이지에서 새 토큰 만들기를 클릭합니다.
  3. 읽기 토큰 유형을 선택하고 토큰 이름을 입력합니다.
  4. 액세스 토큰이 표시됩니다. 토큰을 안전한 장소에 저장합니다.

필요한 역할

튜토리얼을 완료하는 데 필요한 권한을 얻으려면 관리자에게 프로젝트에 대한 다음 IAM 역할을 부여해 달라고 요청하세요.

역할 부여에 대한 자세한 내용은 프로젝트, 폴더, 조직에 대한 액세스 관리를 참조하세요.

커스텀 역할이나 다른 사전 정의된 역할을 통해 필요한 권한을 얻을 수도 있습니다.

환경 변수 설정

이 튜토리얼 전체에서 사용하는 환경 변수를 내보냅니다.

export PROJECT_ID="YOUR_PROJECT_ID"
export REGION="YOUR_REGION"
export ZONE="YOUR_ZONE"
export CLUSTER_NAME="YOUR_CLUSTER_NAME"
export RESERVATION_NAME="YOUR_RESERVATION_NAME"
export GCS_BUCKET="YOUR_BUCKET_NAME"
export HF_TOKEN="YOUR_HF_TOKEN"
export CLUSTER_TOOLKIT_VERSION="v1.104.0"
export DYNAMO_RELEASE_VERSION="1.5.0"

gcloud config set project "${PROJECT_ID}"
gcloud config set billing/quota_project "${PROJECT_ID}"

다음을 바꿉니다.

  • YOUR_PROJECT_ID: Google Cloud프로젝트의 ID
  • YOUR_REGION: A4 용량이 있고 클러스터를 배포할 리전입니다(예: us-central1).
  • YOUR_ZONE: A4 용량이 있는 영역(예: us-central1-b)
  • YOUR_CLUSTER_NAME: GKE 클러스터 및 Cluster Toolkit 배포의 이름입니다. 생성된 노드 풀 서비스 계정 ID(YOUR_CLUSTER_NAME-gke-np-sa)가 30자(영문 기준) Identity and Access Management(IAM) 제한 내에 있도록 20자(영문 기준) 이하의 이름을 사용합니다.
  • YOUR_RESERVATION_NAME: A4 예약의 이름입니다.
  • YOUR_BUCKET_NAME: Cluster Toolkit의 Terraform 상태를 저장하는 Cloud Storage 버킷의 전역 고유 이름입니다.
  • YOUR_HF_TOKEN: Hugging Face 액세스 토큰입니다.

GKE 클러스터 만들기

Cluster Toolkit을 다운로드하고 a4-highgpu-8g 노드 2개, GPUDirect RDMA 네트워킹, DRA 지원을 사용하여 일반 모드에서 GKE 클러스터를 프로비저닝합니다.

  1. Google Cloud CLI에 로그인하고 Terraform의 애플리케이션 기본 사용자 인증 정보를 생성합니다.

    gcloud auth login
    gcloud auth application-default login
  2. CLUSTER_TOOLKIT_VERSION (v1.104.0)에 지정된 버전에 대해 사전 빌드된 Cluster Toolkit 번들을 다운로드하고 압축을 풉니다.

    TAG="${CLUSTER_TOOLKIT_VERSION:-v1.104.0}"
    OS="linux"
    ARCH="amd64"
    mkdir -p cluster-toolkit && curl -L "https://github.com/GoogleCloudPlatform/cluster-toolkit/releases/download/${TAG}/gcluster_bundle_${OS}_${ARCH}.tgz" | tar -xz -C cluster-toolkit

    macOS 또는 arm64 워크스테이션에서 이 명령어를 실행하는 경우 플랫폼과 일치하도록 OS="mac" 또는 ARCH="arm64"를 설정합니다.

  3. Cluster Toolkit 배포의 Terraform 상태를 저장하려면 객체 버전 관리가 사용 설정된 Cloud Storage 버킷을 만듭니다.

    gcloud storage buckets create "gs://${GCS_BUCKET}" \
        --default-storage-class=STANDARD \
        --project="${PROJECT_ID}" \
        --location="${REGION}" \
        --uniform-bucket-level-access
    gcloud storage buckets update "gs://${GCS_BUCKET}" --versioning
  4. a4-highgpu-8g 노드 2개에 대한 cluster-toolkit/examples/gke-a4/gke-a4-deployment.yaml 배포 구성 파일을 만듭니다.

    cat <<EOF > cluster-toolkit/examples/gke-a4/gke-a4-deployment.yaml
    terraform_backend_defaults:
      type: gcs
      configuration:
        bucket: ${GCS_BUCKET}
    
    vars:
      project_id: ${PROJECT_ID}
      deployment_name: ${CLUSTER_NAME}
      region: ${REGION}
      zone: ${ZONE}
      static_node_count: 2
      authorized_cidr: 0.0.0.0/0
      reservation_affinity:
        consume_reservation_type: SPECIFIC_RESERVATION
        specific_reservations:
        - name: ${RESERVATION_NAME}
    EOF

    기본적으로 배포 파일은 특정 예약을 사용하도록 구성됩니다. 특정 예약이 없는 경우 클러스터를 배포하기 전에 스팟 VM 또는 Flex-start VM과 같은 대체 소비 옵션을 사용하도록 cluster-toolkit/examples/gke-a4/gke-a4-deployment.yaml를 업데이트할 수 있습니다. 구성 안내는 GKE 소비 옵션을 참조하세요.

  5. cluster-toolkit 디렉터리로 변경하고 gcluster deploy 명령어를 사용하여 GKE 클러스터를 배포한 후 작업 디렉터리로 돌아갑니다.

    cd cluster-toolkit
    ./gcluster deploy \
        -d examples/gke-a4/gke-a4-deployment.yaml \
        examples/gke-a4/gke-a4.yaml \
        --auto-approve -w
    cd ..

    클러스터를 만드는 데 약 20~30분 정도 걸립니다. --auto-approve 플래그 없이 ./gcluster deploy를 실행하는 경우 제안된 변경사항을 적용하려면 메시지가 표시될 때 a를 입력합니다.

  6. 클러스터와 통신하도록 kubectl 사용자 인증 정보를 구성합니다.

    gcloud container clusters get-credentials "${CLUSTER_NAME}" \
        --location="${REGION}" \
        --project="${PROJECT_ID}"

KAI Scheduler 및 NVIDIA Dynamo 설치

Hugging Face 보안 비밀을 만들고 Grove 갱 예약을 사용하여 KAI 스케줄러와 NVIDIA Dynamo 플랫폼을 설치하여 클러스터를 준비합니다.

  1. Hugging Face 액세스 토큰을 저장할 hf-secret이라는 Kubernetes 보안 비밀을 만듭니다.

    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token="${HF_TOKEN}" \
        --dry-run=client -o yaml | kubectl apply -f -
  2. NVIDIA Dynamo 플랫폼 Helm 차트 (v1.5.0)를 다운로드하고, KAI 스케줄러를 설치하고, Grove가 사용 설정된 Dynamo 플랫폼을 설치합니다.

    RELEASE_VERSION="${DYNAMO_RELEASE_VERSION:-1.5.0}"
    helm fetch "https://helm.ngc.nvidia.com/nvidia/ai-dynamo/charts/dynamo-platform-${RELEASE_VERSION}.tgz"
    tar -zxf "dynamo-platform-${RELEASE_VERSION}.tgz"
    
    helm upgrade --install kai-scheduler ./dynamo-platform/charts/kai-scheduler \
        --set scheduler.args.default-staleness-grace-period="-1s" \
        --set topologyMigration.enabled=false
    
    kubectl wait --for=condition=Available deployment/kai-operator --timeout=120s
    until kubectl get deployment/queue-controller >/dev/null 2>&1; do sleep 2; done
    kubectl wait --for=condition=Available deployment/queue-controller --timeout=120s
    
    helm upgrade --install dynamo-platform ./dynamo-platform \
        --set global.grove.install=true \
        --set global.grove.enabled=true \
        --set global.kai-scheduler.enabled=true

단일 호스트 분할 SGLang 서버 배포

워크로드 매니페스트 (llama3.1-405b-singlehost-a4.yaml)는 DRA를 통해 노드의 멀티 NIC RDMA 기기 (mrdma.google.com)를 할당하는 Kubernetes ResourceClaimTemplate (all-mrdma)과 Grove 그룹 예약이 사용 설정된 NVIDIA Dynamo DynamoGraphDeployment (llama3-405b-a4-disagg)를 정의합니다. 배포는 다음과 같이 구성됩니다.

  • 프런트엔드 (Frontend): 클라이언트 요청을 수락하고, 등록된 프리필 및 디코딩 작업자를 검색하고, 분리된 요청 라우팅을 조정하는 라우터 복제본 하나를 실행합니다.
  • 프리필 작업자 (Prefill): NVIDIA B200 GPU 8개가 있는 a4-highgpu-8g 노드 1개에서 단일 호스트 작업자를 실행합니다. 시작 시 포드는 NCCL/gIB 환경을 소싱하고 Hugging Face Hub에서 노드의 로컬 NVMe SSD로 nvidia/Llama-3.1-405B-Instruct-FP8 체크포인트를 다운로드하고 다음 플래그를 사용하여 --disaggregation-mode prefill에서 python3 -m dynamo.sglang를 실행합니다.
    • --tensor-parallel-size 8
    • --kv-cache-dtype fp8_e4m3
    • --context-length 16384
    • --mem-fraction-static 0.85
  • 디코딩 작업자 (Decode): 8개의 NVIDIA B200 GPU가 있는 두 번째 a4-highgpu-8g 노드에서 단일 호스트 작업자를 실행합니다. 시작 시 포드는 노드의 로컬 NVMe SSD에 모델 가중치를 캐시하고 다음 플래그를 사용하여 --disaggregation-mode decode에서 python3 -m dynamo.sglang를 실행합니다.
    • --tensor-parallel-size 8
    • --kv-cache-dtype fp8_e4m3
    • --context-length 16384
    • --mem-fraction-static 0.85

분리된 추론 서버를 배포하고 모니터링합니다.

  1. llama3.1-405b-singlehost-a4.yaml 워크로드 매니페스트를 다운로드합니다.

    curl -O https://raw.githubusercontent.com/GoogleCloudSamples/AIHypercomputerSamples/refs/heads/main/gpu/inference/llama3-405b/llama3.1-405b-singlehost-a4.yaml

    또는 다음 콘텐츠로 llama3.1-405b-singlehost-a4.yaml이라는 파일을 만듭니다.

    # ==============================================================================
    # Single-Host Disaggregated Serving of Llama 3.1 405B FP8 on GKE
    # using NVIDIA Dynamo Platform (v1.5.0) and SGLang on GPU A4 (2 x a4-highgpu-8g)
    # Topology: 1-node Prefill (8 x B200, TP=8) + 1-node Decode (8 x B200, TP=8)
    # ==============================================================================
    apiVersion: resource.k8s.io/v1
    kind: ResourceClaimTemplate
    metadata:
      name: all-mrdma
    spec:
      spec:
        devices:
          requests:
            - name: req-mrdma
              exactly:
                deviceClassName: mrdma.google.com
                allocationMode: All
    ---
    apiVersion: nvidia.com/v1alpha1
    kind: DynamoGraphDeployment
    metadata:
      name: llama3-405b-a4-disagg
      annotations:
        nvidia.com/enable-grove: "true"
    spec:
      backendFramework: sglang
      services:
        # --------------------------------------------------------------------------
        # FRONTEND: Dynamo OpenAI-Compatible Router (`python3 -m dynamo.frontend`)
        # --------------------------------------------------------------------------
        Frontend:
          componentType: frontend
          replicas: 1
          resources:
            requests:
              cpu: "8"
              memory: "32Gi"
            limits:
              cpu: "8"
              memory: "32Gi"
          extraPodSpec:
            tolerations:
              - key: "nvidia.com/gpu"
                operator: "Exists"
                effect: "NoSchedule"
            mainContainer:
              image: nvcr.io/nvidia/ai-dynamo/sglang-runtime:1.5.0
              command: ["/bin/bash", "-c"]
              args:
                - |
                  set -ex
                  exec python3 -m dynamo.frontend --http-port 8000
    
        # --------------------------------------------------------------------------
        # PREFILL WORKER: 1 x GPU A4 (`a4-highgpu-8g`, 8 x B200 GPUs, TP=8)
        # --------------------------------------------------------------------------
        Prefill:
          componentType: worker
          subComponentType: prefill
          replicas: 1
          livenessProbe:
            httpGet:
              path: /live
              port: system
            initialDelaySeconds: 600
            periodSeconds: 60
            timeoutSeconds: 30
            failureThreshold: 60
          readinessProbe:
            httpGet:
              path: /health
              port: system
            initialDelaySeconds: 120
            periodSeconds: 30
            timeoutSeconds: 30
            failureThreshold: 60
          sharedMemory:
            size: 128Gi
          resources:
            requests:
              gpu: "8"
              cpu: "128"
              memory: "1500Gi"
            limits:
              gpu: "8"
              cpu: "128"
              memory: "2000Gi"
          envs:
            - name: MODEL_PATH
              value: "nvidia/Llama-3.1-405B-Instruct-FP8"
            - name: HF_HOME
              value: "/data"
            - name: HF_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_api_token
                  optional: true
            - name: DYN_SYSTEM_ENABLED
              value: "true"
            - name: DYN_SYSTEM_USE_ENDPOINT_HEALTH_STATUS
              value: "[\"generate\"]"
            - name: DYN_SYSTEM_PORT
              value: "9090"
            - name: LD_LIBRARY_PATH
              value: "/usr/local/gib/lib64:/usr/local/nvidia/lib64"
            - name: GLOO_SOCKET_IFNAME
              value: "eth0"
            - name: WITH_NVIDIA_PEERMEM
              value: "0"
            # Xet CAS downloads returned unretried 429s; plain HTTP downloads back off on 429.
            - name: HF_HUB_DISABLE_XET
              value: "1"
          extraPodSpec:
            resourceClaims:
              - name: rdma
                resourceClaimTemplateName: all-mrdma
            nodeSelector:
              cloud.google.com/gke-accelerator: nvidia-b200
              cloud.google.com/gke-gpu: "true"
              kubernetes.io/arch: amd64
            tolerations:
              - key: "nvidia.com/gpu"
                operator: "Exists"
                effect: "NoSchedule"
              - key: "kubernetes.io/arch"
                operator: "Equal"
                value: "amd64"
                effect: "NoSchedule"
            mainContainer:
              image: nvcr.io/nvidia/ai-dynamo/sglang-runtime:1.5.0
              resources:
                claims:
                  - name: rdma
              startupProbe:
                httpGet:
                  path: /live
                  port: system
                initialDelaySeconds: 120
                periodSeconds: 30
                timeoutSeconds: 10
                failureThreshold: 120
              command: ["/bin/bash", "-c"]
              args:
                - |
                  set -ex
                  if [ -f /usr/local/gib/scripts/set_nccl_env.sh ]; then
                    . /usr/local/gib/scripts/set_nccl_env.sh
                  fi
    
                  # Pre-download with huggingface_hub, which backs off on HTTP 429s. Dynamo's built-in
                  # downloader (ModelExpress) aborts on the first 429 and skips fetching if weights are cached.
                  echo "=== [Dynamo Prefill Worker] Ensuring weights are cached in ${HF_HOME:-/data} ==="
                  python3 -c "import os; from huggingface_hub import snapshot_download; snapshot_download(os.environ['MODEL_PATH'], max_workers=16)"
                  # --host 0.0.0.0 is required so MooncakeKVBootstrapServer binds to 0.0.0.0:30001 instead of 127.0.0.1:30001.
                  echo "=== [Dynamo Prefill Worker] Launching dynamo.sglang ==="
                  exec python3 -m dynamo.sglang \
                    --model-path "${MODEL_PATH}" \
                    --host 0.0.0.0 \
                    --disaggregation-mode prefill \
                    --disaggregation-bootstrap-port 30001 \
                    --tensor-parallel-size 8 \
                    --kv-cache-dtype fp8_e4m3 \
                    --context-length 16384 \
                    --mem-fraction-static 0.85 \
                    --enable-metrics
              volumeMounts:
                - name: library-dir-host
                  mountPath: /usr/local/nvidia
                - name: gib
                  mountPath: /usr/local/gib
                - name: data-volume
                  mountPath: /data
            volumes:
              - name: library-dir-host
                hostPath:
                  path: /home/kubernetes/bin/nvidia
              - name: gib
                hostPath:
                  path: /home/kubernetes/bin/gib
              - name: data-volume
                emptyDir: {}
    
        # --------------------------------------------------------------------------
        # DECODE WORKER: 1 x GPU A4 (`a4-highgpu-8g`, 8 x B200 GPUs, TP=8)
        # --------------------------------------------------------------------------
        Decode:
          componentType: worker
          subComponentType: decode
          replicas: 1
          livenessProbe:
            httpGet:
              path: /live
              port: system
            initialDelaySeconds: 600
            periodSeconds: 60
            timeoutSeconds: 30
            failureThreshold: 60
          readinessProbe:
            httpGet:
              path: /health
              port: system
            initialDelaySeconds: 120
            periodSeconds: 30
            timeoutSeconds: 30
            failureThreshold: 60
          sharedMemory:
            size: 128Gi
          resources:
            requests:
              gpu: "8"
              cpu: "128"
              memory: "1500Gi"
            limits:
              gpu: "8"
              cpu: "128"
              memory: "2000Gi"
          envs:
            - name: MODEL_PATH
              value: "nvidia/Llama-3.1-405B-Instruct-FP8"
            - name: HF_HOME
              value: "/data"
            - name: HF_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_api_token
                  optional: true
            - name: DYN_SYSTEM_ENABLED
              value: "true"
            - name: DYN_SYSTEM_USE_ENDPOINT_HEALTH_STATUS
              value: "[\"generate\"]"
            - name: DYN_SYSTEM_PORT
              value: "9090"
            - name: LD_LIBRARY_PATH
              value: "/usr/local/gib/lib64:/usr/local/nvidia/lib64"
            - name: GLOO_SOCKET_IFNAME
              value: "eth0"
            - name: WITH_NVIDIA_PEERMEM
              value: "0"
            # Xet CAS downloads returned unretried 429s; plain HTTP downloads back off on 429.
            - name: HF_HUB_DISABLE_XET
              value: "1"
          extraPodSpec:
            resourceClaims:
              - name: rdma
                resourceClaimTemplateName: all-mrdma
            nodeSelector:
              cloud.google.com/gke-accelerator: nvidia-b200
              cloud.google.com/gke-gpu: "true"
              kubernetes.io/arch: amd64
            tolerations:
              - key: "nvidia.com/gpu"
                operator: "Exists"
                effect: "NoSchedule"
              - key: "kubernetes.io/arch"
                operator: "Equal"
                value: "amd64"
                effect: "NoSchedule"
            mainContainer:
              image: nvcr.io/nvidia/ai-dynamo/sglang-runtime:1.5.0
              resources:
                claims:
                  - name: rdma
              startupProbe:
                httpGet:
                  path: /live
                  port: system
                initialDelaySeconds: 120
                periodSeconds: 30
                timeoutSeconds: 10
                failureThreshold: 120
              command: ["/bin/bash", "-c"]
              args:
                - |
                  set -ex
                  if [ -f /usr/local/gib/scripts/set_nccl_env.sh ]; then
                    . /usr/local/gib/scripts/set_nccl_env.sh
                  fi
    
                  # Pre-download with huggingface_hub, which backs off on HTTP 429s. Dynamo's built-in
                  # downloader (ModelExpress) aborts on the first 429 and skips fetching if weights are cached.
                  echo "=== [Dynamo Decode Worker] Ensuring weights are cached in ${HF_HOME:-/data} ==="
                  python3 -c "import os; from huggingface_hub import snapshot_download; snapshot_download(os.environ['MODEL_PATH'], max_workers=16)"
                  echo "=== [Dynamo Decode Worker] Launching dynamo.sglang ==="
                  exec python3 -m dynamo.sglang \
                    --model-path "${MODEL_PATH}" \
                    --host 0.0.0.0 \
                    --disaggregation-mode decode \
                    --disaggregation-bootstrap-port 30001 \
                    --tensor-parallel-size 8 \
                    --kv-cache-dtype fp8_e4m3 \
                    --context-length 16384 \
                    --mem-fraction-static 0.85 \
                    --enable-metrics
              volumeMounts:
                - name: library-dir-host
                  mountPath: /usr/local/nvidia
                - name: gib
                  mountPath: /usr/local/gib
                - name: data-volume
                  mountPath: /data
            volumes:
              - name: library-dir-host
                hostPath:
                  path: /home/kubernetes/bin/nvidia
              - name: gib
                hostPath:
                  path: /home/kubernetes/bin/gib
              - name: data-volume
                emptyDir: {}
  2. 워크로드 매니페스트를 클러스터에 적용합니다.

    kubectl apply -f llama3.1-405b-singlehost-a4.yaml
  3. 가중치 다운로드 및 서버 초기화를 모니터링하려면 클러스터의 포드를 나열하고 프리필 및 디코딩 포드의 로그를 스트리밍합니다.

    kubectl get pods
    POD_NAME="YOUR_POD_NAME"
    kubectl logs -f "${POD_NAME}"

    YOUR_POD_NAME을 kubectl get pods 출력의 프리필 포드 또는 디코딩 포드 이름으로 바꿉니다.

    처음 실행할 때는 405B FP8 모델 가중치를 다운로드하고 SGLang 서버를 초기화하는 데 약 10~20분이 걸립니다. 요청을 보내기 전에 프런트엔드, 사전 입력, 디코딩 포드가 kubectl get pods의 READY 열에 1/1을 표시할 때까지 기다립니다.

    NAME                                        READY   STATUS    RESTARTS   AGE
    llama3-405b-a4-disagg-0-decode-tgl7q        1/1     Running   0          15m
    llama3-405b-a4-disagg-0-frontend-wrxq5      1/1     Running   0          15m
    llama3-405b-a4-disagg-0-prefill-67h5s       1/1     Running   0          15m
    

추론 요청 전송

배포된 Llama 3.1 405B 서비스에 채팅 완성 요청을 전송합니다.

  1. 로컬 포트 8000을 llama3-405b-a4-disagg-frontend 서비스로 전달합니다.

    kubectl port-forward svc/llama3-405b-a4-disagg-frontend 8000:8000

    이 터미널 세션에서 포트 전달을 실행 중인 상태로 두거나 백그라운드에서 명령어를 실행합니다.

  2. 두 번째 터미널 세션에서 /v1/chat/completions 엔드포인트에 채팅 완성 요청을 전송합니다.

    curl http://127.0.0.1:8000/v1/chat/completions \
      -H "Content-Type: application/json" \
      -d '{
        "model": "nvidia/Llama-3.1-405B-Instruct-FP8",
        "messages": [{"role": "user", "content": "Explain KV-cache disaggregation in one sentence."}],
        "max_tokens": 64
      }' | jq .

벤치마크 워크로드 실행

sglang.bench_serving를 사용하여 Dynamo 프런트엔드 포드에서 배포된 Llama 3.1 405B 서버를 벤치마킹합니다. 입력 토큰 1,024개, 출력 토큰 1,024개 (1k/1k), 프롬프트 2,048개, 최대 동시 실행 수 512개로 벤치마크를 구성합니다.

ROUTER_POD=$(kubectl get pod -l 'nvidia.com/dynamo-component-type=frontend' -o jsonpath='{.items[0].metadata.name}')

kubectl exec "${ROUTER_POD}" -- python3 -m sglang.bench_serving \
  --backend sglang-oai-chat \
  --base-url http://localhost:8000 \
  --model nvidia/Llama-3.1-405B-Instruct-FP8 \
  --dataset-name random \
  --random-input-len 1024 \
  --random-output-len 1024 \
  --random-range-ratio 1.0 \
  --num-prompts 2048 \
  --max-concurrency 512 \
  --warmup-requests 10 | tee benchmark.log

벤치마크가 완료되면 다음과 같은 측정항목이 포함된 출력을 찾습니다.

  • Output token throughput
  • Total token throughput
  • TTFT
  • TPOT

삭제

이 튜토리얼에서 사용한 리소스에 대한 추가 비용이 Google Cloud 계정에 청구되지 않게 하려면 Kubernetes 워크로드, GKE 클러스터, Cloud Storage 버킷을 삭제합니다.

  1. DynamoGraphDeployment 및 ResourceClaimTemplate 리소스를 삭제합니다.

    kubectl delete -f llama3.1-405b-singlehost-a4.yaml --ignore-not-found || true
  2. NVIDIA Dynamo 플랫폼 및 KAI Scheduler Helm 출시 버전을 제거합니다.

    helm uninstall dynamo-platform kai-scheduler || true
  3. Hugging Face Kubernetes 보안 비밀을 삭제합니다.

    kubectl delete secret hf-secret --ignore-not-found || true
  4. cluster-toolkit 디렉터리에서 gcluster destroy 명령어를 사용하여 GKE 클러스터 및 관련 인프라를 삭제합니다.

    cd cluster-toolkit
    ./gcluster destroy "${CLUSTER_NAME}" --auto-approve || true
    cd ..
  5. Terraform 상태를 저장하는 Cloud Storage 버킷을 삭제하려면 gcloud storage rm 명령어를 사용합니다.

    gcloud storage rm --recursive "gs://${GCS_BUCKET}"

다음 단계