GKE で verl を使用して強化学習をファインチューニングしてスケーリングする

このチュートリアルでは、Google Kubernetes Engine(GKE)で強化学習用の分散トレーニング環境をオーケストレートする方法について説明します。Ray と verl(Volcano Engine Reinforcement Learning)フレームワークを使用して、分散トレーニング環境を設定し、GSM8K データセットで Qwen2.5-32B-Instruct モデルをファインチューニングします。

このチュートリアルでは、Ray と verl を使用した GKE でのグループ相対ポリシー最適化(GRPO)トレーニング パイプラインに焦点を当てます。GRPO は、モデルの推論能力を向上させるように設計された強化学習アルゴリズムです。このメモリ効率の高いアルゴリズムは、Critic(値モデル)を排除し、相対グループベースの計算を使用することで、強化学習(RL)プロセスを簡素化します。

このチュートリアルは、効率性を高めるためにデータ、モデルの重み、トレーニング エンジンを分離する分散トレーニング環境を設定する必要がある場合に適しています。

このチュートリアルは、次の GPU アーキテクチャをサポートしています。

  • Intel または AMD ベースの GPU ノード: NVIDIA B200 または H200 GPU を使用して設定し、スケーリングします。
  • Arm ベースの A4X(GB200)ノード: NVIDIA GB200 Grace Blackwell Superchip を使用して設定とスケーリングを行い、GKE 動的リソース割り当て(DRA)とマルチノード NVLink(IMEX)を使用します。

背景

以降のセクションでは、このチュートリアルで使用するコンセプトの概要を説明します。

強化学習(RL)

RL は、静的な模倣ではなく、経験、探索、フィードバックを通じてモデルを学習させます。事前トレーニングではモデルに何を言うかを教えますが、人間からのフィードバックを用いた強化学習(RLHF)では、有用性、安全性、論理性を教えます。RL は、ベースモデルと特殊なユースケースのファインチューニング済みモデルの橋渡し役として機能します。

詳細については、強化学習とはをご覧ください。

グループ相対ポリシーの最適化(GRPO)

DeepSeek によって普及したアルゴリズムである GRPO は、Critic モデルを削除することで、LLM アライメント用の Proximal Policy Optimization(PPO)に代わるメモリ効率の高い方法を提供します。Critic ネットワークの代わりに、GRPO は同じプロンプトに対する一連のレスポンスを生成し、そのグループの平均報酬をベースラインとして使用します。

詳細については、GRPO をご覧ください。

Volcano Engine Reinforcement Learning(verl)

verl は、LLM ベースの RL の複雑なメモリとコンピューティング パターンを処理するように設計された高性能フレームワークです。

詳細については、verl をご覧ください。

目標

このチュートリアルでは、次の手順に沿って verl を使用して GKE で強化学習を設定する方法について説明します。

  1. A4X(GB200 Superchip)、A4(B200 GPU)、A3 Ultra(H200 GPU)を使用して GKE クラスタを設定します。
  2. 分散 Ray クラスタを管理するように KubeRay を構成します。
  3. Cloud Storage FUSE を使用して、すべてのノードに Cloud Storage バケットをマウントします。
  4. verl を使用して GRPO トレーニング ジョブを実行し、Qwen2.5-32B-Instruct モデルを GSM8K データセットに合わせます。

始める前に

  • Google Cloud アカウントにログインします。 Google Cloudを初めて使用する場合は、 アカウントを作成して、実際のシナリオでの Google プロダクトのパフォーマンスを評価してください。新規のお客様には、ワークロードの実行、テスト、デプロイができる無料クレジット $300 分を差し上げます。
  • Google Cloud CLI をインストールします。

  • 外部 ID プロバイダ(IdP)を使用している場合は、まず連携 ID を使用して gcloud CLI にログインする必要があります。

  • gcloud CLI を初期化するには、次のコマンドを実行します。

    gcloud init
  • Google Cloud プロジェクトを作成または選択します

    プロジェクトの選択または作成に必要なロール

    • プロジェクトを選択する: プロジェクトの選択に特定の IAM ロールは必要ありません。ロールが付与されているプロジェクトであれば、どのプロジェクトでも選択できます。
    • プロジェクトを作成する: プロジェクトを作成するには、resourcemanager.projects.create 権限を含むプロジェクト作成者ロール(roles/resourcemanager.projectCreator)が必要です。詳しくは、ロールを付与する方法をご覧ください。
    • Google Cloud プロジェクトを作成します。

      gcloud projects create PROJECT_ID

      PROJECT_ID は、作成する Google Cloud プロジェクトの名前に置き換えます。

    • 作成した Google Cloud プロジェクトを選択します。

      gcloud config set project PROJECT_ID

      PROJECT_ID は、 Google Cloud プロジェクトの名前に置き換えます。

  • Google Cloud プロジェクトに対して課金が有効になっていることを確認します

  • 必要な API を有効にします。

    API を有効にするために必要なロール

    API を有効にするには、serviceusage.services.enable 権限を含む Service Usage 管理者 IAM ロール(roles/serviceusage.serviceUsageAdmin)が必要です。詳しくは、ロールを付与する方法をご覧ください。

    gcloud services enable container.googleapis.com storage.googleapis.com compute.googleapis.com
  • Google Cloud CLI をインストールします。

  • 外部 ID プロバイダ(IdP)を使用している場合は、まず連携 ID を使用して gcloud CLI にログインする必要があります。

  • gcloud CLI を初期化するには、次のコマンドを実行します。

    gcloud init
  • Google Cloud プロジェクトを作成または選択します

    プロジェクトの選択または作成に必要なロール

    • プロジェクトを選択する: プロジェクトの選択に特定の IAM ロールは必要ありません。ロールが付与されているプロジェクトであれば、どのプロジェクトでも選択できます。
    • プロジェクトを作成する: プロジェクトを作成するには、resourcemanager.projects.create 権限を含むプロジェクト作成者ロール(roles/resourcemanager.projectCreator)が必要です。詳しくは、ロールを付与する方法をご覧ください。
    • Google Cloud プロジェクトを作成します。

      gcloud projects create PROJECT_ID

      PROJECT_ID は、作成する Google Cloud プロジェクトの名前に置き換えます。

    • 作成した Google Cloud プロジェクトを選択します。

      gcloud config set project PROJECT_ID

      PROJECT_ID は、 Google Cloud プロジェクトの名前に置き換えます。

  • Google Cloud プロジェクトに対して課金が有効になっていることを確認します

  • 必要な API を有効にします。

    API を有効にするために必要なロール

    API を有効にするには、serviceusage.services.enable 権限を含む Service Usage 管理者 IAM ロール(roles/serviceusage.serviceUsageAdmin)が必要です。詳しくは、ロールを付与する方法をご覧ください。

    gcloud services enable container.googleapis.com storage.googleapis.com compute.googleapis.com
  • ユーザー アカウントにロールを付与します。次の IAM ロールごとに次のコマンドを 1 回実行します。 roles/container.admin, roles/iam.serviceAccountAdmin, roles/storage.admin

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

    次のように置き換えます。

    • PROJECT_ID: プロジェクト ID。
    • USER_IDENTIFIER: ユーザー アカウントの識別子。例: myemail@example.com
    • ROLE: ユーザー アカウントに付与する IAM ロール。

