이 튜토리얼에서는 Google Kubernetes Engine (GKE)에서 강화 학습을 위한 분산 학습 환경을 오케스트레이션하는 방법을 보여줍니다. Ray와 verl (Volcano Engine Reinforcement Learning) 프레임워크를 사용하여 Qwen2.5-32B-Instruct 모델을 미세 조정하기 위한 분산 학습 환경을 설정합니다.
이 튜토리얼에서는 Ray 및 verl을 사용하여 GKE에서 그룹 상대 정책 최적화 (GRPO) 학습 파이프라인에 중점을 둡니다. GRPO는 모델의 추론 능력을 개선하기 위해 설계된 강화 학습 알고리즘입니다. 이 메모리 효율적인 알고리즘은 Critic 또는 가치 모델을 제거하고 대신 상대적인 그룹 기반 계산을 사용하여 강화 학습 (RL) 프로세스를 간소화합니다.
이 튜토리얼은 효율성을 위해 데이터, 모델 가중치, 학습 엔진이 분리된 분산 학습 환경을 설정해야 하는 경우 좋은 출발점이 될 수 있습니다.
배경
다음 섹션에서는 이 튜토리얼에서 사용되는 개념을 간략하게 설명합니다.
강화 학습
RL은 정적인 모방이 아닌 경험, 탐색, 피드백을 통해 모델을 학습시킵니다. 사전 학습은 모델에 무엇을 말해야 하는지 알려주는 반면, RL(특히 인간 피드백 기반 강화 학습(RLHF))은 유용하고 안전하며 논리적인 방법을 알려줍니다. RL은 기본 모델과 특수 사용 사례를 위해 미세 조정된 모델 간의 연결 역할을 합니다.
자세한 내용은 강화 학습이란 무엇인가요?를 참고하세요.
Volcano Engine 강화 학습 (verl)
verl은 LLM 기반 RL의 복잡한 메모리 및 컴퓨팅 패턴을 처리하도록 설계된 고성능 프레임워크입니다.
자세한 내용은 verl을 참고하세요.
그룹 상대 정책 최적화 (GRPO)
DeepSeek에서 널리 사용되는 알고리즘인 GRPO는 비평가 모델을 삭제하여 LLM 정렬을 위한 근위 정책 최적화 (PPO)의 메모리 효율적인 대안을 제공합니다. 비평가 네트워크 대신 GRPO는 동일한 프롬프트에 대한 응답 그룹을 생성하고 해당 그룹의 평균 보상을 기준선으로 사용합니다.
자세한 내용은 GRPO를 참고하세요.
목표
이 튜토리얼에서는 다음 단계를 완료하여 verl을 사용하여 GKE에서 강화 학습을 설정하는 방법을 보여줍니다.
- B200 또는 H200 GPU가 있는 GKE 클러스터를 설정합니다.
- 분산 Ray 클러스터를 관리하도록 KubeRay를 구성합니다.
- Cloud Storage FUSE를 사용하여 모든 노드에 Cloud Storage 버킷을 마운트합니다.
- 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 프로젝트 이름으로 바꿉니다.
필요한 API를 사용 설정합니다.
API 사용 설정에 필요한 역할
API를 사용 설정하려면
serviceusage.services.enable권한이 포함된 서비스 사용량 관리자 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 프로젝트 이름으로 바꿉니다.
필요한 API를 사용 설정합니다.
API 사용 설정에 필요한 역할
API를 사용 설정하려면
serviceusage.services.enable권한이 포함된 서비스 사용량 관리자 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.admingcloud projects add-iam-policy-binding PROJECT_ID --member="user:USER_IDENTIFIER" --role=ROLE
다음을 바꿉니다.
PROJECT_ID: 프로젝트 ID입니다.USER_IDENTIFIER: 사용자 계정의 식별자입니다. 예를 들면myemail@example.com입니다.ROLE: 사용자 계정에 부여하는 IAM 역할입니다.
- 아직 계정이 없다면 Hugging Face 계정을 만듭니다.
- Hugging Face 토큰이 있는지 확인합니다.
- 프로젝트에 B200 및 H200 GPU 할당량이 충분한지 확인합니다. 자세한 내용은 GPU 할당량 계획 및 GPU 할당량을 참조하세요.
개발 환경 준비
이 튜토리얼에서는 Cloud Shell을 사용합니다.
Google Cloud 콘솔로 이동합니다.
Google Cloud 콘솔 창 상단에서 Cloud Shell 활성화 버튼을 클릭합니다.
다음 환경 변수를 설정합니다.
export PROJECT_ID=$(gcloud config get project) export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)") export GPU_TYPE=GPU_TYPE export CONTROL_PLANE_LOCATION=CONTROL_PLANE_LOCATION export NODE_LOCATION=NODE_LOCATION export CLUSTER_NAME=CLUSTER_NAME export KSA_NAME=CLUSTER_NAME export GS_BUCKET=BUCKET_NAME-${PROJECT_ID} export NAMESPACE=default export HF_TOKEN=YOUR_HUGGING_FACE_TOKEN export MACHINE_TYPE=MACHINE_TYPE export GKE_VERSION=GKE_VERSION다음 값을 바꿉니다.
CONTROL_PLANE_LOCATION: GKE 클러스터 컨트롤 플레인의 Compute Engine 리전입니다.GPU_TYPE: Compute Engine 용량 예약에서 예약한 가속기. 다음 값 중 하나여야 합니다.nvidia-b200: NVIDIA B200(180GB)nvidia-h200-141gb: NVIDIA H200(141GB)
NODE_LOCATION: GKE 노드의 영역입니다. NVIDIA B200 또는 H200 GPU를 사용할 수 있는 영역을 선택합니다.CLUSTER_NAME: GKE 클러스터의 이름입니다.BUCKET_NAME: Cloud Storage 버킷의 기본 이름입니다.gs://접두사를 지정할 필요가 없습니다.YOUR_HUGGING_FACE_TOKEN: 모델 액세스를 위한 Hugging Face 토큰입니다.MACHINE_TYPE: 사용할 머신 유형입니다. 유효한 옵션은c2standard8또는c2standard16입니다.GKE_VERSION: 사용할 GKE 버전입니다.- NVIDIA B200 (180GB) GPU의 경우
1.32.2-gke.1422000이상을 사용하세요. - NVIDIA H200 (141GB) GPU의 경우
1.31.4-gke.1183000이상을 사용합니다.
- NVIDIA B200 (180GB) GPU의 경우
네트워크에 다음 환경 변수를 만듭니다.
export GVNIC_NETWORK_PREFIX="GVNIC-NAME" export RDMA_NETWORK_PREFIX="RDMA-NAME"다음 값을 바꿉니다.
GVNIC-NAME: gVNIC 네트워크 이름의 접두사입니다. 원하는 접두사를 사용할 수 있습니다.RDMA-NAME: 원격 직접 메모리 액세스 (RDMA) 네트워크의 프리픽스입니다. 원하는 접두사를 사용할 수 있습니다.
인프라 설정
이 섹션에서는 RDMA 네트워크와 GKE 클러스터를 만듭니다.
RDMA 네트워크 및 서브넷 만들기
gVNIC 인터페이스용 VPC 네트워크를 만듭니다.
gcloud compute networks create ${GVNIC_NETWORK_PREFIX}-net \ --subnet-mode=custom \ --project=${PROJECT} 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/168개의 GPU에 대해 8개의 서브넷이 있는 RDMA용 VPC 네트워크 및 서브넷을 만듭니다.
gcloud beta 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샘플 저장소를 클론합니다.
git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git cd kubernetes-engine-samples작업 디렉터리로 이동합니다.
cd ai-ml/verl-on-gke
GKE 클러스터 만들기
GKE Autopilot 또는 Standard 클러스터에서 verl을 설정할 수 있습니다. 완전 관리형 Kubernetes 환경을 위해서는 Autopilot을 사용하는 것이 좋습니다. 워크로드에 가장 적합한 GKE 작업 모드를 선택하려면 GKE 작업 모드 선택을 참조하세요.
Autopilot
Autopilot 클러스터를 만듭니다.
gcloud container clusters create-auto ${CLUSTER_NAME} \ --location=${CONTROL_PLANE_LOCATION} \ --enable-multi-networking \ --enable-ray-operator클러스터의 사용자 인증 정보를 가져옵니다.
gcloud container clusters get-credentials ${CLUSTER_NAME} \ --location=${REGION}Autopilot용 NCCL RDMA 설치 프로그램을 설치합니다.
kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/refs/heads/master/gpudirect-rdma/nccl-rdma-installer-autopilot.yaml
표준
표준 클러스터 만들기
gcloud container clusters create ${CLUSTER_NAME} \ --location=${CONTROL_PLANE_LOCATION} \ --location=${ZONE} \ --enable-dataplane-v2 \ --enable-ip-alias \ --enable-multi-networking \ --addons=RayOperator,GcsFuseCsiDriver \ --machine-type=${MACHINE_TYPE} \ --num-nodes=1 \ --min-nodes=1 \ --max-nodes=5 \ --enable-autoscaling클러스터의 사용자 인증 정보를 가져옵니다.
gcloud container clusters get-credentials ${CLUSTER_NAME} --location=${ZONE}비용 효율성을 위해 스팟 인스턴스를 사용하여 GPU 노드 풀을 만듭니다.
gcloud container node-pools create gpu-pool \ --cluster=${CLUSTER_NAME} \ --location=${NODE_LOCATION} \ --machine-type=${MACHINE_TYPE} \ --accelerator=type=${GPU_TYPE},count=8,gpu-driver-version=DEFAULT \ --spot \ --enable-autoscaling \ --num-nodes=0 \ --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-7Standard 클러스터에 사용되는 NCCL RDMA 설치 프로그램을 설치합니다.
kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/refs/heads/master/gpudirect-rdma/nccl-rdma-installer.yaml
네트워크 매핑 구성
network-mapping.yaml매니페스트를 검사합니다.매니페스트를 적용합니다.
kubectl apply -f network-mapping.yaml
데이터 및 스토리지 준비
Cloud Storage 버킷을 만듭니다.
gcloud storage buckets create gs://${GS_BUCKET} --location=${REGION} --enable-hierarchical-namespace --uniform-bucket-level-accessKubernetes 서비스 계정 (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"Hugging Face의 보안 비밀을 만듭니다.
kubectl create secret generic hf-secret --from-literal=hf_api_token=${HF_TOKEN}gcsfuse-storage.yaml매니페스트를 검사합니다.매니페스트를 적용합니다.
kubectl apply -f gcsfuse-storage.yaml
모델 및 데이터 준비
로컬 또는 GKE 포드에서 이러한 명령어를 실행하여 버킷을 채울 수 있습니다.
verl 저장소를 클론합니다.
git clone https://github.com/volcengine/verl.gitHugging Face CLI를 사용하여 Qwen2.5-32B-Instruct 모델을 다운로드합니다.
huggingface-cli download Qwen/Qwen2.5-32B-Instruct --local-dir Qwen2.5-32B-InstructGSM8K 데이터 세트를 사전 처리합니다.
python examples/data_preprocess/gsm8k.py --local_save_dir ~/data/gsm8k모델, 데이터, 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_BUCKET}
RayCluster 커스텀 리소스 배포
일반적으로 하나의 시스템 포드와 여러 개의 작업자 포드로 구성되는 RayCluster 커스텀 리소스를 배포합니다.
Autopilot
RayCluster를 배포합니다. 다음을
ray-cluster-auto.yaml에 저장합니다.RayCluster를 적용합니다.
kubectl apply -f ray-cluster.yaml
표준
RayCluster를 배포합니다. 다음을
ray-cluster.yaml에 저장합니다.RayCluster를 적용합니다.
kubectl apply -f ray-cluster.yaml
GRPO 작업 실행
Ray 대시보드 노드로의 포트 전달을 설정합니다.
kubectl port-forward svc/b200-ray-cluster-head-svc 8265:8265runtime-env.yaml매니페스트를 검사합니다.H200 GPU를 사용하는 경우
NCCL_TUNER_CONFIG_PATH를/usr/local/gib/configs/tuner_config_a3u.txtpb로 변경합니다.이 파일은 Ray 클라이언트에서 사용합니다. 이 매니페스트를 클러스터에 적용할 필요는 없습니다.
ray job submit를 사용하여 작업을 제출합니다.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=Qwen/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 \ algorithm.adv_estimator=grpo \ actor_rollout_ref.rollout.n=8 \ trainer.total_epochs=2" 2>&1 | tee verl_demo.logRay 대시보드 또는 출력에서 로그를 모니터링합니다. 학습을 나타내는
critic/score/mean가 증가하는지 확인합니다.
삭제
요금이 청구되지 않도록 하려면 리소스를 삭제하세요.
kubectl delete raycluster b200-ray-cluster # change to variables
gcloud container clusters delete ${CLUSTER_NAME} --location=${CONTROL_PLANE_LOCATION}
gcloud storage rm -r gs://${GS_BUCKET}