Bestärkendes Lernen mit NVIDIA NeMo RL in GKE optimieren und skalieren

In dieser Anleitung erfahren Sie, wie Sie eine verteilte Trainingsumgebung für Reinforcement Learning (RL) in Google Kubernetes Engine (GKE) orchestrieren. Sie verwenden Ray und das NVIDIA NeMo RL-Framework, um eine verteilte Trainingsumgebung zum Feinabstimmen eines Modells einzurichten.

In dieser Anleitung geht es um die GRPO-Trainingspipeline (Group Relative Policy Optimization) in GKE mit Ray und NeMo RL. GRPO ist ein Reinforcement-Learning-Algorithmus, der die Fähigkeit eines Modells zum logischen Denken verbessern soll. Dieser speichereffiziente Algorithmus vereinfacht den RL-Prozess, indem er den Critic oder das Wertmodell eliminiert und stattdessen eine relative gruppenbasierte Berechnung verwendet.

Bevor Sie diese Anleitung durcharbeiten, sollten Sie die Anleitung Verstärkungslernen mit verl in GKE optimieren und skalieren durcharbeiten. In der folgenden Anleitung wird dieselbe Clusterkonfiguration wie in der Anleitung zum Feinabstimmen und Skalieren von RL mit Verl verwendet.

Hintergrund

In den folgenden Abschnitten erhalten Sie einen kurzen Überblick über die in dieser Anleitung verwendeten Konzepte.

Bestärkendes Lernen (Reinforcement Learning, RL)

Beim bestärkenden Lernen werden Modelle durch Erfahrung, Erkundung und Feedback trainiert, anstatt durch statische Imitation. Beim Vortraining wird einem Modell beigebracht, was es sagen soll. Beim bestärkenden Lernen durch menschliches Feedback (Reinforcement Learning from Human Feedback, RLHF) wird ihm beigebracht, wie es hilfreich, sicher und logisch sein kann. RL dient als Brücke zwischen einem Basismodell und einem feinabgestimmten Modell für einen speziellen Anwendungsfall.

Weitere Informationen finden Sie unter Was ist bestärkendes Lernen?

Gruppenrelative Richtlinienoptimierung (Group Relative Policy Optimization, GRPO)

GRPO, ein von DeepSeek entwickelter Algorithmus, bietet eine speichereffiziente Alternative zur Proximal Policy Optimization (PPO) für die LLM-Abstimmung, da das Critic-Modell entfernt wird. Anstelle eines Critic-Netzwerks generiert GRPO eine Gruppe von Antworten für denselben Prompt und verwendet die durchschnittliche Belohnung dieser Gruppe als Baseline.

Weitere Informationen finden Sie unter GRPO.

NVIDIA NeMo RL

NeMo RL ist die Open-Source-Bibliothek von NVIDIA für das Post-Training, die für skalierbares RL entwickelt wurde. NeMo RL ist Teil des umfassenderen NeMo-Framework-Ökosystems und ermöglicht sowohl kleine Experimente auf einer einzelnen GPU als auch Multi-Node-Bereitstellungen auf Tausenden von GPUs.

Weitere Informationen finden Sie unter NVIDIA NeMo RL.

GSM8k-Dataset

In dieser Anleitung verwenden Sie das GSM8k-Dataset, das 8.500 hochwertige, sprachlich vielfältige mathematische Textaufgaben für Grundschüler enthält.

Mit GSM8k und GRPO generiert das Modell n verschiedene Antworten auf dasselbe Problem. GRPO vergleicht diese Antworten mit dem Gruppendurchschnitt. Das Modell wird stärker für Pfade belohnt, die im Vergleich zum Rest der Gruppe durchgehend richtig und logisch sind. Im Laufe der Zeit lernt das Modell, dass die klare Formulierung seiner Schritte die zuverlässigste Methode ist, um die Belohnung zu maximieren. Dadurch wird die Belohnung für Antworten mit geringer Leistung effektiv reduziert.