環境を準備する

このチュートリアルでは、Cloud Shell を使用します。

  1. Google Cloud コンソールに移動します。

  2. Google Cloud コンソール ウィンドウの上部にある [Cloud Shell をアクティブにする] ボタンをクリックします。

  3. 環境変数を設定します。

    export PROJECT_ID=$(gcloud config get project)
    export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")
    export CONTROL_PLANE_REGION=CONTROL_PLANE_REGION
    export NODE_ZONE=NODE_ZONE
    export CLUSTER_NAME=CLUSTER_NAME
    export KSA_NAME=KSA_NAME
    export GS_BUCKET=BUCKET_NAME-${PROJECT_ID}
    export NAMESPACE=default
    export GPU_TYPE=GPU_TYPE
    export MACHINE_TYPE=MACHINE_TYPE
    export RESERVATION=RESERVATION
    export HF_TOKEN=YOUR_HUGGING_FACE_TOKEN
    
    # A4 (B200 GPUs) or A3 Ultra (H200 GPUs) only variables
    export GVNIC_NETWORK_PREFIX=YGVNIC_NAME
    export RDMA_NETWORK_PREFIX=RDMA_NAME
    
    # A4X (GB200 Superchips) only variables
    export NUM_GPU_NODES=4
    export VERL_IMAGE=verlai/verl:vllm023.aarch64.dev1
    export VERL_REF=ddbcdb7
    

    次の値を置き換えます。

    • CONTROL_PLANE_REGION: GKE クラスタ コントロール プレーンの Compute Engine リージョン。
    • NODE_ZONE: ノードが予約されているゾーン。詳細については、GPU の可用性をご覧ください。
    • CLUSTER_NAME: GKE クラスタの名前。
    • KSA_NAME: Kubernetes サービス アカウントの名前。
    • BUCKET_NAME: Cloud Storage バケットのベース名。gs:// プレフィックスを指定する必要はありません。
    • GPU_TYPE: Compute Engine の容量予約で予約したアクセラレータ。次のいずれかの値を指定する必要があります。
      • nvidia-gb200: A4X(GB200 Superchip)
      • nvidia-b200: A4(B200 GPU)
      • nvidia-h200-141gb: A3 Ultra(H200 GPU)
    • MACHINE_TYPE: 使用するマシンのタイプ:
      • A4X(GB200 Superchip)の場合は、a4x-highgpu-4g を使用します。
      • A4(B200 GPU)の場合は、a4-highgpu-8g 以降を使用します。
      • A3 Ultra(H200 GPU)の場合は、a3-ultragpu-8g 以降を使用します。
    • RESERVATION: 容量予約の名前。
    • YOUR_HUGGING_FACE_TOKEN: Hugging Face トークン。
    • GVNIC_NAME(A4 または A3 Ultra のみ): gVNIC ネットワーク名の接頭辞。任意の接頭辞を使用できます。
    • RDMA_NAME(A4 または A3 Ultra のみ): リモート ダイレクト メモリ アクセス(RDMA)ネットワークの接頭辞。任意の接頭辞を使用できます。

インフラストラクチャを設定する

このセクションでは、標準の VPC ネットワークと GKE クラスタを作成します。

RDMA ネットワークとサブネットを作成する(A4 と A3 Ultra のみ)

