הטמעה לדוגמה של שירות מודלים פתוחים בתוכנת Distributed Cloud בלבד

סקירה כללית

המסמך הזה הוא מדריך ליישום הפתרון (SRI), שמתאר את השלבים הנדרשים לפריסה, להכניס לשימוש בסביבת הייצור ולבדיקה של הפתרון Open Model Serving on Distributed Cloud software only.

במדריך הזה נסביר איך לפרוס את מנוע הפרסום האופטימלי של vLLM שמריץ את Gemma 4 (google/gemma-4-31B-it) בתוכנה של Distributed Cloud בלבד, בהגדרת אשכול עם צומת יחיד שמיועד לפריסה בקצה הרשת, תוך שימוש במשאבי GPU פיזיים של NVIDIA (לדוגמה, RTX PRO 6000).

ההדרכה המפורטת הזו מיועדת לביצוע מלקוח טרמינל של CLI עם גישת kubectl רק לאשכול התוכנה של Distributed Cloud.

מטרות

בסיום ההטמעה של הפתרון הזה, תוכלו:

  • הגדרת פרמטרים של סביבת ההצגה באמצעות קובץ .env מקומי.
  • פריסת דרישת נפח אחסון מתמיד (PVC) באמצעות מחלקת האחסון local-shared כדי לשמור את משקלי המודל.
  • פריסת מנוע האופטימיזציה vLLM באמצעות קונטיינרים שעומדים בדרישות של Vertex AI, מיפוי משאבים פיזיים nvidia.com/gpu.
  • פורסים את ממשק המשתמש האינטרנטי של Gradio כדי לספק ממשק צ'אט אינטראקטיבי.
  • יצירת מנהרות מקומיות להעברת נתונים דרך יציאות כדי לאמת את מהירות התגובה של ההצגה באמצעות ממשקי API וממשק המשתמש באינטרנט.
  • מאמתים את השילוב של ניראות (observability) על ידי אישור של הכנסת נתונים של יומנים ומדדים (כולל טלמטריית GPU) ל-Cloud Logging ול-Cloud Monitoring.

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

דרישות מוקדמות (הגדרת אשכול)

המדריך הזה מבוסס על ההנחה שיש לכם אשכול תוכנה פעיל של Distributed Cloud בלבד, עם תמיכה ב-GPU. להתקנת האשכול, עיין במאמרי העזרה הרשמיים בנושא תוכנת Distributed Cloud ל-Bare Metal בלבד. הגדרת ה-GPU צריכה להיות בהתאם למסמכים הרשמיים Google Cloud בנושא הגדרה ושימוש ב-GPU של NVIDIA.

דרישות סף לתחנת עבודה של לקוח

מוודאים שהכלים הבאים מותקנים ומוגדרים בסביבת המסוף המקומית:

  • ‫ה-CLI של gcloud: נדרש להגדרת הפרויקט ולשליחת שאילתות יומנים. צריך להיות מאומת (gcloud auth login) ומוגדר לפרויקט הפעילGoogle Cloud שבו רשום האשכול של תוכנת Distributed Cloud בלבד.
  • ‫kubectl: נדרש לניהול משאבי אשכול. צריך להגדיר אותו עם ההקשר המתאים כדי לגשת רק לאשכול התוכנה המבוזרת של יעד Cloud (למשל, דרך שער חיבור או גישה ישירה לרשת המקומית).
  • ‫curl: נדרש לשליחת בקשות בדיקה לנקודת הקצה של הצגת המודעות.
  • ‫jq: נדרש לניתוח פלט JSON מנקודת הקצה של ההצגה.

גישה למודל של Hugging Face

