כוונון עדין והרחבה של למידת חיזוק עם NVIDIA NeMo RL ב-GKE

במדריך הזה תלמדו איך לתזמן סביבת אימון מבוזרת ללמידת חיזוק (RL) ב-Google Kubernetes Engine ‏ (GKE). אתם יכולים להשתמש ב-Ray ובמסגרת NVIDIA NeMo RL כדי להגדיר סביבת אימון מבוזרת לצורך כוונון עדין של מודל.

המדריך הזה מתמקד בצינור עיבוד הנתונים לאימון של Group Relative Policy Optimization ‏ (GRPO) ב-GKE עם Ray ו-NeMo RL. ‫GRPO הוא אלגוריתם ללמידה חיזוקית שנועד לשפר את יכולת ההסקה של מודל. האלגוריתם הזה חוסך בזיכרון ומפשט את תהליך הלמידה המחוזקת על ידי ביטול של רכיב ה-Critic, או מודל הערך, ושימוש בחישוב יחסי שמבוסס על קבוצה במקום זאת.

לפני שאתם מתחילים את המדריך הזה, אתם צריכים להשלים את המדריך כוונון ושינוי קנה המידה של למידת חיזוק באמצעות verl ב-GKE. במדריך הזה נעשה שימוש באותה הגדרת אשכול ובתצורה כמו במדריך בנושא כוונון עדין של RL ושינוי קנה מידה באמצעות verl.

רקע

בקטעים הבאים מופיעה סקירה כללית קצרה של המושגים שבהם משתמשים במדריך הזה.

למידת חיזוקים (RL)

ב-RL, המודלים לומדים מתוך ניסיון, מחקר ומשוב, ולא מתוך חיקוי סטטי. במהלך האימון המקדים, המודל לומד מה לומר, ובמהלך הלמידה ממשוב אנושי (RLHF), הוא לומד איך להיות מועיל, בטוח והגיוני. למידה מחוזקת משמשת כגשר בין מודל בסיס לבין מודל שעבר כוונון לתרחיש שימוש ספציפי.

מה זה למידת חיזוק?

אופטימיזציה יחסית של מדיניות קבוצתית (GRPO)

‫GRPO, אלגוריתם שזכה לפופולריות בזכות DeepSeek, מציע חלופה חסכונית בזיכרון ל-Proximal Policy Optimization ‏ (PPO) להתאמת LLM, על ידי הסרת מודל ה-Critic. במקום רשת Critic, ‏ GRPO יוצר קבוצה של תשובות לאותה הנחיה ומשתמש בתגמול הממוצע של הקבוצה הזו כבסיס להשוואה.

מידע נוסף זמין במאמר בנושא GRPO.

NVIDIA NeMo RL

‫NeMo RL היא ספריית קוד פתוח של NVIDIA לפוסט-טריינינג, שמיועדת ל-RL שניתן להתאמה לעומס. ‫NeMo RL הוא חלק ממערכת NeMo הרחבה, והוא מאפשר ניסויים בקנה מידה קטן ב-GPU יחיד ופריסות מרובות צמתים באלפי GPU.

מידע נוסף זמין במאמר NVIDIA NeMo RL.

קבוצת הנתונים GSM8k

במדריך הזה משתמשים במערך הנתונים GSM8k, שמכיל 8,500 בעיות מילוליות במתמטיקה ברמה של בית ספר יסודי, באיכות גבוהה ובמגוון לשוני.

באמצעות GSM8k ו-GRPO, המודל יוצר קבוצה של n תשובות שונות לאותה בעיה. ‫GRPO משווה את התשובות האלה לממוצע של הקבוצה. המודל מקבל תגמול גדול יותר על נתיבים שהם נכונים באופן עקבי והגיוניים בהשוואה לשאר הקבוצה. עם הזמן, המודל לומד שהדרך הכי אמינה למקסם את התגמול היא להסביר את השלבים בצורה ברורה, ולכן הוא מצמצם את התגמול על תשובות עם ביצועים נמוכים.

מידע נוסף זמין במאמר בנושא GSM8k.

מטרות