このセクションは、A4 GPU と A3 Ultra GPU のみに必要です。

A4X(GB200)GPU を使用する場合は、このセクションをスキップして、GKE クラスタを作成するに直接進みます。A4X(GB200)GPU の場合、ノードプールが auto アクセラレータ ネットワーク プロファイルを使用すると、GKE はネットワークを自動的に作成します。Cluster Toolkit ブループリントは、enable_dranet:true フラグを使用してこのプロファイルを有効にします。

  1. gVNIC インターフェース用の VPC ネットワークを作成します。

    gcloud compute networks create ${GVNIC_NETWORK_PREFIX}-net \
        --subnet-mode=custom \
        --project=${PROJECT_ID}
    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. 8 個の GPU 用に 8 個のサブネットを持つ RDMA 用の VPC ネットワークとサブネットを作成します。

    gcloud beta 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
    
  3. サンプル リポジトリのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git
    cd kubernetes-engine-samples
    
  4. 作業ディレクトリに移動します。

    cd ai-ml/verl-on-gke
    

GKE クラスタを作成する

GPU アーキテクチャに対応する GKE クラスタを作成します。

A4 と A3 Ultra

使用する GKE クラスタモードを選択します。

Autopilot

  1. Autopilot クラスタを作成します。

    gcloud container clusters create-auto ${CLUSTER_NAME} \
        --location=${CONTROL_PLANE_REGION} \
        --enable-multi-networking  \
        --enable-ray-operator
    
  2. クラスタの認証情報を取得します。

    gcloud container clusters get-credentials ${CLUSTER_NAME} \
        --location=${CONTROL_PLANE_REGION}
    
  3. Autopilot 用の NCCL RDMA インストーラをインストールします。

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

標準

  1. Standard クラスタを作成します。

    gcloud container clusters create ${CLUSTER_NAME} \
        --location=${CONTROL_PLANE_REGION} \
        --enable-dataplane-v2 \
        --workload-pool=${PROJECT_ID}.svc.id.goog \
        --enable-ip-alias \
        --enable-multi-networking \
        --addons=RayOperator,GcsFuseCsiDriver \
        --machine-type=c2-standard-16 \
        --num-nodes=1 \
        --min-nodes=1 \
        --max-nodes=5 \
        --enable-autoscaling
    
  2. クラスタの認証情報を取得します。

    gcloud container clusters get-credentials ${CLUSTER_NAME} --location=${CONTROL_PLANE_REGION}
    
  3. GPU ノードプールを作成します。これらのノードプールは、予約を使用して可用性を確保します。2 つのノードから始めます。

    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=2 \
        --total-max-nodes=10 \
        --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. Standard クラスタで使用される NCCL RDMA インストーラをインストールします。

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

A4X

  1. Cluster Toolkit gke-a4x ブループリントを使用して、GKE クラスタとノードプールを作成します。ブループリントは、予約にバインドされた A4X ノードプール、アクセラレータ ネットワーク(1 つの追加の gVNIC と 4 つの RDMA レール)、CX-7 NIC を DRA デバイスとして公開するマネージド DRANET ドライバなど、GKE クラスタをプロビジョニングします。

    ブループリントのデプロイ手順に沿って、パラメータ(PROJECT_IDCONTROL_PLANE_REGIONNODE_ZONE、予約、NUM_GPU_NODES など)を構成し、クラスタをデプロイします。または、A4X GKE クラスタ作成ガイドに沿って、クラスタを手動で作成することもできます。

  2. クラスタの認証情報を取得します。

    gcloud container clusters get-credentials ${CLUSTER_NAME} --location=${CONTROL_PLANE_REGION}
    
  3. クラスタが DRA を介して RDMA NIC を公開していることを確認します。

    kubectl get deviceclasses
    

    出力には mrdma.google.com が含まれている必要があります。

  4. A4X ノードが存在することを確認します。

    kubectl get nodes -l cloud.google.com/gke-accelerator=nvidia-gb200
    
  5. gIB NCCL プラグイン(A4X バリアント)をインストールします。

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/master/gpudirect-rdma/nccl-rdma-installer-a4x.yaml
    
  6. マルチノード NVLink 用の ComputeDomain(IMEX)チャネルを提供する NVIDIA DRA ドライバをインストールします。

    helm repo add nvidia https://helm.ngc.nvidia.com/nvidia && helm repo update
    kubectl create namespace nvidia-dra-driver-gpu
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ResourceQuota
    metadata:
      name: nvidia-dra-driver-gpu-quota
      namespace: nvidia-dra-driver-gpu
    spec:
      hard:
        pods: "$((2 * NUM_GPU_NODES + 1))"
      scopeSelector:
        matchExpressions:
        - operator: In
          scopeName: PriorityClass
          values:
          - system-node-critical
          - system-cluster-critical
    EOF
    helm upgrade --install nvidia-dra-driver-gpu nvidia/nvidia-dra-driver-gpu \
      --version=25.3.1 --namespace nvidia-dra-driver-gpu \
      --set nvidiaDriverRoot=/home/kubernetes/bin/nvidia \
      --set resources.gpus.enabled=false \
      --set kubeletPlugin.tolerations[0].key=nvidia.com/gpu \
      --set kubeletPlugin.tolerations[0].operator=Exists \
      --set kubeletPlugin.tolerations[1].key=kubernetes.io/arch \
      --set kubeletPlugin.tolerations[1].operator=Exists
    
  7. ワークロード Namespace にスコープ設定された KubeRay オペレーターをインストールします。

    kubectl create namespace ${NAMESPACE}
    helm repo add kuberay https://ray-project.github.io/kuberay-helm/ && helm repo update
    helm upgrade --install kuberay-operator kuberay/kuberay-operator \
      --namespace ${NAMESPACE} \
      --set singleNamespaceInstall=true --set "watchNamespace={${NAMESPACE}}"
    