Weitere Informationen finden Sie unter GSM8k.

Ziele

In dieser Anleitung erfahren Sie, wie Sie RL in GKE mit NeMo RL einrichten. Dazu führen Sie die folgenden Schritte aus:

  1. Bereiten Sie Ihre Umgebung vor.
  2. GKE-Cluster mit B200- oder H200-GPUs einrichten
  3. KubeRay zum Verwalten eines verteilten Ray-Clusters konfigurieren
  4. Managed Lustre für Hochleistungsspeicher verwenden
  5. Führen Sie einen GRPO-Trainingsjob aus, der NeMo RL verwendet.

Hinweis

  • Melden Sie sich in Ihrem Google Cloud -Konto an. Wenn Sie mit Google Cloudnoch nicht vertraut sind, erstellen Sie ein Konto, um die Leistungsfähigkeit unserer Produkte in der Praxis sehen und bewerten zu können. Neukunden erhalten außerdem ein Guthaben von 300 $, um Arbeitslasten auszuführen, zu testen und bereitzustellen.
  • Installieren Sie die Google Cloud CLI.

  • Wenn Sie einen externen Identitätsanbieter (IdP) verwenden, müssen Sie sich zuerst mit Ihrer föderierten Identität in der gcloud CLI anmelden.

  • Führen Sie den folgenden Befehl aus, um die gcloud CLI zu initialisieren:

    gcloud init
  • Erstellen Sie ein Google Cloud Projekt oder wählen Sie eines aus.

    Rollen, die zum Auswählen oder Erstellen eines Projekts erforderlich sind

    • Projekt auswählen: Für die Auswahl eines Projekts ist keine bestimmte IAM-Rolle erforderlich. Sie können jedes Projekt auswählen, für das Ihnen eine Rolle zugewiesen wurde.
    • Projekt erstellen: Zum Erstellen eines Projekts benötigen Sie die Rolle „Projektersteller“ (roles/resourcemanager.projectCreator), die die Berechtigung resourcemanager.projects.create enthält. Weitere Informationen zum Zuweisen von Rollen
    • So erstellen Sie ein Google Cloud -Projekt:

      gcloud projects create PROJECT_ID

      Ersetzen Sie PROJECT_ID durch einen Namen für das Google Cloud -Projekt, das Sie erstellen.

    • Wählen Sie das von Ihnen erstellte Google Cloud Projekt aus:

      gcloud config set project PROJECT_ID

      Ersetzen Sie PROJECT_ID durch den Namen Ihres Projekts in Google Cloud .

  • Prüfen Sie, ob für Ihr Google Cloud Projekt die Abrechnung aktiviert ist.

  • Aktivieren Sie die erforderlichen APIs:

    Rollen, die zum Aktivieren von APIs erforderlich sind

    Zum Aktivieren von APIs benötigen Sie die IAM-Rolle „Service Usage-Administrator“ (roles/serviceusage.serviceUsageAdmin), die die Berechtigung serviceusage.services.enable enthält. Weitere Informationen zum Zuweisen von Rollen

    gcloud services enable container.googleapis.com storage.googleapis.com compute.googleapis.com
  • Installieren Sie die Google Cloud CLI.

  • Wenn Sie einen externen Identitätsanbieter (IdP) verwenden, müssen Sie sich zuerst mit Ihrer föderierten Identität in der gcloud CLI anmelden.

  • Führen Sie den folgenden Befehl aus, um die gcloud CLI zu initialisieren:

    gcloud init
  • Erstellen Sie ein Google Cloud Projekt oder wählen Sie eines aus.

    Rollen, die zum Auswählen oder Erstellen eines Projekts erforderlich sind

    • Projekt auswählen: Für die Auswahl eines Projekts ist keine bestimmte IAM-Rolle erforderlich. Sie können jedes Projekt auswählen, für das Ihnen eine Rolle zugewiesen wurde.
    • Projekt erstellen: Zum Erstellen eines Projekts benötigen Sie die Rolle „Projektersteller“ (roles/resourcemanager.projectCreator), die die Berechtigung resourcemanager.projects.create enthält. Weitere Informationen zum Zuweisen von Rollen
    • So erstellen Sie ein Google Cloud -Projekt:

      gcloud projects create PROJECT_ID

      Ersetzen Sie PROJECT_ID durch einen Namen für das Google Cloud -Projekt, das Sie erstellen.

    • Wählen Sie das von Ihnen erstellte Google Cloud Projekt aus:

      gcloud config set project PROJECT_ID

      Ersetzen Sie PROJECT_ID durch den Namen Ihres Projekts in Google Cloud .

  • Prüfen Sie, ob für Ihr Google Cloud Projekt die Abrechnung aktiviert ist.

  • Aktivieren Sie die erforderlichen APIs:

    Rollen, die zum Aktivieren von APIs erforderlich sind

    Zum Aktivieren von APIs benötigen Sie die IAM-Rolle „Service Usage-Administrator“ (roles/serviceusage.serviceUsageAdmin), die die Berechtigung serviceusage.services.enable enthält. Weitere Informationen zum Zuweisen von Rollen

    gcloud services enable container.googleapis.com storage.googleapis.com compute.googleapis.com
  • Weisen Sie Ihrem Nutzerkonto Rollen zu. Führen Sie den folgenden Befehl für jede der folgenden IAM-Rollen einmal aus: roles/container.admin, roles/iam.serviceAccountAdmin, roles/storage.admin

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

    Ersetzen Sie Folgendes:

    • PROJECT_ID: Ihre Projekt-ID.
    • USER_IDENTIFIER: Die Kennung für Ihr Nutzerkonto . Beispiel: myemail@example.com
    • ROLE: Die IAM-Rolle, die Sie Ihrem Nutzerkonto zuweisen.