במדריך הזה מוסבר איך להגדיר RL ב-GKE באמצעות NeMo RL. לשם כך, צריך לבצע את השלבים הבאים:

  1. מכינים את הסביבה.
  2. הגדרת אשכול GKE עם מעבדי GPU מסוג B200 או H200.
  3. מגדירים את KubeRay לניהול אשכול Ray מבוזר.
  4. משתמשים ב-Managed Lustre לאחסון עם ביצועים גבוהים.
  5. מריצים משימת אימון של GRPO באמצעות NeMo RL.

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

  1. נכנסים לחשבון Google Cloud . אנחנו ממליצים למשתמשים חדשים ב- Google Cloud ליצור חשבון כדי שיוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 300$ להרצה, לבדיקה ולפריסה של עומסי העבודה.
  2. התקינו את ה-CLI של Google Cloud.

  3. אם אתם משתמשים בספק זהויות חיצוני (IdP), קודם אתם צריכים להיכנס ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

  4. כדי לאתחל את ה-CLI של gcloud, הריצו את הפקודה הבאה:

    gcloud init
  5. יוצרים או בוחרים Google Cloud פרויקט.

    תפקידים שנדרשים כדי לבחור או ליצור פרויקט

    • בחירת פרויקט: כדי לבחור פרויקט לא נדרש תפקיד IAM ספציפי – אתם יכולים לבחור כל פרויקט שקיבלתם בו תפקיד.
    • יצירת פרויקט: כדי ליצור פרויקט, צריך את התפקיד "יצירת פרויקטים" (roles/resourcemanager.projectCreator), שכולל את ההרשאה resourcemanager.projects.create. איך מקצים תפקידים
    • יוצרים פרויקט ב- Google Cloud :

      gcloud projects create PROJECT_ID

      מחליפים את PROJECT_ID בשם של פרויקט Google Cloud שיוצרים.

    • בוחרים את הפרויקט שיצרתם: Google Cloud

      gcloud config set project PROJECT_ID

      מחליפים את PROJECT_ID בשם הפרויקט ב- Google Cloud .

  6. מוודאים שהחיוב מופעל בפרויקט Google Cloud .

  7. מפעילים את ממשקי ה-API הנדרשים, אם הם עדיין לא מופעלים:

    התפקידים שנדרשים כדי להפעיל ממשקי API

    כדי להפעיל ממשקי API, צריך את ההרשאה serviceusage.services.enable. אם יצרתם את הפרויקט, סביר להניח שכבר יש לכם את ההרשאה הזו דרך התפקיד 'בעלים' (roles/owner). אחרת, תוכלו לקבל את ההרשאה הזו דרך התפקיד 'אדמין בממשק Service Usage' (roles/serviceusage.serviceUsageAdmin). איך מקצים תפקידים

    gcloud services enable container.googleapis.com storage.googleapis.com compute.googleapis.com servicenetworking.googleapis.com lustre.googleapis.com
  8. התקינו את ה-CLI של Google Cloud.

  9. אם אתם משתמשים בספק זהויות חיצוני (IdP), קודם אתם צריכים להיכנס ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

  10. כדי לאתחל את ה-CLI של gcloud, הריצו את הפקודה הבאה:

    gcloud init
  11. יוצרים או בוחרים Google Cloud פרויקט.

    תפקידים שנדרשים כדי לבחור או ליצור פרויקט

    • בחירת פרויקט: כדי לבחור פרויקט לא נדרש תפקיד IAM ספציפי – אתם יכולים לבחור כל פרויקט שקיבלתם בו תפקיד.
    • יצירת פרויקט: כדי ליצור פרויקט, צריך את התפקיד "יצירת פרויקטים" (roles/resourcemanager.projectCreator), שכולל את ההרשאה resourcemanager.projects.create. איך מקצים תפקידים
    • יוצרים פרויקט ב- Google Cloud :

      gcloud projects create PROJECT_ID

      מחליפים את PROJECT_ID בשם של פרויקט Google Cloud שיוצרים.

    • בוחרים את הפרויקט שיצרתם: Google Cloud

      gcloud config set project PROJECT_ID

      מחליפים את PROJECT_ID בשם הפרויקט ב- Google Cloud .

  12. מוודאים שהחיוב מופעל בפרויקט Google Cloud .

  13. מפעילים את ממשקי ה-API הנדרשים, אם הם עדיין לא מופעלים:

    התפקידים שנדרשים כדי להפעיל ממשקי API

    כדי להפעיל ממשקי API, צריך את ההרשאה serviceusage.services.enable. אם יצרתם את הפרויקט, סביר להניח שכבר יש לכם את ההרשאה הזו דרך התפקיד 'בעלים' (roles/owner). אחרת, תוכלו לקבל את ההרשאה הזו דרך התפקיד 'אדמין בממשק Service Usage' (roles/serviceusage.serviceUsageAdmin). איך מקצים תפקידים

    gcloud services enable container.googleapis.com storage.googleapis.com compute.googleapis.com servicenetworking.googleapis.com lustre.googleapis.com
  14. מעניקים תפקידים לחשבון המשתמש. מריצים את הפקודה הבאה לכל אחד מהתפקידים הבאים ב-IAM: roles/container.admin, roles/iam.serviceAccountAdmin, roles/storage.admin, roles/servicenetworking.networksAdmin, roles/compute.networkAdmin, roles/lustre.admin

    gcloud projects add-iam-policy-binding PROJECT_ID --member="user:USER_IDENTIFIER" --role=ROLE

    מחליפים את מה שכתוב בשדות הבאים:

    • ‫PROJECT_ID: מזהה הפרויקט.
    • ‫USER_IDENTIFIER: המזהה של חשבון המשתמש . לדוגמה, myemail@example.com.
    • ‫ROLE: תפקיד ה-IAM שאתם מקצים לחשבון המשתמש.
  15. יוצרים חשבון ב-Hugging Face, אם עדיין אין לכם חשבון.
  16. מוודאים שיש לכם אסימון של Hugging Face עם read access.
  17. אם אין לכם חשבון Weights & Biases (Wandb), אתם צריכים ליצור חשבון.
  18. יוצרים מפתח API של Wandb.
  19. מוודאים שלפרויקט Google Cloud שלכם יש מכסה מספקת ל-GPU מסוג B200 ו-H200. מידע נוסף זמין במאמרים תכנון מכסת GPU ומכסת GPU.
  20. מוודאים של Google Cloud פרויקט יש את המכסה הנדרשת ל-Managed Lustre. מידע נוסף זמין במאמר מכסות ומגבלות של Managed Lustre.

הכנת הסביבה