ネットワーク マッピングを構成する(A4 と A3 Ultra のみ)

この手順は、標準 GPU の設定(A4 と A3 Ultra のみ)に必要です。A4X(GB200)を使用する場合、GKE がネットワーク インターフェースを自動的に管理するため、このセクションはスキップしてください。

  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 > network-mapping-updated.yaml
    kubectl apply -f network-mapping-updated.yaml
    

データとストレージを準備する

Cloud Storage と Kubernetes のリソースを構成します。

  1. Cloud Storage バケットを作成します。

      gcloud storage buckets create gs://${GS_BUCKET} \
          --location=${CONTROL_PLANE_REGION}  \
          --enable-hierarchical-namespace \
          --uniform-bucket-level-access
    
  2. Kubernetes サービス アカウント(KSA)を作成し、バケットにバインドします。

      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. Hugging Face の Secret を作成します。

      kubectl create secret generic hf-secret --from-literal=hf_api_token=${HF_TOKEN}
    
  4. マニフェスト gcsfuse-storage.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: v1
    kind: PersistentVolume
    metadata:
      name: training-bucket-pv
    spec:
      accessModes:
      -   ReadWriteMany
      capacity:
        storage: 768Gi
      persistentVolumeReclaimPolicy: Delete
      storageClassName: gcsfuse-sc
      mountOptions:
      -   implicit-dirs
      -   metadata-cache:negative-ttl-secs:0
      -   metadata-cache:ttl-secs:0
      -   metadata-cache:stat-cache-max-size-mb:-1
      -   metadata-cache:type-cache-max-size-mb:-1
      -   file-cache:max-size-mb:-1
      -   file-cache:cache-file-for-range-read:true
      -   file-cache:enable-parallel-downloads:true
      -   read_ahead_kb=1024
      -   write:enable-streaming-writes:true
      -   write:global-max-blocks:200000
      csi:
        driver: gcsfuse.csi.storage.gke.io
        volumeHandle: ${GS_BUCKET}
        volumeAttributes:
          skipCSIBucketAccessCheck: "true"
          gcsfuseMetadataPrefetchOnMount: "true"
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: training-bucket-pvc
    spec:
      accessModes:
      -   ReadWriteMany
      resources:
        requests:
          storage: 768Gi
      storageClassName: gcsfuse-sc
    
  5. 次のようにマニフェストを適用します。

      envsubst < gcsfuse-storage.yaml > gcsfuse-storage-updated.yaml
      kubectl apply -f gcsfuse-storage-updated.yaml
    

モデルとデータを準備する