כדי להוריד ולפרוס את מודל Gemma 4, צריך לקבל גישה למאגר Hugging Face:

  1. חשבון ב-Hugging Face: צריך לוודא שיש לכם חשבון רשום ב-Hugging Face.
  2. מאשרים את רישיון המודל: עוברים אל הדף של מודל Gemma 4 31B IT ומאשרים את תנאי הרישיון כדי לקבל גישה למשקלים של המודל המוגבל.
  3. יצירת אסימון גישה: יוצרים אסימון גישה למשתמש עם הרשאות קריאה בהגדרות החשבון ב-Hugging Face (הגדרות > אסימוני גישה). הטוקן הזה ישמש כ-HF_TOKEN בהגדרה שלכם.

הגדרת הפניה מרכזית

כל פרמטרי הפריסה מנוהלים באמצעות קובץ .env מרכזי שנמצא בכתובת <local-config-dir>/.env. צריך לוודא שהקובץ הזה קיים ומכיל את הפרמטרים הספציפיים שלכם (טוקן Hugging Face, מרחב שמות, שם המודל וכו').

מבנה .env לדוגמה:

# GDCso Cluster Namespace
NAMESPACE_NAME="your-custom-namespace"

# Hugging Face Token (Required to download gated Gemma models)
HF_TOKEN="your-hf-token-here"

# Model Sizing Parameters
MODEL_NAME="google/gemma-4-31B-it"
SAFE_MODEL_NAME="google-gemma-4-31B-it"

# Hyperparameters
MAX_MODEL_LEN=8192
GPU_MEMORY_UTILIZATION=0.95
MAX_NUM_SEQS=512
MAX_NUM_BATCHED_TOKENS=4096
DTYPE="bfloat16"

# Pod Sizing Requests
CPU_REQUEST="4"
MEMORY_REQUEST="80"

הגדרת הסביבה ופרטי הכניסה

יצירת מרחב שמות וסוד של Hugging Face

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

# Navigate to your project root
cd <local-working-dir>

# Sourced from your local .env file
source <local-config-dir>/.env

# Create namespace
kubectl create namespace ${NAMESPACE_NAME} --dry-run=client -o yaml | kubectl apply -f -

# Create Hugging Face token secret
kubectl create secret generic hf-token-secret \
  --from-literal=token="${HF_TOKEN}" \
  -n "${NAMESPACE_NAME}" --dry-run=client -o yaml | kubectl apply -f -

פריסת vLLM ב-Kubernetes

הפריסה מורכבת משלושה מניפסטים עיקריים: PVC,‏ Service ו-Deployment. התבניות האלה משתמשות במשתני סביבה שמוחלפים בזמן הפריסה.

דרישת נפח אחסון מתמיד (PVC)

ה-PVC מבטיח שמשקלי המודל יישמרו גם אחרי הפעלה מחדש של הפוד, באמצעות מחלקת האחסון local-shared של תוכנת Distributed Cloud בלבד.

יוצרים קובץ בשם vllm-pvc.yaml עם התוכן הבא:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: model-cache-pvc
  namespace: ${NAMESPACE_NAME}
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: local-shared
  resources:
    requests:
      storage: 100Gi

שירות

חושף את שרת vLLM API באופן פנימי ביציאה 8000.

יוצרים קובץ בשם vllm-service.yaml עם התוכן הבא:

apiVersion: v1
kind: Service
metadata:
  name: vllm-service
  namespace: ${NAMESPACE_NAME}
  labels:
    app: vllm
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/path: "/metrics"
    prometheus.io/port: "8000"
spec:
  ports:
    - name: http
      port: 8000
      targetPort: 8000
  selector:
    app: vllm
  type: ClusterIP

פריסה

מריצים את קונטיינר vLLM, טוענים את נפח מטמון המודל ומבקשים משאבי GPU פיזיים. אף על פי ש-96GB של VRAM מתאימים למשקלים לא מכומתים של BF16 ‏ (‎~62GB), יישום ההפניה הזה מאפשר כימות של Blackwell FP8 ‏(--quantization=fp8, משקלים של ‎~31GB) כדי להרחיב את מטמון KV ל-56.53GiB ‏(61,728 טוקנים) לשיפור התפוקה המקבילית.

יוצרים קובץ בשם vllm-deployment.yaml עם התוכן הבא:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-deployment
  namespace: ${NAMESPACE_NAME}
  labels:
    app: vllm
    ai.gke.io/inference-server: vllm
    ai.gke.io/model: ${SAFE_MODEL_NAME}
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: vllm
  template:
    metadata:
      labels:
        app: vllm
        examples.ai.gke.io/source: user-guide
        ai.gke.io/inference-server: vllm
        ai.gke.io/model: ${SAFE_MODEL_NAME}
    spec:
      runtimeClassName: nvidia
      containers:
        - name: vllm-container
          image: us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:gemma4
          imagePullPolicy: IfNotPresent
          command: ["python3", "-m", "vllm.entrypoints.openai.api_server"]
          args:
            - --model=$(MODEL_ID)
            - --host=0.0.0.0
            - --port=8000
            - --tensor-parallel-size=1
            - --enable-log-requests
            - --max-model-len=${MAX_MODEL_LEN}
            - --gpu-memory-utilization=${GPU_MEMORY_UTILIZATION}
            - --max-num-seqs=${MAX_NUM_SEQS}
            - --max-num-batched-tokens=${MAX_NUM_BATCHED_TOKENS}
            - --dtype=${DTYPE}
            - --trust-remote-code
            - --enable-prefix-caching
            - --enable-chunked-prefill
            - --quantization=fp8
          env:
            - name: MODEL_ID
              value: ${MODEL_NAME}
            - name: HUGGING_FACE_HUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-token-secret
                  key: token
            - name: LD_LIBRARY_PATH
              value: "/usr/local/nvidia/lib64"
          ports:
            - name: http
              containerPort: 8000
          resources:
            requests:
              cpu: ${CPU_REQUEST}
              memory: "${MEMORY_REQUEST}Gi"
              nvidia.com/gpu: "1"
            limits:
              cpu: ${CPU_REQUEST}
              memory: "${MEMORY_REQUEST}Gi"
              nvidia.com/gpu: "1"
          volumeMounts:
            - mountPath: /root/.cache/huggingface
              name: cache-volume
            - mountPath: /dev/shm
              name: dshm
      volumes:
        - name: cache-volume
          persistentVolumeClaim:
            claimName: model-cache-pvc
        - name: dshm
          emptyDir:
            medium: Memory
            sizeLimit: 16Gi

פריסת ממשק המשתמש של Gradio

ממשק המשתמש של Gradio מספק ממשק אינטרנטי אינטראקטיבי לשיחה עם המודל. הוא נפרס באשכול ומתחבר לשירות vLLM דרך רשת Kubernetes הפנימית.

פריסת Gradio

יוצרים קובץ בשם gradio-deployment.yaml עם התוכן הבא:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: gradio-deployment
  namespace: ${NAMESPACE_NAME}
  labels:
    app: gradio
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gradio
  template:
    metadata:
      labels:
        app: gradio
    spec:
      containers:
      - name: gradio
        image: us-docker.pkg.dev/google-samples/containers/gke/gradio-app:v1.0.4
        resources:
          requests:
            cpu: "250m"
            memory: "512Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"
        env:
        - name: CONTEXT_PATH
          value: "/v1/chat/completions"
        - name: HOST
          value: "http://vllm-service:8000"
        - name: MODEL_ID
          value: "${MODEL_NAME}"
        ports:
        - containerPort: 7860

שירות Gradio

יוצרים קובץ בשם gradio-service.yaml עם התוכן הבא:

apiVersion: v1
kind: Service
metadata:
  name: gradio-service
  namespace: ${NAMESPACE_NAME}
spec:
  selector:
    app: gradio
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 7860
  type: ClusterIP

הגדרת ניראות (observability)

לפרטים על הגדרת רישום ביומן ומעקב באפליקציה, אפשר לעיין במדריך הרשמי בנושא רישום ביומן ומעקב באפליקציה.

מעקב אחרי vLLM

כדי להפעיל גירוד של מדדי vLLM באמצעות השירות המנוהל של Google Cloud ל-Prometheus‏ (GMP), אנחנו פורסים משאב PodMonitoring שמטרגט את ה-pods של vLLM.

יוצרים קובץ בשם vllm-pod-monitoring.yaml עם התוכן הבא:

apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
  name: vllm-pod-monitoring
  namespace: ${NAMESPACE_NAME}
spec:
  selector:
    matchLabels:
      app: vllm
  endpoints:
  - port: http
    interval: 30s

מעקב אחר GPU

כדי להפעיל גירוד של מדדי GPU מ-NVIDIA DCGM Exporter באמצעות השירות המנוהל של Google Cloud ל-Prometheus‏ (GMP), אנחנו פורסים משאב PodMonitoring במרחב השמות gpu-operator. פרטים נוספים זמינים במאמר שליחת מדדי GPU אל Cloud Monitoring.

יוצרים קובץ בשם gpu-pod-monitoring.yaml עם התוכן הבא:

apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
  name: dcgm-gmp
  namespace: gpu-operator
spec:
  selector:
    matchLabels:
      app: nvidia-dcgm-exporter
  endpoints:
  - port: metrics
    interval: 30s

שלבי הביצוע

פריסת מניפסטים

מחילים את המניפסטים על האשכול. מכיוון שקובצי ה-YAML מכילים placeholders של משתני סביבה, צריך להשתמש ב-envsubst כדי להחליף את המשתנים מקובץ .env לפני שמחילים אותם.

  1. עוברים לספריית העבודה שמכילה את קובצי ה-YAML:

    cd <local-working-dir>
    
  2. מפעילים את משתני הסביבה:

    source <local-config-dir>/.env
    
  3. מחילים את המניפסטים ברצף:

    # Apply core serving manifests
    envsubst < vllm-pvc.yaml | kubectl apply -f -
    envsubst < vllm-service.yaml | kubectl apply -f -
    envsubst < vllm-deployment.yaml | kubectl apply -f -
    
    # Apply observability manifests
    envsubst < vllm-pod-monitoring.yaml | kubectl apply -f -
    kubectl apply -f gpu-pod-monitoring.yaml
    
    # Apply Gradio UI manifests
    envsubst < gradio-deployment.yaml | kubectl apply -f -
    envsubst < gradio-service.yaml | kubectl apply -f -
    

אימות

מעקב אחר רצף האתחול של ה-pod

מזרימים את היומנים של מאגר התגים כדי לעקוב אחרי רצף ההפעלה:

# 1. Check pod status (wait until it is Running)
kubectl get pods -n ${NAMESPACE_NAME} -l app=vllm

# 2. Stream logs to verify model loading
kubectl logs -f -l app=vllm -n ${NAMESPACE_NAME}

מחכים עד שרואים ביומנים את האות שההצגה הפעילה מוכנה:

INFO: Application startup complete.

יצירת מנהרה להעברה ליציאה אחרת

כדי לבדוק את נקודת הקצה באופן מקומי, מעבירים את יציאת השירות 8000:

kubectl port-forward service/vllm-service 8000:8000 -n ${NAMESPACE_NAME}

משאירים את חלון ה-Terminal פתוח או מריצים אותו ברקע.

נקודת קצה (endpoint) להצגת שאילתות

ממסוף נפרד, בודקים את מהירות התגובה של ה-API:

1. רשימת מודלים של שאילתות

curl -s http://localhost:8000/v1/models | jq

2. שאילתות להשלמת צ'אט

curl -s http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "'"${MODEL_NAME}"'",
    "messages": [
      {"role": "user", "content": "What is AGI in 100 words"}
    ],
    "max_tokens": 150
  }' | jq