במדריך הזה משתמשים ב-Cloud Shell.

  1. עוברים אל Google Cloud המסוף.

  2. בחלק העליון של Google Cloud חלון המסוף, לוחצים על הלחצן Activate Cloud Shell.

  3. מגדירים את משתני הסביבה הבאים:

    export CONTROL_PLANE_REGION="YOUR_REGION"
    export NODE_ZONE="YOUR_ZONE"
    export LUSTRE_ZONE="YOUR_LUSTRE_ZONE"
    export CLUSTER_NAME="YOUR_CLUSTER_NAME"
    export GPU_TYPE="YOUR_GPU_TYPE"
    export MACHINE_TYPE="YOUR_MACHINE_TYPE"
    export CPU_MACHINE_TYPE="YOUR_CPU_MACHINE_TYPE"
    export KSA_NAME="generic-ksa"
    export NAMESPACE="default"
    export RESERVATION="YOUR_RESERVATION_NAME"
    export LUSTRE_NAME="YOUR_LUSTRE_NAME"
    export HF_TOKEN="YOUR_HF_TOKEN"
    export WANDB_API_KEY="YOUR_WANDB_API_KEY"
    
    export PROJECT_ID=$(gcloud config get project)
    export PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}" --format="value(projectNumber)")

    מחליפים את הערכים הבאים:

    • ‫YOUR_REGION: האזור ב-Compute Engine למישור הבקרה של אשכול GKE.
    • ‫YOUR_ZONE: האזור של הצמתים. בוחרים אזור שבו זמינים מעבדי GPU מסוג NVIDIA B200 או H200.
    • ‫YOUR_LUSTRE_ZONE: האזור של המכונה ב-Managed Lustre. בוחרים אזור שבו Managed Lustre זמין. כדי לשפר את הביצועים, להקטין את זמן האחזור ולמנוע עלויות של תעבורת נתונים יוצאת (egress) ברשת, צריך להשתמש באותו אזור כמו YOUR_ZONE.
    • ‫YOUR_CLUSTER_NAME: השם של אשכול GKE.
    • ‫YOUR_GPU_TYPE: המאיץ שהזמנתם בהזמנת הקיבולת של Compute Engine. צריך להזין אחד מהערכים הבאים:
      • ‫nvidia-b200: NVIDIA B200 ‏ (180 GB)
      • nvidia-h200-141gb: NVIDIA H200 (141 GB)
    • ‫YOUR_MACHINE_TYPE: סוג המכונה לשימוש:
      • למעבדי GPU מסוג NVIDIA B200 (180GB), צריך להשתמש בגרסה a4-highgpu-8g ואילך.
      • למעבדי GPU מסוג NVIDIA H200 (141GB), צריך להשתמש בגרסה a3-ultragpu-8g ואילך.
    • ‫YOUR_CPU_MACHINE_TYPE: סוג המכונה ב-Compute Engine עבור מאגר הצמתים שמוגדר כברירת מחדל ל-CPU. משתמשים בסוג מכונה highmem עם לפחות 16 ליבות וירטואליות (vCPU) שנתמך באזור האשכול, כמו n4-highmem-16 או n2d-highmem-16.
    • ‫YOUR_RESERVATION_NAME: השם של הקצאת ה-GPU.
    • ‫YOUR_LUSTRE_NAME: השם של מכונת Lustre.
    • ‫YOUR_HF_TOKEN: האסימון שלכם ב-Hugging Face.
    • ‫YOUR_WANDB_API_KEY: מפתח ה-API של Wandb.
  4. יוצרים את משתני הסביבה הבאים לרשת:

    export NETWORK_PREFIX="YOUR_NETWORK_NAME"
    export NETWORK="${NETWORK_PREFIX}-nemo-rl"
    export GVNIC_NETWORK_PREFIX="YOUR_GVNIC_NETWORK_PREFIX"
    export RDMA_NETWORK_PREFIX="YOUR_RDMA_NETWORK_PREFIX"

    מחליפים את הערכים הבאים:

    • ‫YOUR_NETWORK_NAME: התחילית שמשמשת ליצירת השם של רשת ה-VPC לאשכול GKE.
    • ‫YOUR_GVNIC_NETWORK_PREFIX: הקידומת של שם רשת gVNIC. אתם יכולים להשתמש בכל קידומת שתרצו.
    • ‫YOUR_RDMA_NETWORK_PREFIX: הקידומת של רשת הגישה הישירה לזיכרון (RDMA) מרחוק. אתם יכולים להשתמש בכל קידומת שתרצו.
  5. אם הפלאגין KubeRay (בטא) ל-CLI של kubectl עדיין לא מותקן, מתקינים אותו:

    KUBECTL_RAY_VERSION="v1.3.2"
    curl -LO "https://github.com/ray-project/kuberay/releases/download/${KUBECTL_RAY_VERSION}/kubectl-ray_${KUBECTL_RAY_VERSION}_linux_amd64.tar.gz"
    tar -xvf "kubectl-ray_${KUBECTL_RAY_VERSION}_linux_amd64.tar.gz"
    sudo cp kubectl-ray /usr/local/bin/
    sudo chmod +x /usr/local/bin/kubectl-ray
    rm -f "kubectl-ray_${KUBECTL_RAY_VERSION}_linux_amd64.tar.gz" kubectl-ray

    הפלאגין הזה מספק פקודות לניהול עומסי עבודה של Ray ב-Kubernetes, כמו פתיחת סשנים אינטראקטיביים ושליחת משימות.

הגדרת התשתית

בקטע הזה, יוצרים רשתות VPC ואשכול GKE.

יצירת רשת VPC

  1. יוצרים רשת VPC לממשק gVNIC:

    gcloud compute networks create ${NETWORK} --subnet-mode=auto
    
    gcloud compute networks create ${GVNIC_NETWORK_PREFIX}-net \
        --subnet-mode=custom
    
    gcloud compute networks subnets create ${GVNIC_NETWORK_PREFIX}-sub \
        --network=${GVNIC_NETWORK_PREFIX}-net \
        --region=${CONTROL_PLANE_REGION} \
        --range=192.168.0.0/24
    
    gcloud compute firewall-rules create ${GVNIC_NETWORK_PREFIX}-internal \
        --network=${GVNIC_NETWORK_PREFIX}-net \
        --action=ALLOW \
        --rules=tcp:0-65535,udp:0-65535,icmp \
        --source-ranges=192.168.0.0/16
  2. יוצרים רשת VPC ורשתות משנה ל-RDMA שכוללות שמונה רשתות משנה לשמונה יחידות GPU:

    gcloud compute networks create ${RDMA_NETWORK_PREFIX}-net \
        --network-profile=${NODE_ZONE}-vpc-roce \
        --subnet-mode=custom
    
    for N in $(seq 0 7); do
      gcloud compute networks subnets create ${RDMA_NETWORK_PREFIX}-sub-$N \
        --network=${RDMA_NETWORK_PREFIX}-net \
        --region=${CONTROL_PLANE_REGION} \
        --range=192.168.$((N+1)).0/24 &
    done
    wait

יצירת אשכול GKE

אפשר להגדיר את NeMo RL באשכול GKE Standard.

  1. יצירת אשכול Standard:

    gcloud container clusters create ${CLUSTER_NAME} \
        --location=${CONTROL_PLANE_REGION} \
        --workload-pool=${PROJECT_ID}.svc.id.goog \
        --enable-dataplane-v2 \
        --enable-ip-alias \
        --enable-multi-networking \
        --addons=RayOperator,LustreCsiDriver \
        --enable-legacy-lustre-port \
        --machine-type=${CPU_MACHINE_TYPE} \
        --num-nodes=1 \
        --min-nodes=1 \
        --max-nodes=5 \
        --enable-autoscaling \
        --network=${NETWORK}
  2. מקבלים פרטי כניסה לאשכול:

    gcloud container clusters get-credentials $CLUSTER_NAME \
        --location=$CONTROL_PLANE_REGION
  3. יוצרים את מאגר הצמתים של ה-GPU:

    gcloud container node-pools create gpu-pool \
        --cluster=${CLUSTER_NAME} \
        --location=${CONTROL_PLANE_REGION} \
        --node-locations=${NODE_ZONE} \
        --machine-type=${MACHINE_TYPE} \
        --accelerator=type=${GPU_TYPE},count=8,gpu-driver-version=DEFAULT \
        --reservation-affinity=specific \
        --reservation=${RESERVATION} \
        --enable-autoscaling \
        --num-nodes=0 \
        --total-max-nodes=2 \
        --additional-node-network=network=${GVNIC_NETWORK_PREFIX}-net,subnetwork=${GVNIC_NETWORK_PREFIX}-sub \
        --additional-node-network=network=${RDMA_NETWORK_PREFIX}-net,subnetwork=${RDMA_NETWORK_PREFIX}-sub-0 \
        --additional-node-network=network=${RDMA_NETWORK_PREFIX}-net,subnetwork=${RDMA_NETWORK_PREFIX}-sub-1 \
        --additional-node-network=network=${RDMA_NETWORK_PREFIX}-net,subnetwork=${RDMA_NETWORK_PREFIX}-sub-2 \
        --additional-node-network=network=${RDMA_NETWORK_PREFIX}-net,subnetwork=${RDMA_NETWORK_PREFIX}-sub-3 \
        --additional-node-network=network=${RDMA_NETWORK_PREFIX}-net,subnetwork=${RDMA_NETWORK_PREFIX}-sub-4 \
        --additional-node-network=network=${RDMA_NETWORK_PREFIX}-net,subnetwork=${RDMA_NETWORK_PREFIX}-sub-5 \
        --additional-node-network=network=${RDMA_NETWORK_PREFIX}-net,subnetwork=${RDMA_NETWORK_PREFIX}-sub-6 \
        --additional-node-network=network=${RDMA_NETWORK_PREFIX}-net,subnetwork=${RDMA_NETWORK_PREFIX}-sub-7
  4. מתקינים את תוכנת ההתקנה של NCCL RDMA:

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/64f0eeb0dc1d980eaef8939584f832bb10e3d482/gpudirect-rdma/nccl-rdma-installer.yaml

