הצגת Llama 3.1 405B ב-GKE עם פירוק של PD באמצעות Dynamo ו-SGLang במכונות וירטואליות מסוג A4

במדריך הזה מוסבר איך לפרוס ולהציג את Llama 3.1 405B (nvidia/Llama-3.1-405B-Instruct-FP8), מודל עם 405 מיליארד פרמטרים שעבר כוונון לפי הוראות וקוונטיזציה ל-FP8, ב-Google Kubernetes Engine (GKE) ב- Google Cloud. הפריסה משתמשת ב-Cluster Toolkit, בשתי מכונות וירטואליות (VM) מסוג A4 (a4-highgpu-8g) עם 16 מעבדי NVIDIA B200 GPU, ב-NVIDIA Dynamo וב-SGLang serving framework עם הפרדה של prefill-decode (PD) במארח יחיד.

בהגדרת הפריסה שמתוארת במדריך הזה, משתמשים ב-Cluster Toolkit כדי להקצות אשכול GKE במצב Standard. האשכול מכיל שני צומתי a4-highgpu-8g שמחוברים באמצעות רשת GPUDirect Remote Direct Memory Access ‏ (RDMA). לאחר מכן מתקינים את KAI Scheduler ואת פלטפורמת NVIDIA Dynamo עם תזמון קבוצתי של Grove. בשלב האחרון, פורסים משאב מותאם אישית של DynamoGraphDeployment שמפריד בין מילוי מראש לבין פענוח בשני וורקרים עם מארח יחיד ו-8 מעבדי GPU, עם מקביליות של טנזורים ומטמון KV של FP8.

מטרות

  • מתקינים את Cluster Toolkit ומקצים אשכול GKE עם שני צמתים מסוג A4 במצב רגיל.
  • מתקינים באשכול את פלטפורמת KAI Scheduler ו-NVIDIA Dynamo עם תזמון Grove gang.
  • פריסת שרת הסקת מסקנות SGLang עם הפרדת PD, עם וורקר אחד למילוי מראש ב-A4 במארח יחיד ווורקר אחד לפענוח ב-A4 במארח יחיד.
  • שליחת בקשה להשלמת צ'אט לנתב החזיתי של Dynamo.
  • מריצים עומס עבודה להערכת ביצועים של מילוי בקשות.
  • מחיקת המשאבים שנפרסו.

עלויות

במסמך הזה משתמשים ברכיבים הבאים שלGoogle Cloud, והשימוש בהם כרוך בתשלום:

כדי להעריך את העלויות בהתאם לשימוש הצפוי, אתם יכולים להשתמש במחשבון העלויות.

לפני שמתחילים

מגדירים את הסביבה למדריך:

  1. מתקינים ומאתחלים את Google Cloud CLI, ומתקינים את kubectl ואת Helm (גרסה 3.10 ואילך). אם אתם משתמשים ב-Cloud Shell, הכלים האלה מותקנים מראש.
  2. אם אתם מריצים את המדריך הזה מתחנת עבודה של Linux או macOS ולא מ-Cloud Shell, תצטרכו להתקין את יחסי התלות הנדרשים של Cluster Toolkit, כמו Terraform ו-Git.
  3. מפעילים את ממשקי ה-API הבאים בפרויקט ב- Google Cloud : ‏Kubernetes Engine API, ‏Compute Engine API, ‏Cloud Storage API, ‏ניהול זהויות והרשאות גישה (IAM) API, ‏Cloud Resource Manager API ו-Service Usage API.

    הפעלת ממשקי API

איך מקבלים טוקן גישה ל-Hugging Face

כדי להשתמש במדריך הזה, צריך טוקן גישה ל-Hugging Face. אפשר להירשם לחשבון בחינם ב-Hugging Face. אחרי שיוצרים חשבון, יוצרים אסימון גישה:

  1. בדף Welcome to Hugging Face, לוחצים על הדמות של החשבון ובוחרים באפשרות טוקן גישה.
  2. בדף טוקני גישה, לוחצים על יצירת טוקן חדש.
  3. בוחרים את סוג הטוקן Read ומזינים שם לטוקן.
  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.
  • ‫YOUR_REGION: האזור שבו נמצאת הקיבולת של A4 ושבו רוצים לפרוס את האשכול, למשל us-central1.
  • ‫YOUR_ZONE: האזור שבו נמצאת הקיבולת של A4, למשל us-central1-b.
  • ‫YOUR_CLUSTER_NAME: השם של אשכול GKE ושל פריסת Cluster Toolkit. השתמשו בשם באורך של עד 20 תווים, כדי שמזהה חשבון השירות של מאגר הצמתים שנוצר (YOUR_CLUSTER_NAME-gke-np-sa) לא יחרוג מהמגבלה של 30 תווים בניהול הזהויות והרשאות הגישה (IAM).
  • ‫YOUR_RESERVATION_NAME: השם של ההזמנה שלכם ב-A4.
  • ‫YOUR_BUCKET_NAME: שם ייחודי גלובלי לקטגוריה של Cloud Storage שבה מאוחסן המצב של Terraform ל-Cluster Toolkit.
  • ‫YOUR_HF_TOKEN: טוקן הגישה שלכם ל-Hugging Face.

יצירת אשכול GKE