אימות ההקצאות של ה-GPU וה-VRAM

כדי לוודא שהמודל משתמש בחומרה בצורה יעילה ושהזיכרון תואם, אפשר להריץ ביקורת על ה-GPU.

בדיקת ה-GPU באמצעות nvidia-smi בתוך ה-pod

מאחזרים את השם של ה-pod הפעיל של vLLM ומריצים את הפקודה nvidia-smi בתוך המאגר:

# Dynamically get the pod name
export VLLM_POD_NAME=$(kubectl get pods -n ${NAMESPACE_NAME} -l app=vllm -o jsonpath='{.items[0].metadata.name}')

# Run nvidia-smi inside the container
kubectl exec ${VLLM_POD_NAME} -n ${NAMESPACE_NAME} -- nvidia-smi

פלט צפוי: הפלט צריך לדווח על ניצול זיכרון ה-VRAM הפיזי שתואם לפרמטר GPU_MEMORY_UTILIZATION. לדוגמה, בתחנת עבודה עם NVIDIA RTX PRO 6000 (כ-96GB VRAM) שמופעלת עם google/gemma-4-31B-it, אמור להיות מוקצה ל-VLLM::EngineCore נפח של כ-95,800MiB:GPU_MEMORY_UTILIZATION=0.95

+-----------------------------------------------------------------------------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA RTX PRO 6000 Blac...    On  |   00000000:05:00.0 Off |                    0 |
| N/A   36C    P0             85W /  600W |   95805MiB /  97887MiB |      0%      Default |
+-----------------------------------------+------------------------+----------------------+