Umgebung vorbereiten

In dieser Anleitung verwenden Sie Cloud Shell.

  1. Rufen Sie die Google Cloud Console auf.

  2. Klicken Sie oben im Google Cloud Console-Fenster auf die Schaltfläche Cloud Shell aktivieren.

  3. Legen Sie die folgenden Umgebungsvariablen fest:

    export PROJECT_ID=$(gcloud config get project)
    export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")
    export CONTROL_PLANE_LOCATION=CONTROL_PLANE_LOCATION
    export NODE_LOCATION=NODE_LOCATION
    export CLUSTER_NAME=CLUSTER_NAME
    export GPU_TYPE=GPU_TYPE
    export MACHINE_TYPE=MACHINE_TYPE
    export GKE_VERSION=GKE_VERSION
    export KSA_NAME=generic-ksa
    export NAMESPACE=default
    export GS_BUCKET=BUCKET_NAME-${PROJECT_ID}
    export HF_TOKEN=YOUR_HUGGING_FACE_TOKEN
    

    Ersetzen Sie die folgenden Werte:

    • CLUSTER_NAME: Name Ihres GKE-Clusters.
    • CONTROL_PLANE_LOCATION: die Compute Engine-Region für die GKE-Cluster-Steuerungsebene.
    • NODE_LOCATION: Der Standort für Ihre Knoten. Wählen Sie eine Zone aus, in der NVIDIA B200- oder H200-GPUs verfügbar sind.
    • GPU_TYPE: Der Beschleuniger, den Sie in der Compute Engine-Kapazitätsreservierung reserviert haben. Muss einer der folgenden Werte sein:
      • nvidia-b200: NVIDIA B200 (180 GB)
      • nvidia-h200-141gb: NVIDIA H200 (141 GB)
    • MACHINE_TYPE: der zu verwendende Maschinentyp:
      • Verwenden Sie für NVIDIA B200-GPUs (180 GB) a4-highgpu-8g oder höher.
      • Verwenden Sie für NVIDIA H200-GPUs (141 GB) a3-ultragpu-8g oder höher.
    • GKE_VERSION: Die zu verwendende GKE-Version:

      • Verwenden Sie für NVIDIA B200-GPUs (180 GB) 1.32.2-gke.1422000 oder höher.
      • Verwenden Sie für NVIDIA H200-GPUs (141 GB) 1.31.4-gke.1183000 oder höher.
    • BUCKET_NAME: Der Basisname für Ihren Cloud Storage-Bucket.

    • YOUR_HUGGING_FACE_TOKEN: Ihr Hugging Face-Token.

  4. Erstellen Sie die folgenden Umgebungsvariablen für das Netzwerk:

    export GVNIC_NETWORK_PREFIX="GVNIC-NAME"
    export RDMA_NETWORK_PREFIX="RDMA-NAME"
    

    Ersetzen Sie die folgenden Werte:

    • GVNIC-NAME: Das Präfix für den Namen des gVNIC-Netzwerks. Sie können ein beliebiges Präfix verwenden.
    • RDMA-NAME: Das Präfix für das RDMA-Netzwerk (Remote Direct Memory Access). Sie können ein beliebiges Präfix verwenden.