הגדרת מיפוי רשת

  1. שומרים את המניפסט הבא בתור network-mapping.yaml:

    # Copyright 2026 Google LLC. All rights reserved.
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    apiVersion: networking.gke.io/v1
    kind: GKENetworkParamSet
    metadata:
      name: gvnic-1
    spec:
      vpc: ${GVNIC_NETWORK_PREFIX}-net
      vpcSubnet: ${GVNIC_NETWORK_PREFIX}-sub
      deviceMode: NetDevice
    ---
    apiVersion: networking.gke.io/v1
    kind: Network
    metadata:
      name: gvnic-1
    spec:
      type: "Device"
      parametersRef:
        group: networking.gke.io
        kind: GKENetworkParamSet
        name: gvnic-1
    ---
    apiVersion: networking.gke.io/v1
    kind: GKENetworkParamSet
    metadata:
      name: rdma-0
    spec:
      vpc: ${RDMA_NETWORK_PREFIX}-net
      vpcSubnet: ${RDMA_NETWORK_PREFIX}-sub-0
      deviceMode: RDMA
    ---
    apiVersion: networking.gke.io/v1
    kind: Network
    metadata:
      name: rdma-0
    spec:
      type: "Device"
      parametersRef:
        group: networking.gke.io
        kind: GKENetworkParamSet
        name: rdma-0
    ---
    apiVersion: networking.gke.io/v1
    kind: GKENetworkParamSet
    metadata:
      name: rdma-1
    spec:
      vpc: ${RDMA_NETWORK_PREFIX}-net
      vpcSubnet: ${RDMA_NETWORK_PREFIX}-sub-1
      deviceMode: RDMA
    ---
    apiVersion: networking.gke.io/v1
    kind: Network
    metadata:
      name: rdma-1
    spec:
      type: "Device"
      parametersRef:
        group: networking.gke.io
        kind: GKENetworkParamSet
        name: rdma-1
    ---
    apiVersion: networking.gke.io/v1
    kind: GKENetworkParamSet
    metadata:
      name: rdma-2
    spec:
      vpc: ${RDMA_NETWORK_PREFIX}-net
      vpcSubnet: ${RDMA_NETWORK_PREFIX}-sub-2
      deviceMode: RDMA
    ---
    apiVersion: networking.gke.io/v1
    kind: Network
    metadata:
      name: rdma-2
    spec:
      type: "Device"
      parametersRef:
        group: networking.gke.io
        kind: GKENetworkParamSet
        name: rdma-2
    ---
    apiVersion: networking.gke.io/v1
    kind: GKENetworkParamSet
    metadata:
      name: rdma-3
    spec:
      vpc: ${RDMA_NETWORK_PREFIX}-net
      vpcSubnet: ${RDMA_NETWORK_PREFIX}-sub-3
      deviceMode: RDMA
    ---
    apiVersion: networking.gke.io/v1
    kind: Network
    metadata:
      name: rdma-3
    spec:
      type: "Device"
      parametersRef:
        group: networking.gke.io
        kind: GKENetworkParamSet
        name: rdma-3
    ---
    apiVersion: networking.gke.io/v1
    kind: GKENetworkParamSet
    metadata:
      name: rdma-4
    spec:
      vpc: ${RDMA_NETWORK_PREFIX}-net
      vpcSubnet: ${RDMA_NETWORK_PREFIX}-sub-4
      deviceMode: RDMA
    ---
    apiVersion: networking.gke.io/v1
    kind: Network
    metadata:
      name: rdma-4
    spec:
      type: "Device"
      parametersRef:
        group: networking.gke.io
        kind: GKENetworkParamSet
        name: rdma-4
    ---
    apiVersion: networking.gke.io/v1
    kind: GKENetworkParamSet
    metadata:
      name: rdma-5
    spec:
      vpc: ${RDMA_NETWORK_PREFIX}-net
      vpcSubnet: ${RDMA_NETWORK_PREFIX}-sub-5
      deviceMode: RDMA
    ---
    apiVersion: networking.gke.io/v1
    kind: Network
    metadata:
      name: rdma-5
    spec:
      type: "Device"
      parametersRef:
        group: networking.gke.io
        kind: GKENetworkParamSet
        name: rdma-5
    ---
    apiVersion: networking.gke.io/v1
    kind: GKENetworkParamSet
    metadata:
      name: rdma-6
    spec:
      vpc: ${RDMA_NETWORK_PREFIX}-net
      vpcSubnet: ${RDMA_NETWORK_PREFIX}-sub-6
      deviceMode: RDMA
    ---
    apiVersion: networking.gke.io/v1
    kind: Network
    metadata:
      name: rdma-6
    spec:
      type: "Device"
      parametersRef:
        group: networking.gke.io
        kind: GKENetworkParamSet
        name: rdma-6
    ---
    apiVersion: networking.gke.io/v1
    kind: GKENetworkParamSet
    metadata:
      name: rdma-7
    spec:
      vpc: ${RDMA_NETWORK_PREFIX}-net
      vpcSubnet: ${RDMA_NETWORK_PREFIX}-sub-7
      deviceMode: RDMA
    ---
    apiVersion: networking.gke.io/v1
    kind: Network
    metadata:
      name: rdma-7
    spec:
      type: "Device"
      parametersRef:
        group: networking.gke.io
        kind: GKENetworkParamSet
        name: rdma-7
  2. החלת המניפסט:

    envsubst < network-mapping.yaml | kubectl apply -f -

הכנת האחסון