モデルの重みとデータセットを使用して Cloud Storage バケットにデータを入力します。これらのコマンドをローカルまたは GKE Pod で実行して、バケットにデータを入力できます。

  1. verl リポジトリをクローンし、仮想環境を準備して GSM8K データセットを処理します。

    git clone https://github.com/volcengine/verl.git
    git -C verl checkout ${VERL_REF}
    
    VENV_DIR=.venv
    python3 -m venv $VENV_DIR
    source $VENV_DIR/bin/activate
    pip install verl
    
    python verl/examples/data_preprocess/gsm8k.py --local_save_dir ~/data/gsm8k
    
  2. Hugging Face CLI を使用して Qwen2.5-32B-Instruct モデルをダウンロードします(これには約 66 GB のディスク容量が必要です)。

    hf download Qwen/Qwen2.5-32B-Instruct --local-dir Qwen2.5-32B-Instruct
    
  3. モデル、データ、verl コードを Cloud Storage バケットにアップロードします。

    gcloud storage cp --recursive verl gs://${GS_BUCKET}/verl
    gcloud storage cp --recursive Qwen2.5-32B-Instruct gs://${GS_BUCKET}/Qwen2.5-32B-Instruct
    gcloud storage cp --recursive ~/data/gsm8k/* gs://${GS_BUCKET}/gsm8k/
    

RayCluster カスタム リソースをデプロイする

RayCluster カスタム リソースをデプロイします。これは、1 つのシステム ヘッド Pod と複数の GPU バックアップ ワーカー Pod で構成されます。

A4 と A3 Ultra

クラスタの作成に使用した GKE クラスタモードを選択します。

Autopilot

  1. RayCluster をデプロイします。次のコードを ray-cluster-auto.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: ray.io/v1
    kind: RayCluster
    metadata:
      name: b200-ray-cluster
      annotations:
    spec:
      rayVersion: '2.47.0'
      headGroupSpec:
        rayStartParams:
          dashboard-host: '0.0.0.0'
        template:
          metadata:
            annotations:
              gke-gcsfuse/volumes: "true"
          spec:
            serviceAccountName: ${KSA_NAME}
            nodeSelector:
              cloud.google.com/gke-spot: "true"
              cloud.google.com/machine-family: "c2"
              cloud.google.com/compute-class: Performance
            containers:
            - name: ray-head
              image: verlai/verl:vllm011.latest 
              ports:
                - containerPort: 6379
                  name: gcs-server
                - containerPort: 8265
                  name: dashboard
                - containerPort: 10001
                  name: client
              resources:
                limits:
                  cpu: "12"
                  memory: "32G"
                  ephemeral-storage: "9Gi"
                requests:
                  cpu: "12"
                  memory: "32G"
                  ephemeral-storage: "9Gi"
              volumeMounts:
                - mountPath: /tmp/ray
                  name: ray-logs
                - name: training-bucket-vol
                  mountPath: /data
            volumes:
              - name: ray-logs
                emptyDir: {}
              - name: training-bucket-vol
                persistentVolumeClaim:
                  claimName: training-bucket-pvc
      workerGroupSpecs:
      - replicas: 2
        minReplicas: 2
        maxReplicas: 2
        groupName: gpu-group
        rayStartParams:
          num-cpus: "220"
        template:
          metadata:
            annotations:
              gke-gcsfuse/volumes: "true"
              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"}
                ]
          spec:
            initContainers:
            - name: verl-setup
              image: verlai/verl:vllm011.latest
              command: ["/bin/bash", "-c"]
              args:
                - |
                  echo "Performing local editable install..."
                  cd /data/verl && pip3 install --no-deps -e .
              volumeMounts:
              - name: training-bucket-vol
                mountPath: /data
            serviceAccountName: ${KSA_NAME}
            nodeSelector:
              cloud.google.com/gke-accelerator: ${GPU_TYPE}
              cloud.google.com/gke-accelerator-count: 8
              cloud.google.com/gke-spot: "true"
              cloud.google.com/compute-class: Performance
            tolerations:
              - key: "nvidia.com/gpu"
                operator: "Exists"
                effect: "NoSchedule"
            containers:
            - name: ray-worker
              image: verlai/verl:vllm011.latest
              env:
               - name: LD_LIBRARY_PATH
                 value: /usr/local/nvidia/lib64
              resources:
                limits:
                  cpu: "220"
                  memory: "2800Gi"
                  nvidia.com/gpu: "8"
                  ephemeral-storage: "1000Gi"
                requests:
                  cpu: "220"
                  memory: "2800Gi"
                  nvidia.com/gpu: "8"
                  ephemeral-storage: "1000Gi"
              volumeMounts:
              - name: nvidia
                mountPath: /usr/local/nvidia
                readOnly: true
              - name: gib
                mountPath: /usr/local/gib
                readOnly: true
              - name: shared-memory
                mountPath: /dev/shm
              - name: ray-tmp-storage
                mountPath: /tmp
              - name: training-bucket-vol
                mountPath: /data
            volumes:
            - name: gib
              hostPath:
                path: /home/kubernetes/bin/gib
            - name: nvidia
              hostPath:
                path: /home/kubernetes/bin/nvidia
            - name: lib64
              hostPath:
                path: /lib64
            - name: shared-memory
              emptyDir:
                medium: "Memory"
                sizeLimit: 250Gi 
            - name: sys
              hostPath:
                path: /sys
            - name: proc-sys
              hostPath:
                path: /proc/sys
            - name: ray-tmp-storage
              emptyDir: {}
            - name: training-bucket-vol
              persistentVolumeClaim:
                claimName: training-bucket-pvc
    
  2. RayCluster を適用します。

    envsubst < ray-cluster-auto.yaml > ray-cluster-auto-updated.yaml
    kubectl apply -f ray-cluster-auto-updated.yaml
    

標準

  1. RayCluster をデプロイします。次の構成を ray-cluster-standard.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: ray.io/v1
    kind: RayCluster
    metadata:
      name: b200-ray-cluster
      annotations:
    spec:
      rayVersion: '2.47.0'
      headGroupSpec:
        rayStartParams:
          dashboard-host: '0.0.0.0'
        template:
          metadata:
            annotations:
              gke-gcsfuse/volumes: "true"
          spec:
            serviceAccountName: ${KSA_NAME}
            nodeSelector:
              cloud.google.com/gke-nodepool: "default-pool"
            containers:
            - name: ray-head
              image: verlai/verl:vllm011.latest 
              ports:
                - containerPort: 6379
                  name: gcs-server
                - containerPort: 8265
                  name: dashboard
                - containerPort: 10001
                  name: client
              resources:
                limits:
                  cpu: "12"
                  memory: "32G"
                  ephemeral-storage: "9Gi"
                requests:
                  cpu: "12"
                  memory: "32G"
                  ephemeral-storage: "9Gi"
              volumeMounts:
                - mountPath: /tmp/ray
                  name: ray-logs
                - name: training-bucket-vol
                  mountPath: /data
            volumes:
              - name: ray-logs
                emptyDir: {}
              - name: training-bucket-vol
                persistentVolumeClaim:
                  claimName: training-bucket-pvc
      workerGroupSpecs:
      - replicas: 2
        minReplicas: 2
        maxReplicas: 2
        groupName: gpu-group
        rayStartParams:
          num-cpus: "220"
        template:
          metadata:
            annotations:
              gke-gcsfuse/volumes: "true"
              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"}
                ]
          spec:
            initContainers:
            - name: verl-setup
              image: verlai/verl:vllm011.latest
              command: ["/bin/bash", "-c"]
              args:
                - |
                  echo "Performing local editable install..."
                  cd /data/verl && pip3 install --no-deps -e .
              volumeMounts:
              - name: training-bucket-vol
                mountPath: /data
            serviceAccountName: ${KSA_NAME}
            nodeSelector:
              cloud.google.com/gke-accelerator: ${GPU_TYPE}
            tolerations:
              - key: "nvidia.com/gpu"
                operator: "Exists"
                effect: "NoSchedule"
            containers:
            - name: ray-worker
              image: verlai/verl:vllm011.latest
              env:
               - name: LD_LIBRARY_PATH
                 value: /usr/local/nvidia/lib64
              resources:
                limits:
                  cpu: "220"
                  memory: "2800Gi"
                  nvidia.com/gpu: "8"
                  ephemeral-storage: "1000Gi"
                requests:
                  cpu: "220"
                  memory: "2800Gi"
                  nvidia.com/gpu: "8"
                  ephemeral-storage: "1000Gi"
              volumeMounts:
              - name: nvidia
                mountPath: /usr/local/nvidia
              - name: gib
                mountPath: /usr/local/gib
              - name: shared-memory
                mountPath: /dev/shm
              - name: ray-tmp-storage
                mountPath: /tmp
              - name: training-bucket-vol
                mountPath: /data
            volumes:
            - name: gib
              hostPath:
                path: /home/kubernetes/bin/gib
            - name: nvidia
              hostPath:
                path: /home/kubernetes/bin/nvidia
            - name: lib64
              hostPath:
                path: /lib64
            - name: shared-memory
              emptyDir:
                medium: "Memory"
                sizeLimit: 250Gi 
            - name: sys
              hostPath:
                path: /sys
            - name: proc-sys
              hostPath:
                path: /proc/sys
            - name: ray-tmp-storage
              emptyDir: {}
            - name: training-bucket-vol
              persistentVolumeClaim:
                claimName: training-bucket-pvc
    
  2. RayCluster を適用します。

    envsubst < ray-cluster-standard.yaml > ray-cluster-updated.yaml
    kubectl apply -f ray-cluster-updated.yaml
    

A4X

  1. RDMA ResourceClaimTemplate と NVIDIA ComputeDomain を作成します。各 GPU ワーカー Pod は、4 つの RDMA NIC(ノードのすべてのレール)と 1 つの IMEX チャネルを要求します。次のマニフェストを compute-domain-a4x.yaml に保存します。

    apiVersion: resource.k8s.io/v1
    kind: ResourceClaimTemplate
    metadata:
      name: verl-rdma-nic
      namespace: ${NAMESPACE}
    spec:
      spec:
        devices:
          requests:
          - name: nic
            exactly:
              deviceClassName: mrdma.google.com
              allocationMode: ExactCount
              count: 1
    ---
    apiVersion: resource.nvidia.com/v1beta1
    kind: ComputeDomain
    metadata:
      name: verl-compute-domain
      namespace: ${NAMESPACE}
    spec:
      numNodes: ${NUM_GPU_NODES}
      channel:
        resourceClaimTemplate:
          name: verl-compute-domain-channel
    
  2. 次のようにマニフェストを適用します。

    kubectl apply -f compute-domain-a4x.yaml
    
  3. RayCluster をデプロイします。Ray ヘッド Pod は、GPU をリクエストせずに A4X ノードで実行されます(イメージは arm64 のみであるため)。次の構成を ray-cluster-a4x.yaml に保存します。

    apiVersion: ray.io/v1
    kind: RayCluster
    metadata:
      name: gb200-ray-cluster
      namespace: ${NAMESPACE}
    spec:
      rayVersion: '2.49.0'
      headGroupSpec:
        rayStartParams:
          dashboard-host: '0.0.0.0'
          num-cpus: "0"
        template:
          metadata:
            annotations:
              gke-gcsfuse/volumes: "true"
          spec:
            serviceAccountName: ${KSA_NAME}
            nodeSelector:
              cloud.google.com/gke-accelerator: nvidia-gb200
            tolerations:
            - key: nvidia.com/gpu
              operator: Exists
              effect: NoSchedule
            - key: kubernetes.io/arch
              operator: Exists
              effect: NoSchedule
            containers:
            - name: ray-head
              image: ${VERL_IMAGE}
              lifecycle:
                postStart:
                  exec:
                    command:
                    - /bin/bash
                    - -c
                    - pip3 install --quiet TransferQueue==0.1.8
              ports:
              - containerPort: 6379
                name: gcs-server
              - containerPort: 8265
                name: dashboard
              - containerPort: 10001
                name: client
              resources:
                limits:
                  cpu: "12"
                  memory: 32Gi
                  ephemeral-storage: 20Gi
                requests:
                  cpu: "12"
                  memory: 32Gi
                  ephemeral-storage: 20Gi
              volumeMounts:
              - mountPath: /tmp/ray
                name: ray-logs
              - name: training-bucket-vol
                mountPath: /data
            volumes:
            - name: ray-logs
              emptyDir: {}
            - name: training-bucket-vol
              persistentVolumeClaim:
                claimName: training-bucket-pvc
      workerGroupSpecs:
      - replicas: ${NUM_GPU_NODES}
        minReplicas: ${NUM_GPU_NODES}
        maxReplicas: ${NUM_GPU_NODES}
        groupName: gpu-group
        rayStartParams:
          num-cpus: "120"
        template:
          metadata:
            annotations:
              gke-gcsfuse/volumes: "true"
          spec:
            serviceAccountName: ${KSA_NAME}
            nodeSelector:
              cloud.google.com/gke-accelerator: nvidia-gb200
            affinity:
              podAntiAffinity:
                requiredDuringSchedulingIgnoredDuringExecution:
                - labelSelector:
                    matchLabels:
                      ray.io/group: gpu-group
                  topologyKey: kubernetes.io/hostname
            tolerations:
            - key: nvidia.com/gpu
              operator: Exists
              effect: NoSchedule
            - key: kubernetes.io/arch
              operator: Exists
              effect: NoSchedule
            containers:
            - name: ray-worker
              image: ${VERL_IMAGE}
              lifecycle:
                postStart:
                  exec:
                    command:
                    - /bin/bash
                    - -c
                    - pip3 install --quiet TransferQueue==0.1.8
              env:
              - name: LD_LIBRARY_PATH
                value: /usr/local/nvidia/lib64
              resources:
                limits:
                  cpu: "120"
                  memory: 600Gi
                  nvidia.com/gpu: "4"
                  ephemeral-storage: 500Gi
                requests:
                  cpu: "120"
                  memory: 600Gi
                  nvidia.com/gpu: "4"
                  ephemeral-storage: 500Gi
                claims:
                - name: rdma-nic-0
                - name: rdma-nic-1
                - name: rdma-nic-2
                - name: rdma-nic-3
                - name: compute-domain-channel
              volumeMounts:
              - name: nvidia
                mountPath: /usr/local/nvidia
              - name: gib
                mountPath: /usr/local/gib
              - name: shared-memory
                mountPath: /dev/shm
              - name: ray-tmp-storage
                mountPath: /tmp
              - name: training-bucket-vol
                mountPath: /data
            resourceClaims:
            - name: rdma-nic-0
              resourceClaimTemplateName: verl-rdma-nic
            - name: rdma-nic-1
              resourceClaimTemplateName: verl-rdma-nic
            - name: rdma-nic-2
              resourceClaimTemplateName: verl-rdma-nic
            - name: rdma-nic-3
              resourceClaimTemplateName: verl-rdma-nic
            - name: compute-domain-channel
              resourceClaimTemplateName: verl-compute-domain-channel
            volumes:
            - name: gib
              hostPath:
                path: /home/kubernetes/bin/gib
            - name: nvidia
              hostPath:
                path: /home/kubernetes/bin/nvidia
            - name: shared-memory
              emptyDir:
                medium: Memory
                sizeLimit: 200Gi
            - name: ray-tmp-storage
              emptyDir: {}
            - name: training-bucket-vol
              persistentVolumeClaim:
                claimName: training-bucket-pvc
    
  4. RayCluster マニフェストを適用します。

    envsubst < ray-cluster-a4x.yaml | kubectl apply -f -
    
  5. 1 つのヘッド Pod と 4 つのワーカー Pod が Running 状態になるまで待ちます。

    kubectl get pods -w
    

GRPO ジョブを起動する

強化学習トレーニング ジョブを構成して送信します。

A4 と A3 Ultra

  1. Ray ダッシュボード ノードへのポート転送を設定します。このコマンドは実行中、ターミナルをブロックするため、別のターミナル ウィンドウを使用してください。Ctrl+C キーを押して停止します。

    kubectl port-forward svc/b200-ray-cluster-head-svc 8265:8265
    
  2. マニフェスト runtime-env.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.
    
    py_modules: ["."]
    working_dir": "."
    py_executable": "uv run"
    setup_hook: runtime_env.uv_runtime_env_hook.hook 
    env_vars:
      PYTHONPATH: "/data/verl"
      LD_LIBRARY_PATH: "/usr/local/nvidia/lib64"
      NCCL_DEBUG: "INFO"
      NUM_WORKERS: "2"
      CPUS_PER_WORKER: "192"
      GPUS_PER_WORKER: "8"
      NCCL_NET_PLUGIN: "/usr/local/gib/lib64/libnccl-net_internal.so"
      NCCL_CROSS_NIC: "0"
      NCCL_NET_GDR_LEVEL: "PIX"
      NCCL_P2P_NET_CHUNKSIZE: "131072"
      NCCL_NVLS_CHUNKSIZE: "524288"
      NCCL_IB_ADAPTIVE_ROUTING: "1"
      NCCL_IB_QPS_PER_CONNECTION: "4"
      NCCL_IB_TC: "52"
      NCCL_IB_FIFO_TC: "84"
      NCCL_TUNER_CONFIG_PATH: "/usr/local/gib/configs/tuner_config_a4.txtpb" 
      HF_HOME: "/data/huggingface_cache"
      GLOO_SOCKET_IFNAME: "eth0" 
    pip:
      packages:
        - torch 
        - torchvision
    

    H200 GPU を使用する場合は、NCCL_TUNER_CONFIG_PATH/usr/local/gib/configs/tuner_config_a3u.txtpb に変更します。

    このファイルは Ray クライアントで使用されます。このマニフェストをクラスタに適用する必要はありません。

  3. ray job submit を使用して Job を送信します。

    ray job submit \
    --address "http://localhost:8265" \
    --runtime-env runtime-env.yaml \
    -- \
    bash -c "
        cd /data/verl && PYTHONUNBUFFERED=1 python3 -m verl.trainer.main_ppo \
        data.train_files=/data/gsm8k/train.parquet \
        data.val_files=/data/gsm8k/test.parquet \
        data.train_batch_size=256 \
        data.max_prompt_length=512 \
        data.max_response_length=512 \
        actor_rollout_ref.model.path=/data/Qwen2.5-32B-Instruct \
        actor_rollout_ref.actor.optim.lr=1e-5 \
        actor_rollout_ref.actor.ppo_mini_batch_size=256 \
        actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu=64 \
        actor_rollout_ref.rollout.name=vllm \
        actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu=8 \
        actor_rollout_ref.rollout.tensor_model_parallel_size=8 \
        actor_rollout_ref.rollout.gpu_memory_utilization=0.6 \
        actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu=4 \
        actor_rollout_ref.actor.strategy=fsdp2 \
        algorithm.kl_ctrl.kl_coef=0.001 \
        trainer.logger=console \
        trainer.val_before_train=False \
        trainer.n_gpus_per_node=8 \
        trainer.nnodes=2 \
        trainer.save_freq=10 \
        trainer.test_freq=10 \
        trainer.default_local_dir=/data/verl/checkpoints \
        algorithm.adv_estimator=grpo \
        actor_rollout_ref.rollout.n=8 \
        trainer.total_epochs=2"
    

    Ray ダッシュボードまたはコンソール出力でログをモニタリングします。critic/score/mean が増加していることを確認します。これは学習を示しています。

  4. トレーニングが完了すると、トレーニング済みモデルのチェックポイントが gs://$GS_BUCKET/verl/checkpoints に保存されます。

A4X

  1. Ray ヘッド Pod の名前を取得します。

    export HEAD_POD=$(kubectl get pod -n ${NAMESPACE} -l ray.io/node-type=head -o jsonpath='{.items[0].metadata.name}')
    
  2. ヘッド Pod で Ray ランタイム環境ファイルを直接構成します。

    kubectl exec ${HEAD_POD} -c ray-head -- bash -c 'mkdir -p /tmp/submit && cat > /tmp/submit/runtime-env.yaml <<EOF
    working_dir: "."
    env_vars:
      PYTHONPATH: "/data/verl"
      LD_LIBRARY_PATH: "/usr/local/nvidia/lib64:/usr/local/gib/lib64"
      NCCL_DEBUG: "INFO"
      NCCL_ENV_PLUGIN: "gcp"
      HF_HOME: "/data/huggingface_cache"
      GLOO_SOCKET_IFNAME: "eth0"
    EOF'
    
  3. Ray ヘッド Pod で実行して GRPO トレーニング ジョブを送信します。

    kubectl exec ${HEAD_POD} -c ray-head -- bash -c 'cd /tmp/submit && \
    ray job submit --runtime-env runtime-env.yaml --no-wait -- \
      python3 -m verl.trainer.main_ppo \
        algorithm.adv_estimator=grpo \
        data.train_files=/data/gsm8k/train.parquet \
        data.val_files=/data/gsm8k/test.parquet \
        data.train_batch_size=256 \
        data.max_prompt_length=512 \
        data.max_response_length=512 \
        actor_rollout_ref.model.path=/data/Qwen2.5-32B-Instruct \
        actor_rollout_ref.actor.optim.lr=1e-5 \
        actor_rollout_ref.actor.ppo_mini_batch_size=64 \
        actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu=8 \
        actor_rollout_ref.actor.use_kl_loss=True \
        actor_rollout_ref.actor.strategy=fsdp2 \
        actor_rollout_ref.rollout.name=vllm \
        actor_rollout_ref.rollout.tensor_model_parallel_size=4 \
        actor_rollout_ref.rollout.gpu_memory_utilization=0.6 \
        actor_rollout_ref.rollout.n=8 \
        actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu=16 \
        actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu=16 \
        algorithm.kl_ctrl.kl_coef=0.001 \
        trainer.logger=console \
        trainer.n_gpus_per_node=4 \
        trainer.nnodes=4 \
        trainer.save_freq=10 \
        trainer.test_freq=10 \
        trainer.total_epochs=2 \
        trainer.default_local_dir=/data/verl/checkpoints'
    
  4. ジョブログをモニタリングします(ray job submit から返された一意の ID を使用)。

    kubectl exec ${HEAD_POD} -c ray-head -- ray job logs <var>JOB_ID</var> --follow
    

    JOB_ID には、via P2P/MNNVL を含むログで NCCL 行を探して、クロスノード NVLink がアクティブであることを確認します。

クリーンアップ

課金されないようにするには、リソースを削除します。

A4 と A3 Ultra

kubectl delete raycluster b200-ray-cluster
gcloud container clusters delete ${CLUSTER_NAME} --location=${CONTROL_PLANE_REGION}
gcloud storage rm -r gs://${GS_BUCKET}

A4X

kubectl delete raycluster gb200-ray-cluster
kubectl delete computedomain verl-compute-domain
gcloud storage rm -r gs://${GS_BUCKET}
gcloud container clusters delete ${CLUSTER_NAME} --location=${CONTROL_PLANE_REGION}

次のステップ