Infrastruktur einrichten

In diesem Abschnitt erstellen Sie VPC-Netzwerke und einen GKE-Cluster.

VPC-Netzwerk erstellen

  1. Erstellen Sie ein VPC-Netzwerk für die gVNIC-Schnittstelle:

    gcloud compute networks create ${GVNIC_NETWORK_PREFIX}-net \
        --project=${PROJECT_ID} \
        --subnet-mode=custom
    gcloud compute networks subnets create ${GVNIC_NETWORK_PREFIX}-sub \
        --network=${GVNIC_NETWORK_PREFIX}-net \
        --location=${CONTROL_PLANE_LOCATION} \
        --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. Erstellen Sie ein VPC-Netzwerk und Subnetze für RDMA, das acht Subnetze für acht GPUs enthält:

    gcloud compute networks create ${RDMA_NETWORK_PREFIX}-net \
        --network-profile=${CONTROL_PLANE_LOCATION}-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 \
        --location=${CONTROL_PLANE_LOCATION} \
        --range=192.168.$((N+1)).0/24 &
    done
    wait
    

GKE-Cluster erstellen

Sie können NeMo RL in einem GKE Autopilot- oder Standardcluster festlegen. Für eine vollständig verwaltete Kubernetes-Umgebung empfehlen wir die Verwendung eines Autopilot-Clusters. Informationen zum Auswählen des GKE-Betriebsmodus, der für Ihre Arbeitslasten am besten geeignet ist, finden Sie unter GKE-Betriebsmodi.

Autopilot

  1. Autopilot-Cluster erstellen:

    gcloud container clusters create-auto ${CLUSTER_NAME} \
        --location=${CONTROL_PLANE_LOCATION} \
        --enable-multi-networking  \
        --enable-ray-operator
    
  2. Rufen Sie Anmeldedaten für Ihren Cluster ab:

    gcloud container clusters get-credentials ${CLUSTER_NAME} \
        --location=${CONTROL_PLANE_LOCATION}
    
  3. Installieren Sie das NCCL RDMA-Installationsprogramm für Autopilot:

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/refs/heads/master/gpudirect-rdma/nccl-rdma-installer-autopilot.yaml
    

Standard

  1. Standardcluster erstellen:

    gcloud container clusters create ${CLUSTER_NAME} \
        --location=${CONTROL_PLANE_LOCATION} \
        --enable-dataplane-v2 \
        --enable-ip-alias \
        --enable-multi-networking \
        --addons=RayOperator \
        --num-nodes=1
    
  2. Rufen Sie Anmeldedaten für Ihren Cluster ab:

    gcloud container clusters get-credentials ${CLUSTER_NAME} \
        --location=${CONTROL_PLANE_LOCATION}
    
  3. Erstellen Sie den GPU-Knotenpool:

    gcloud container node-pools create gpu-pool \
        --cluster=${CLUSTER_NAME} \
        --node-locations=${NODE_LOCATION} \
        --machine-type=${MACHINE_TYPE} \
        --accelerator=type=${GPU_TYPE},count=8 \
        --spot \
        --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. Installieren Sie das NCCL RDMA-Installationsprogramm:

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