אימות ההקצאה של מטמון KV ביומנים

אפשר גם לבדוק את יומני ההקצאה הפנימיים של vLLM כדי לוודא את גודל מטמון KV ואת מספר האסימונים המקבילים:

kubectl logs ${VLLM_POD_NAME} -n ${NAMESPACE_NAME} | grep -E "Available KV cache|GPU KV cache size"

הפלט הצפוי:

(EngineCore pid=362) INFO 06-18 15:03:43 [gpu_worker.py:456] Available KV cache memory: 56.53 GiB
(EngineCore pid=362) INFO 06-18 15:03:43 [kv_cache_utils.py:1316] GPU KV cache size: 61,728 tokens

הנתון הזה מאשר ש-56.53‎ GiB של VRAM שמורים למטמון KV של ההקשר, מה שמאפשר מספר גדול של טוקנים במקביל.

אימות הפריסה של ממשק המשתמש של Gradio

אחרי שה-pod של Gradio פועל, אפשר לגשת לממשק המשתמש על ידי יצירת מנהרת העברת נתונים (port-forwarding).

מעקב אחרי הסטטוס של פוד Gradio

מוודאים שרכיב ה-Gradio פועל:

kubectl get pods -n ${NAMESPACE_NAME} -l app=gradio

הפלט הצפוי:

NAME                                READY   STATUS    RESTARTS   AGE
gradio-deployment-yyyyyyyy-yyyyy    1/1     Running   0          1m

הקמת מנהרת העברת נתונים ליציאה אחרת ל-Gradio

מעבירים את יציאת שירות Gradio‏ 8080 ליציאה המקומית 7860:

kubectl port-forward service/gradio-service 7860:8080 -n ${NAMESPACE_NAME}

משאירים את הטרמינל פתוח או מפעילים אותו ברקע:

kubectl port-forward service/gradio-service 7860:8080 -n ${NAMESPACE_NAME} > pf_gradio.log 2>&1 & sleep 3

גישה לממשק המשתמש באינטרנט

  • אם מריצים את הכלי בתחנת עבודה מקומית: פותחים את הדפדפן ועוברים ישירות לכתובת: http://localhost:7860
  • אם מריצים ב-Cloud Shell: לוחצים על הלחצן תצוגה מקדימה באינטרנט, בוחרים באפשרות שינוי יציאה, מזינים 7860 ולוחצים על שינוי ותצוגה מקדימה.

אמור להופיע ממשק הצ'אטבוט של Gradio. אתם יכולים להקליד הודעה כדי ליצור אינטראקציה עם מודל Gemma. ה-pod של Gradio ינתב את הבקשה באופן פנימי לנקודת הקצה vllm-service.