בקטע הזה יוצרים מכונה של Managed Lustre, שמקצה את האחסון בביצועים גבוהים שנדרש לעומס העבודה של ה-RL.

  1. מקצים טווח כתובות IP לגישה לשירותים פרטיים:

    gcloud compute addresses create ${LUSTRE_NAME}-range \
        --global --purpose=VPC_PEERING \
        --prefix-length=20 --network=${NETWORK}
  2. חיבור הקישוריות:

    gcloud services vpc-peerings connect \
        --service=servicenetworking.googleapis.com \
        --ranges=${LUSTRE_NAME}-range \
        --network=${NETWORK}
  3. יצירת מכונה של Managed Lustre:

    gcloud lustre instances create ${LUSTRE_NAME} \
        --per-unit-storage-throughput=500 \
        --capacity-gib=18000 \
        --filesystem=lustrefs \
        --location=${LUSTRE_ZONE} \
        --network=projects/${PROJECT_ID}/global/networks/${NETWORK} \
        --gke-support-enabled
  4. גישה למכונה קיימת של Managed Lustre באמצעות מנהל התקן ה-CSI של Managed Lustre.

    1. מחפשים את כתובת ה-IP של מכונת Managed Lustre.

      export LUSTRE_IP=$(gcloud lustre instances describe ${LUSTRE_NAME} \
          --location=${LUSTRE_ZONE} --format="value(mountPoint)" | awk -F'@' '{print $1}')
    2. שומרים את קובץ המניפסט הבא בשם lustre-pv.yaml:

      # Copyright 2026 Google LLC
      #
      # Licensed under the Apache License, Version 2.0 (the "License");
      # you may not use this file except in compliance with the License.
      # You may obtain a copy of the License at
      #
      #     http://www.apache.org/licenses/LICENSE-2.0
      #
      # Unless required by applicable law or agreed to in writing, software
      # distributed under the License is distributed on an "AS IS" BASIS,
      # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      # See the License for the specific language governing permissions and
      # limitations under the License.
      
      apiVersion: v1
      kind: PersistentVolume
      metadata:
        name: lustre-pv
      spec:
        storageClassName: lustre-rwx-500mbps-per-tib
        capacity:
          storage: 18000Gi
        accessModes:
          - ReadWriteMany
        persistentVolumeReclaimPolicy: Retain
        volumeMode: Filesystem
        claimRef:
          namespace: default
          name: lustre-pvc
        csi:
          driver: lustre.csi.storage.gke.io
          volumeHandle: "${PROJECT_ID}/${LUSTRE_ZONE}/${LUSTRE_NAME}"
          volumeAttributes:
            ip: ${LUSTRE_IP}
            filesystem: lustrefs
    3. מחילים את המניפסט:

      envsubst < lustre-pv.yaml | kubectl apply -f -
    4. שומרים את קובץ המניפסט הבא בשם lustre-pvc.yaml:

      # Copyright 2026 Google LLC
      #
      # Licensed under the Apache License, Version 2.0 (the "License");
      # you may not use this file except in compliance with the License.
      # You may obtain a copy of the License at
      #
      #     http://www.apache.org/licenses/LICENSE-2.0
      #
      # Unless required by applicable law or agreed to in writing, software
      # distributed under the License is distributed on an "AS IS" BASIS,
      # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      # See the License for the specific language governing permissions and
      # limitations under the License.
      
      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        name: lustre-pvc
      spec:
        accessModes:
          - ReadWriteMany
        storageClassName: lustre-rwx-500mbps-per-tib
        volumeName: lustre-pv
        resources:
          requests:
            storage: 18000Gi
    5. מחילים את המניפסט:

      kubectl apply -f lustre-pvc.yaml

פריסת RayCluster