מורידים את Cluster Toolkit ומקצים אשכול GKE במצב רגיל עם שני צומתי a4-highgpu-8g, רשת GPUDirect RDMA ותמיכה ב-DRA:

  1. נכנסים ל-Google Cloud CLI ויוצרים Application Default Credentials ל-Terraform:

    gcloud auth login
    gcloud auth application-default login
  2. מורידים ומחלצים את חבילת Cluster Toolkit שנוצרה מראש לגרסה שצוינה ב-CLUSTER_TOOLKIT_VERSION (v1.104.0):

    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. כדי לאחסן את מצב Terraform של פריסת Cluster Toolkit, צריך ליצור קטגוריה של 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. יוצרים את קובץ התצורה cluster-toolkit/examples/gke-a4/gke-a4-deployment.yaml לפריסה של שני צומתי a4-highgpu-8g:

    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

    כברירת מחדל, קובץ הפריסה מוגדר לצרוך הזמנה ספציפית. אם אין לכם מקום שמור ספציפי, לפני פריסת האשכול אתם יכולים לעדכן את cluster-toolkit/examples/gke-a4/gke-a4-deployment.yaml כדי להשתמש באפשרות צריכה חלופית, כמו VM במודל Spot או Flex-start VM. הוראות להגדרה מופיעות במאמר אפשרויות צריכה של GKE.

  5. עוברים לספרייה cluster-toolkit, פורסים את אשכול GKE באמצעות הפקודה gcluster deploy וחוזרים לספריית העבודה:

    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 דקות. אם מריצים את ./gcluster deploy בלי הדגל --auto-approve, כדי להחיל את השינויים המוצעים צריך להזין a כשמוצגת בקשה לעשות זאת.

  6. מגדירים את פרטי הכניסה של kubectl כדי לתקשר עם האשכול:

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

התקנת KAI Scheduler ו-NVIDIA Dynamo

מכינים את האשכול על ידי יצירת סוד של Hugging Face והתקנת KAI Scheduler ופלטפורמת NVIDIA Dynamo עם תזמון קבוצתי של Grove:

  1. יוצרים סוד של Kubernetes בשם hf-secret כדי לאחסן את טוקן הגישה של Hugging Face:

    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token="${HF_TOKEN}" \
        --dry-run=client -o yaml | kubectl apply -f -
  2. מורידים את תרשים ה-Helm של פלטפורמת NVIDIA Dynamo‏ (v1.5.0), מתקינים את KAI Scheduler ומתקינים את פלטפורמת Dynamo עם Grove:

    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) מגדיר ResourceClaimTemplate של Kubernetes‏ (all-mrdma), שמקצה את מכשירי ה-RDMA עם multi-NIC של הצומת (mrdma.google.com) באמצעות DRA, ו-DynamoGraphDeployment של NVIDIA Dynamo‏ (llama3-405b-a4-disagg) עם תזמון קבוצתי של Grove. הפריסה כוללת את הרכיבים הבאים:

  • רכיב קצה קדמי (Frontend): מריץ רפליקה אחת של נתב שמקבלת בקשות מהלקוח, מגלה את ה-workers הרשומים למילוי מראש ולפענוח, ומתאמת את הניתוב של הבקשות המפורקות.
  • Prefill worker (Prefill): מריץ וורקר של מארח יחיד בצומת a4-highgpu-8g אחד עם שמונה מעבדי NVIDIA B200 GPU. בזמן ההפעלה, ה-Pod מקבל את סביבת NCCL/gIB, מוריד את נקודת הבדיקה nvidia/Llama-3.1-405B-Instruct-FP8 מ-Hugging Face Hub ל-Local NVMe SSD של הצומת, ומפעיל את python3 -m dynamo.sglang ב---disaggregation-mode prefill עם הדגלים הבאים:
    • --tensor-parallel-size 8
    • --kv-cache-dtype fp8_e4m3
    • --context-length 16384
    • --mem-fraction-static 0.85
  • ‫Decode worker (Decode): מריץ וורקר של מארח יחיד בצומת השני של a4-highgpu-8g עם שמונה מעבדי GPU מסוג NVIDIA B200. בזמן ההפעלה, ה-Pod שומר במטמון את משקלי המודל ב-SSD המקומי של NVMe של הצומת, ומפעיל את python3 -m dynamo.sglang ב---disaggregation-mode decode עם הדגלים הבאים:
    • --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. כדי לעקוב אחרי הורדת המשקלים וההפעלה של השרת, מציגים את ה-Pods באשכול ומזרימים את היומנים של ה-Pods למילוי מראש ולפענוח:

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

    מחליפים את YOUR_POD_NAME בשם של ה-Pod למילוי מראש או ה-Pod לפענוח מהפלט של kubectl get pods.

    בהפעלה הראשונה, נדרשות כ-10 עד 20 דקות כדי להוריד את משקלי המודל 405B FP8 ולאתחל את שרתי SGLang. לפני ששולחים בקשות, מחכים עד שבעמודה READY של kubectl get pods יופיע הערך 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. בסשן שני של הטרמינל, שולחים בקשה להשלמת צ'אט לנקודת הקצה (endpoint) של /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 כדי לבדוק את הביצועים של שרת Llama 3.1 405B שנפרס מ-Dynamo frontend Pod. מגדירים את ההערכה ל-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. מסירים את גרסאות ה-Helm של פלטפורמת NVIDIA Dynamo ושל KAI Scheduler:

    helm uninstall dynamo-platform kai-scheduler || true
  3. מוחקים את הסוד של Hugging Face ב-Kubernetes:

    kubectl delete secret hf-secret --ignore-not-found || true
  4. בספרייה cluster-toolkit, משמידים את אשכול GKE ואת התשתית שמשויכת אליו באמצעות הפקודה gcluster destroy:

    cd cluster-toolkit
    ./gcluster destroy "${CLUSTER_NAME}" --auto-approve || true
    cd ..
  5. כדי למחוק את הקטגוריה של Cloud Storage שבה מאוחסן המצב של Terraform, משתמשים בפקודה gcloud storage rm:

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

המאמרים הבאים