ניקוי מנהרות

כדי להפסיק את מנהרת העברת הפורטים:

pkill -f "port-forward service/gradio-service"

מעקב אחרי האימות

אחרי שפורסים את vLLM ומחילים את המשאב PodMonitoring, אפשר לוודא שהמדדים מוזנים ל-Cloud Monitoring.

אימות הסטטוס של PodMonitoring

מוודאים שמשאב PodMonitoring נוצר ופעיל:

kubectl get podmonitoring -n ${NAMESPACE_NAME}

אימות ההטמעה של מדדים באמצעות מסוף Google Cloud

כדי לוודא שהמדדים נקלטים, אפשר להשתמש ב Google Cloud מסוף (Metrics Explorer):

  1. פותחים את Metrics Explorer במסוף Google Cloud :
    • עוברים אל https://console.cloud.google.com/monitoring/metrics-explorer (חשוב לבחור את הפרויקט ${GCP_PROJECT_ID}).
  2. בתפריט הנפתח בחירת מדד, מחפשים ובוחרים את המדדים הבאים:
    • ‫prometheus.googleapis.com/vllm:num_requests_running/gauge כדי לאמת את מדדי ה-vLLM.
    • ‫prometheus.googleapis.com/DCGM_FI_DEV_GPU_UTIL/gauge כדי לאמת את מדדי השימוש ב-GPU.
  3. בודקים את התרשים כדי לוודא שנקודות הנתונים מוצגות באופן פעיל.

אימות רישום

אחרי שעומס העבודה יפעל, תוכלו לוודא שהיומנים מיוצאים ל-Cloud Logging.

אימות של הטמעת יומנים ב-vLLM

מוודאים שיומני מאגרי vLLM מיוצאים ל-Cloud Logging:

project_id=$(gcloud config get-value project)

gcloud logging read "resource.type=\"k8s_container\" AND resource.labels.namespace_name=\"${NAMESPACE_NAME}\" AND resource.labels.container_name=\"vllm-container\"" --limit=10 --project=${project_id}

אימות ההטמעה של יומן ייצוא ה-GPU

מוודאים שיומני הקונטיינר של כלי הייצוא של ה-GPU מיוצאים ל-Cloud Logging:

project_id=$(gcloud config get-value project)

gcloud logging read "resource.type=\"k8s_container\" AND resource.labels.namespace_name=\"gpu-operator\" AND resource.labels.container_name=\"nvidia-dcgm-exporter\"" --limit=10 --project=${project_id}

אימות יומנים באמצעות מסוף Google Cloud (Logs Explorer)

אפשר גם לאמת את ההעברה של היומן באמצעות Google Cloud המסוף:

  1. פותחים את Logs Explorer במסוף Google Cloud :
    • עוברים אל https://console.cloud.google.com/logs/query (חשוב לבחור את הפרויקט ${GCP_PROJECT_ID}).
  2. בתיבה Query, מזינים את השאילתה הבאה כדי להציג את יומני vLLM:

    resource.type="k8s_container"
    resource.labels.namespace_name="<NAMESPACE_NAME>"
    resource.labels.container_name="vllm-container"
    

    (מחליפים את <NAMESPACE_NAME> במרחב השמות בפועל, לדוגמה: your-custom-namespace).

  3. לוחצים על Run query. אמורות להופיע רשומות ביומן מהמאגר vLLM.

  4. כדי לבדוק את היומנים של כלי הייצוא של GPU, מריצים את השאילתה הבאה:

    resource.type="k8s_container"
    resource.labels.namespace_name="gpu-operator"
    resource.labels.container_name="nvidia-dcgm-exporter"