בקטע הזה משכפלים את מאגר הדוגמאות, מכינים את קובצי המניפסט ומפריסים את אשכול Ray:

  1. משכפלים את המאגר לדוגמה ומבצעים checkout של הקומיט המאומת:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git
    git -C kubernetes-engine-samples checkout 87fa0575c977b96f6d3af2cab58c9128f3cf0dc3
  2. מנווטים לספריית העבודה:

    cd kubernetes-engine-samples/ai-ml/nemo-rl-on-gke/nemoRL
  3. בודקים את המניפסט של values.yaml:

    # Copyright 2026 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    image:
      repository: "nvcr.io/nvidia/nemo-rl"
      tag: "v0.5.0" 
      pullPolicy: Always
    
    nameOverride: "kuberay"
    fullnameOverride: ""
    
    common:
      containerEnv: {}
    
    configMap:
      fluentbit:
        data:
          fluent-bit.conf: |
            [INPUT]
                Name              tail
                Path              /tmp/ray/session_latest/logs/worker-*
                Tag               ray-worker
            [INPUT]
                Name              tail
                Path              /tmp/ray/session_latest/logs/raylet*
                Tag               raylet
            [INPUT]
                Name              tail
                Path              /tmp/ray/session_latest/logs/*
                Exclude_Path      /tmp/ray/session_latest/logs/debug_state.txt,/tmp/ray/session_latest/logs/raylet*,/tmp/ray/session_latest/logs/worker-*
                Tag               ray-misc
            [OUTPUT]
                Name              stackdriver
                Match             *
                resource          gce_instance
                labels_key        labels
    
    # --- Head Node Configuration ---
    head:
      enableInTreeAutoscaling: false
      serviceAccountName: ""
      rayStartParams:
        dashboard-host: '0.0.0.0'
      template:
        metadata:
          annotations:
            gke-gcsfuse/volumes: "true"
            networking.gke.io/default-interface: 'eth0'
      containerEnv:
      - name: RAY_GROUP
        value: "head"
      nodeSelector:
        cloud.google.com/gke-nodepool: default-pool
      resources:
        limits:
          cpu: "64"
          memory: "500G"
          nvidia.com/gpu: 0
        requests:
          cpu: "64"
          memory: "500G"
          nvidia.com/gpu: 0
      tolerations:
        # - operator: "Exists"
        #   key: "components.gke.io/gke-managed-components"
        # - key: "nvidia.com/gpu"
        #   operator: "Exists"
        #   effect: "NoSchedule"
      volumeMounts:
        - mountPath: /data
          name: lustre-data
    
      volumes:
        - name: log-volume
          emptyDir: {}
        - name: fluentbit-config-volume
          configMap:
            name: "ray-cluster-kuberay-fluentbit-config"
        - name: lustre-data
          persistentVolumeClaim:
            claimName: lustre-pvc
      sidecarContainers:
        - name: fluent-bit
          image: fluent/fluent-bit:latest
          env:
          - name: RAY_GROUP
            value: "head"
          volumeMounts:
            - name: fluentbit-config-volume
              mountPath: /fluent-bit/etc/
            - mountPath: /tmp/ray
              name: log-volume
    
      # --- HEAD POD STARTUP SCRIPT ---
      command:
        - "bash"
        - "-c"
        - |
          set -ex
          echo "--- Head Pod Setup ---"
          apt-get update
          apt-get install -y sudo netcat-openbsd pciutils
          cd /opt/nemo-rl
          /usr/bin/python -m pip install uv
          /usr/bin/python -m uv venv
          echo "Head pod setup complete. Starting Ray..."
    
          exec ${KUBERAY_GEN_RAY_START_CMD}
    
      args: []
      headService: {}
      # nodeSelector:
      #   cloud.google.com/gke-accelerator: nvidia-b200 #cloud.google.com/gke-nodepool: cpu-node-pool-llama #cpu-node-pool
    
    # --- Default Worker (Disabled) ---
    worker:
      disabled: true
    
    # --- A4 GPU Worker Groups ---
    additionalWorkerGroups:
      worker-grp-0:
        disabled: false
        replicas: 4
        annotations:
          networking.gke.io/default-interface: 'eth0'
          networking.gke.io/interfaces: |
            [
              {"interfaceName":"eth0","network":"default"},
              {"interfaceName":"eth1","network":"gvnic-1"},
              {"interfaceName":"eth2","network":"rdma-0"},
              {"interfaceName":"eth3","network":"rdma-1"},
              {"interfaceName":"eth4","network":"rdma-2"},
              {"interfaceName":"eth5","network":"rdma-3"},
              {"interfaceName":"eth6","network":"rdma-4"},
              {"interfaceName":"eth7","network":"rdma-5"},
              {"interfaceName":"eth8","network":"rdma-6"},
              {"interfaceName":"eth9","network":"rdma-7"}
            ]
        containerEnv:
          - name: RAY_GROUP
            valueFrom:
              fieldRef:
                fieldPath: metadata.labels['ray.io/group']
          - name: NCCL_NET  
            value: "gIB"
          - name: NCCL_IB_GID_INDEX
            value: "3"   
          - name: GLOO_SOCKET_IFNAME
            value: "eth0"
          - name: NCCL_CROSS_NIC
            value: "0"
          - name: NCCL_SOCKET_IFNAME
            value: "eth0"
          - name: TP_SOCKET_IFNAME # Specific to DTensor/PyTorch Distributed
            value: "eth0"
          - name: NCCL_TUNER_CONFIG_PATH
            value: "/usr/local/gib/configs/tuner_config_a4.txtpb"
          - name: NCCL_NET_GDR_LEVEL
            value: "PIX"
          - name: LD_LIBRARY_PATH
            value: /usr/local/nvidia/lib64
        resources:
          limits:
            nvidia.com/gpu: 8
            cpu: "206"
            memory: "2400Gi"
          requests:
            nvidia.com/gpu: 8
            cpu: "206"
            memory: "2400Gi"
    
        nodeSelector:
          cloud.google.com/gke-accelerator: nvidia-b200
        tolerations:
          - operator: "Exists"
            key: "nvidia.com/gpu"
          - operator: "Exists"
            key: "cloud.google.com/impending-node-termination"
          - operator: "Exists"
            key: "user-workload"
        securityContext:
          privileged: true
        volumes:
          - name: log-volume
            emptyDir: {}
          - name: shared-memory
            emptyDir:
              medium: "Memory"
              sizeLimit: 240Gi
          - name: ray-tmp
            emptyDir:
              medium: "Memory"
          - name: fluentbit-config-volume
            configMap:
              name: "ray-cluster-kuberay-fluentbit-config"
          - name: nvidia-install-dir-host
            hostPath:
              path: /home/kubernetes/bin/nvidia
          - name: gib-nccl-plugin-volume
            hostPath: 
              path: /home/kubernetes/bin/gib
          - name: lustre-data
            persistentVolumeClaim:
              claimName: lustre-pvc
        volumeMounts:
          - mountPath: /tmp/ray
            name: log-volume
          - name: shared-memory
            mountPath: /dev/shm
          - name: nvidia-install-dir-host
            mountPath: /usr/local/nvidia
          - name: gib-nccl-plugin-volume
            mountPath: /usr/local/gib
          - mountPath: /data
            name: lustre-data   
        # --- WORKER POD STARTUP SCRIPT ---
        command:
          - "bash"
          - "-c"
          - |
            set -ex
    
            echo "--- Worker Pod Setup ---"
            apt-get update
            apt-get install -y sudo netcat-openbsd pciutils
            cd /opt/nemo-rl
            /usr/bin/python -m pip install uv
            /usr/bin/python -m uv venv
    
            ldconfig /usr/local/nvidia/lib64/
            ldconfig -p | grep libcuda | sed 's/^/  /'
            export LD_LIBRARY_PATH="/usr/local/gib/lib64:$LD_LIBRARY_PATH"
            source /usr/local/gib/scripts/set_nccl_env.sh
    
            echo "Worker pod setup complete. Starting Ray..."
    
            exec ${KUBERAY_GEN_RAY_START_CMD}
    
    
        sidecarContainers:
          - name: fluent-bit
            env:
              - name: RAY_GROUP
                valueFrom:
                  fieldRef:
                    fieldPath: metadata.labels['ray.io/group']
            image: fluent/fluent-bit:latest
            volumeMounts:
              - name: fluentbit-config-volume
                mountPath: /fluent-bit/etc/
              - mountPath: /tmp/ray
                name: log-volume
    
    # --- Service Config ---
    service:
      type: ClusterIP
    

    מחליפים את NCCL_TUNER_CONFIG_PATH באחד מהערכים הבאים, בהתאם למאיץ שבו משתמשים במדריך הזה:

    • NVIDIA B200 (180 GB): /usr/local/gib/configs/tuner_config_a4.txtpb
    • ‫NVIDIA H200‏ (141 GB): /usr/local/gib/configs/tuner_config_a3u.txtpb

    במניפסט הזה, צומת הראש מנהל את העבודה ומארח את לוח הבקרה של Ray. צמתי ה-worker מריצים את משימות האימון.

  4. פורסים את אשכול Ray:

    export REPLICA_COUNT=2
    helm install ray-cluster . \
      --set additionalWorkerGroups.worker-grp-0.replicas=$REPLICA_COUNT \
      --set head.resources.requests.cpu=12 \
      --set head.resources.requests.memory=96Gi \
      --set head.resources.limits.cpu=12 \
      --set head.resources.limits.memory=96Gi

    במדריך הזה משתמשים בשני צמתי עובד. אם רוצים לשנות את מספר צמתי העובדים, משנים את הערך של REPLICA_COUNT.

  5. מוודאים שה-Pods שמארחים את הצמתים הראשיים והמשניים של Ray פועלים:

    kubectl get pods

    הפלט אמור להיראות כך:

    NAME                                          READY STATUS RESTARTS AGE
    ray-cluster-kuberay-head-sw7dp                2/2   Running 0      33h
    ray-cluster-kuberay-worker-grp-0-worker-gkbxw 2/2   Running 0      33h
    ray-cluster-kuberay-worker-grp-0-worker-kdg62 2/2   Running 0      33h
    

    יכולות לעבור כ-10 דקות עד שה-Pods יתחילו לפעול.

  6. מוודאים שאשכול Ray פועל:

    kubectl ray get cluster

    הפלט אמור להיראות כך:

    NAME                 NAMESPACE DESIRED WORKERS AVAILABLE WORKERS CPUS GPUS TPUS MEMORY CONDITION STATUS AGE
    ray-cluster-kuberay  default   2       2           618     17   0    1573741824k RayClusterProvisioned ready 33h
    

הפעלת משימת ה-GRPO

אחרי שאשכול Ray מוכן, אתם יכולים לשלוח משימת Ray לאשכול Ray שפועל ב-GKE. ‫NeMo RL מוריד את המודל באופן אוטומטי במהלך ההפעלה של משימת האימון של RL.

כדי לשלוח Ray Job, צריך להתחיל סשן אינטראקטיבי כדי להריץ את ה-Job.

  1. כדי ליצור חיבור מקומי לאשכול Ray, מריצים את הפקודה הזו:

    kubectl ray session ray-cluster-kuberay

    הפקודה הזו מפעילה העברה ליציאה אחרת בין המכונה המקומית לבין הצומת הראשי של Ray באשכול GKE. שימו לב שהטרמינל יהיה תפוס בזמן שהסשן פעיל. כדי להמשיך, צריך לפתוח מכונה נפרדת של הטרמינל.

  2. בטרמינל נפרד, עוברים אל kubernetes-engine-samples/ai-ml/nemo-rl-on-gke/nemoRL/gemma3-27b-it ועורכים את הקובץ gemma3-27b-gsm8k.sh:

    # Copyright 2026 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    #!/bin/bash
    WANDB_API_KEY='YOUR_WANDB_API_KEY' # Update this with your WANDB API key
    HF_TOKEN='YOUR_HF_TOKEN' # Update this with your HF token
    WORLD_SIZE=16
    
    # --- Step 1: Find the Ray Head Pod ---
    echo "Finding Ray head pod..."
    export HEAD_POD_NAME=$(kubectl get pods --selector=ray.io/node-type=head -o jsonpath='{.items[0].metadata.name}')
    if [ -z "$HEAD_POD_NAME" ]; then
        echo "Error: No running Ray head pod found. Please check your cluster."
        exit 1
    fi
    echo "Found head pod: $HEAD_POD_NAME"
    echo ""
    
    # --- Step 2: Define the Job Script to Run ---
    # This is the script that will be executed *inside* the head pod.
    # It assumes the 'uv venv' setup from the values.yaml is already done.
    JOB_SCRIPT=$(cat <<EOF
    set -ex
    
    echo "--- Running on Ray Head Pod ($HOSTNAME) ---"
    cd /opt/nemo-rl
    
    git pull && git checkout main
    
    sed -i 's/subset: Optional\[str\] = None/subset: Optional[str] = "main"/' /opt/nemo-rl/nemo_rl/data/datasets/response_datasets/response_dataset.py
    sed -i 's/raw_dataset = load_dataset(data_path)/raw_dataset = load_dataset(data_path, "main")/' /opt/nemo-rl/nemo_rl/data/datasets/utils.py
    
    echo "Setting environment variables..."
    export WANDB_API_KEY=$WANDB_API_KEY
    export HF_TOKEN=$HF_TOKEN
    export HF_HOME=/opt/nemo-rl/
    
    ###-----Example to launch Gemma3-27B on 2 nodes (16 GPUs)----------
    uv run python examples/run_grpo_math.py \
      --config examples/configs/recipes/llm/grpo-gemma3-27b-it-8n4g-fsdp2tp4-actckpt-long.yaml \
      cluster.num_nodes=2 \
      cluster.gpus_per_node=8 \
      grpo.max_num_steps=10 \
      checkpointing.checkpoint_dir=/data/nemo_rl_gemma3_27b_3_17 \
      data.dataset_name=ResponseDataset \
      +data.train_data_path=openai/gsm8k \
      +data.val_data_path=openai/gsm8k \
      +data.val_split=test \
      +data.train_split=train \
      +data.subset="main" \
      +data.input_key="question" \
      +data.output_key="answer" \
      logger.tensorboard_enabled=False \
      logger.wandb_enabled=True \
      logger.wandb.name='nemo_rl_gemma3_27b_3_17' \
      grpo.num_prompts_per_step=16 \
      grpo.num_generations_per_prompt=32 \
      policy.generation.colocated.enabled=False \
      policy.generation.colocated.resources.num_nodes=1 \
      policy.generation.colocated.resources.gpus_per_node=8 \
      policy.generation.vllm_cfg.tensor_parallel_size=8 \
      policy.generation.vllm_cfg.gpu_memory_utilization=0.9 \
      policy.dtensor_cfg.tensor_parallel_size=8
    
    echo "--- Job Finished ---"
    EOF
    )
    
    # --- Step 3: Execute the Job ---
    echo "Submitting job to $HEAD_POD_NAME..."
    echo "$JOB_SCRIPT" | tr -d '\r' | kubectl exec -i $HEAD_POD_NAME -c ray-head -- /bin/bash
    
    echo ""
    echo "Job submission complete."
    

    מחליפים את הערכים הבאים בקובץ gemma3-27b-gsm8k.sh:

    • ‫YOUR_WANDB_API_KEY: מפתח ה-API של WandB.
    • ‫YOUR_HF_TOKEN: האסימון שלכם ב-Hugging Face.

    בקובץ הזה אפשר לראות את ההגדרה להפעלת משימה עם מודל gemma3-27b-it במערך הנתונים GSM8k. כדי להשלים את צינור ההכשרה של GRPO, הסקריפט הזה מגדיר את הפרמטרים הבאים:

    • ‫num_prompts_per_step: 16 ו-num_generations_per_prompt: 32: מודל Gemma3-27b-it יוצר קבוצה גדולה של תשובות לכל הנחיה. במקרה כזה, המודל יפיק 512 תשובות (16 × 32 = 512).
    • ‫policy.generation.colocated.enabled=False: הפרמטר הזה משבית את התכונה של יצירת תשובות באותו מיקום, כלומר המודל לא יוצר תשובות באותו צומת שבו מתבצע תהליך האימון. ב-RL רגיל, אותם מעבדי GPU מטפלים גם באימון וגם ביצירה. בהגדרה הזו של NeMo RL, מקצים צמתים ספציפיים (שמנוהלים באמצעות הפרמטר policy.generation.colocated.resources) רק להסקת מסקנות של vLLM, בעוד ששאר האשכול מתמקד במתמטיקה של אימון כבד. הפרדה בין עומסי העבודה האלה מונעת תחרות על משאבים בין מאגרי האימון שדורשים הרבה זיכרון לבין עומסי העבודה של ההסקה שדורשים הרבה כוח מחשוב.
  3. בחלון הטרמינל הראשוני, שולחים את המשימה:

    bash gemma3-27b-it/gemma3-27b-gsm8k.sh

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

מעקב אחרי התקינות של משימת ה-GRPO

אחרי שמשימת Ray מסתיימת, NeMo RL שומר את נקודות הבדיקה בנתיב שהוגדר.

  1. כדי לבדוק את הפלט של עבודת ה-GRPO, יוצרים סשן SSH למאגר ray-head:

    kubectl exec -it $(kubectl get pods -l ray.io/node-type=head -o name) -c ray-head -- bash
  2. מתקינים את כלי העץ apt בטרמינל של מאגר התגים ray-head:

    apt update && apt install -y tree
  3. מציגים את מבנה הספרייה של מאגר התגים ray-head:

    tree /data/nemo_rl_gemma3_27b_3_17/

    הפלט אמור להיראות כך:

    root@ray-cluster-kuberay-worker-grp-0-worker-gkbxw:/opt/nemo-rl# tree /data/nemo_rl_gemma3_27b_3_17/
    /data/nemo_rl_gemma3_27b_3_17/
    `-- step_10
        |-- config.yaml
        |-- policy
        |   |-- optimizer
        |   |   |-- __0_0.distcp
        |   |   |-- __10_0.distcp
        |   |   |-- __11_0.distcp
        |   |   |-- __12_0.distcp
        |   |   |-- __13_0.distcp
        |   |   |-- __14_0.distcp
        |   |   |-- __15_0.distcp
        |   |   |-- __1_0.distcp
        |   |   |-- __2_0.distcp
        |   |   |-- __3_0.distcp
        |   |   |-- __4_0.distcp
        |   |   |-- __5_0.distcp
        |   |   |-- __6_0.distcp
        |   |   |-- __7_0.distcp
        |   |   |-- __8_0.distcp
        |   |   `-- __9_0.distcp
        |   |-- tokenizer
        |   |   |-- chat_template.jinja
        |   |   |-- special_tokens_map.json
        |   |   |-- tokenizer.json
        |   |   `-- tokenizer_config.json
        |   `-- weights
        |       |-- __0_0.distcp
        |       |-- __10_0.distcp
        |       |-- __11_0.distcp
        |       |-- __12_0.distcp
        |       |-- __13_0.distcp
        |       |-- __14_0.distcp
        |       |-- __15_0.distcp
        |       |-- __1_0.distcp
        |       |-- __2_0.distcp
        |       |-- __3_0.distcp
        |       |-- __4_0.distcp
        |       |-- __5_0.distcp
        |       |-- __6_0.distcp
        |       |-- __7_0.distcp
        |       |-- __8_0.distcp
        |       `-- __9_0.distcp
        |-- train_dataloader.pt
        `-- training_info.json
    
    6 directories, 39 files
    

הסרת המשאבים

כדי להימנע מחיובים בחשבון Google Cloud על המשאבים שבהם השתמשתם במדריך הזה, אתם יכולים למחוק את המשאבים הספציפיים או את הפרויקט שמכיל אותם.

מחיקת המשאבים

  1. מחיקת אשכול Slurm:

    helm delete ray-cluster
  2. מוחקים את אשכול GKE:

    gcloud container clusters delete ${CLUSTER_NAME} \
        --location=${CONTROL_PLANE_REGION} \
        --quiet || true
  3. מוחקים את מערכת הקבצים של Lustre:

    gcloud lustre instances delete ${LUSTRE_NAME} \
        --location=${LUSTRE_ZONE} \
        --quiet || true
  4. מחיקת קישור בין רשתות VPC שכנות (peering):

    gcloud services vpc-peerings delete \
        --service=servicenetworking.googleapis.com \
        --network=${NETWORK} \
        --quiet || true
  5. מוחקים את טווח כתובות ה-IP הפרטיות של Lustre:

    gcloud compute addresses delete ${LUSTRE_NAME}-range --global --quiet || true
  6. מחיקת תת-רשתות של RDMA ו-gVNIC:

    gcloud compute networks subnets delete ${GVNIC_NETWORK_PREFIX}-sub \
        --region=${CONTROL_PLANE_REGION} \
        --quiet || true
    
    for N in $(seq 0 7); do
      gcloud compute networks subnets delete ${RDMA_NETWORK_PREFIX}-sub-$N \
        --region=${CONTROL_PLANE_REGION} --quiet || true &
    done
    wait
  7. מחיקת הכללים של חומת האש והרשתות:

    echo "[$(date)] ========== Deleting firewall rules and networks =========="
    
    NETWORKS=(
        "${RDMA_NETWORK_PREFIX}-net"
        "${GVNIC_NETWORK_PREFIX}-net"
        "${NETWORK}"
    )
    
    for NW in "${NETWORKS[@]}"; do
    
      # Skip if empty, named 'default', or not starting with a lowercase letter (invalid resource name)
      if [[ "${NW}" == "default" || ! "${NW}" =~ ^[a-z] ]]; then
        echo "Skipping protected/invalid network: '${NW}'"
        continue
      fi
    
      echo "========== Deleting firewall rules for ${NW} =========="
      while true; do
          rules=$(gcloud compute firewall-rules list \
            --filter="network:${NW}" \
            --format="value(name)" \
            --project="${PROJECT_ID}")
    
            if [[ -z "${rules}" ]]; then
              echo "No firewall rules remain for ${NW}"
              break
            fi
    
            for rule in ${rules}; do
              echo "Deleting firewall rule ${rule}..."
              gcloud compute firewall-rules delete "${rule}" --project="${PROJECT_ID}" --quiet || true
            done
    
            sleep 3
          done
    
          echo "[$(date)] ========== Deleting network ${NW} =========="
          gcloud compute networks delete ${NW} --quiet || true
    done

מחיקת הפרויקט

כדי למחוק פרויקט ב- Google Cloud :

gcloud projects delete PROJECT_ID

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