Netzwerkzuordnungen konfigurieren

  1. Speichern Sie das folgende Manifest als 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. Wenden Sie das Manifest an:

    kubectl apply -f network-mapping.yaml
    

Speicher vorbereiten

In diesem Abschnitt erstellen Sie Cloud Storage-Buckets und eine verwaltete Lustre-Instanz, die den für Ihre RL-Arbeitslast erforderlichen leistungsstarken Speicher bereitstellt.

  1. Erstellen Sie einen Cloud Storage-Bucket:

    gcloud storage buckets create gs://${GS_BUCKET} \
        --location=${CONTROL_PLANE_LOCATION} \
        --enable-hierarchical-namespace \
        --uniform-bucket-level-access
    
  2. Erstellen Sie ein Kubernetes-Dienstkonto (Kubernetes Service Account, KSA) und binden Sie es an den Bucket:

    kubectl create serviceaccount ${KSA_NAME} --namespace ${NAMESPACE}
    
    gcloud storage buckets add-iam-policy-binding gs://${GS_BUCKET} \
        --member "principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/${NAMESPACE}/sa/${KSA_NAME}" \
        --role "roles/storage.objectUser"
    
  3. Führen Sie die folgenden Schritte aus, um Managed Lustre einzurichten:

    1. Erstellen Sie eine Managed Lustre-Instanz, indem Sie der Anleitung unter Managed Lustre-Instanz erstellen folgen. Achten Sie darauf, dass die Instanz dasselbe Netzwerk wie Ihr GKE-Cluster verwendet.
    2. Greifen Sie auf die Managed Lustre-Instanz zu, indem Sie der Anleitung unter Auf eine vorhandene Managed Lustre-Instanz zugreifen folgen.

RayCluster bereitstellen

In diesem Abschnitt klonen Sie das Beispiel-Repository, bereiten die Manifeste vor und führen ein launcher.sh-Skript aus:

  1. Klonen Sie das Beispiel-Repository:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git
    cd kubernetes-engine-samples
    
  2. Wechseln Sie zum Arbeitsverzeichnis:

    cd ai-ml/nemo-rl-on-gke/nemoRL
    
  3. Prüfen Sie das values.yaml-Manifest:

    # 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"
      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
    

    Ersetzen Sie NCCL_TUNER_CONFIG_PATH durch einen der folgenden Werte, je nachdem, welchen Beschleuniger Sie in diesem Tutorial verwenden:

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

    In diesem Manifest verwaltet der Head-Knoten den Job und hostet das Ray-Dashboard. Die Trainingsjobs werden von den Worker-Knoten ausgeführt.

  4. Installieren Sie den Ray-Cluster:

    export REPLICA_COUNT=2
    helm install ray-cluster . \
      --set values.additionalWorkerGroups.worker-grp-0.replicas=$REPLICA_COUNT
    

    In dieser Anleitung verwenden Sie zwei Worker-Knoten. Wenn Sie die Anzahl der Worker-Knoten ändern möchten, ändern Sie den Wert von REPLICA_COUNT.

  5. Führen Sie das Skript launcher.sh aus, um den Ray-Cluster bereitzustellen:

    bash launcher.sh
    
  6. Prüfen Sie, ob die Worker- und Head-Knoten ausgeführt werden:

    kubectl get pods
    

    Die Ausgabe sieht etwa so aus:

    NAME                                          READY STATUS RESTARTS AGE
    ray-cluster-kuberay-head-sw7dp                3/3   Running 0      33h
    ray-cluster-kuberay-worker-grp-0-worker-gkbxw 3/3   Running 0      33h
    ray-cluster-kuberay-worker-grp-0-worker-kdg62 3/3   Running 0      33h
    
  7. Prüfen Sie, ob der Ray-Cluster ausgeführt wird:

    kubectl ray get clusters
    

    Die Ausgabe sieht etwa so aus:

    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-Job starten

Sobald Ihr Ray-Cluster bereit ist, können Sie einen Ray-Job an Ihren laufenden Ray-Cluster in GKE senden. NeMo RL lädt das Modell automatisch während der Ausführung des RL-Trainingsjobs herunter.

Wenn Sie einen Ray-Job senden möchten, starten Sie eine interaktive Sitzung, um den Job auszuführen.

  1. Führen Sie diesen Befehl aus, um eine lokale Verbindung zu Ihrem Ray-Cluster herzustellen:

      kubectl ray session ray-cluster-kuberay
    

    Mit diesem Befehl wird die Portweiterleitung zwischen Ihrem lokalen Computer und dem Ray-Head-Knoten in Ihrem GKE-Cluster initiiert. Beachten Sie, dass Ihr Terminal belegt ist, solange diese Sitzung aktiv ist. Öffnen Sie eine separate Terminalinstanz, um fortzufahren.

  2. Bearbeiten Sie die Datei 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 3 nodes (24 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=300 \
      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=64 \
      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."
    

    Ersetzen Sie die folgenden Werte in der Datei gemma3-27b-gsm8k.sh:

    • YOUR_WANDB_API_KEY: Ihr WandB-API-Schlüssel.
    • YOUR_HF_TOKEN: Ihr Hugging Face-Token.

    In dieser Datei sehen Sie die Konfiguration zum Ausführen eines Jobs mit dem Modell „gemma3-27b-it“ für den GSM8k-Datensatz. Für die GRPO-Trainingspipeline werden in diesem Skript die folgenden Parameter definiert:

    • num_prompts_per_step: 16 und num_generations_per_prompt: 64: Das Modell „Gemma3-27b-it“ generiert für jeden Prompt eine große Gruppe von Antworten. In dieser Konfiguration generiert das Modell insgesamt 1.024 Antworten (16 × 64 = 1.024).
    • policy.generation.colocated.enabled=False: Mit diesem Parameter wird die Funktion für die gemeinsame Generierung deaktiviert. Das bedeutet, dass das Modell keine Antworten im selben Knoten wie der Trainingsprozess generiert. Beim Standard-RL werden sowohl Training als auch Generierung von denselben GPUs übernommen. Bei dieser NeMo RL-Einrichtung weisen Sie bestimmte Knoten (die mit dem Parameter policy.generation.colocated.resources verwaltet werden) ausschließlich der vLLM-Inferenz zu, während sich der Rest des Clusters auf die rechenintensive Trainingsmathematik konzentriert. Durch die Trennung dieser Arbeitslasten verhindern Sie Ressourcenkonflikte zwischen den speicherintensiven Trainingspuffern und den rechenintensiven Inferenzarbeitslasten.
  3. Führen Sie den folgenden Befehl aus, um den Job zu senden:

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

    Während der Job ausgeführt wird, werden in der Ausgabe die Trainingsergebnisse, das Timing und die Leistungsmesswerte angezeigt.

Integrität des GRPO-Jobs überwachen

Nachdem Ray den Job abgeschlossen hat, speichert NeMo RL die Prüfpunkte im konfigurierten Pfad.

  1. Installieren Sie das apt-tree-Dienstprogramm:

    apt install tree
    
  2. Wenn Sie den Status des GRPO-Jobs überwachen möchten, prüfen Sie die Logs des Ray-Head-Knotens:

    kubectl exec -it $(kubectl get pods -l ray.io/node-type=head -o name) -c ray-head -- bash
    

    Die Ausgabe sieht etwa so aus:

    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
    

Bereinigen

Löschen Sie die Ressourcen, um Gebühren zu vermeiden:

helm delete ray-cluster
gcloud container clusters delete ${CLUSTER_NAME} --location=${CONTROL_PLANE_LOCATION}
gcloud storage rm -r gs://${GS_BUCKET}

Nächste Schritte