本教程介绍了如何在 Google Kubernetes Engine (GKE) 上编排强化学习的分布式训练环境。您将使用 Ray 和 verl(Volcano Engine 强化学习)框架设置分布式训练环境,以在 GSM8K 数据集上对 Qwen2.5-32B-Instruct 模型进行微调。
本教程重点介绍如何使用 Ray 和 verl 在 GKE 上运行 Group Relative Policy Optimization (GRPO) 训练流水线。GRPO 是一种旨在提升模型推理能力的强化学习算法。这种内存高效型算法通过以下方式简化了强化学习 (RL) 流程:消除 Critic 或价值模型,并使用基于相对组的计算。
如果您需要设置一个分布式训练环境,在该环境中,数据、模型权重和训练引擎会解耦以提高效率,那么本教程是一个很好的起点。
本教程支持以下 GPU 架构:
- 基于 Intel 或 AMD 的 GPU 节点:使用 NVIDIA B200 或 H200 GPU 设置和扩缩,并使用 GKE 动态资源分配 (DRA) 实现 Autopilot 路径。
- 基于 Arm 的 A4X (GB200) 节点:使用 GKE 动态资源分配 (DRA) 和多节点 NVLink (IMEX),通过 NVIDIA GB200 Grace Blackwell 超级芯片进行设置和扩缩。
背景
以下部分简要介绍了本教程中使用的概念。
强化学习 (RL)
RL 通过经验、探索和反馈来训练模型,而不是静态模仿。虽然预训练可以教会模型说什么,但基于人类反馈的强化学习 (RLHF) 可以教会模型如何提供有用的、安全的、符合逻辑的回答。RL 可作为基础模型与针对特定用例微调的模型之间的桥梁。
如需了解详情,请参阅什么是强化学习?
群组相对政策优化 (GRPO)
GRPO 是一种由 DeepSeek 推广的算法,它通过移除 Critic 模型,为 LLM 对齐提供了一种内存高效的近端策略优化 (PPO) 替代方案。与 Critic 网络不同,GRPO 会针对同一提示生成一组回答,并使用该组回答的平均奖励作为基准。
如需了解详情,请参阅 GRPO。
Volcano Engine 强化学习 (verl)
verl 是一个高性能框架,旨在处理基于 LLM 的强化学习的复杂内存和计算模式。
如需了解详情,请参阅 verl。
目标
本教程将通过完成以下步骤,介绍如何在 GKE 上使用 verl 设置强化学习:
- 设置一个配备 A4X(GB200 超级芯片)、A4(B200 GPU)或 A3 Ultra(H200 GPU)的 GKE 集群。
- 配置 KubeRay 以管理分布式 Ray 集群。
- 使用 Cloud Storage FUSE 在所有节点上装载 Cloud Storage 存储桶。
- 使用 verl 运行 GRPO 训练作业,使 Qwen2.5-32B-Instruct 模型与 GSM8K 数据集保持一致。
准备工作
-
安装 Google Cloud CLI。
-
配置 gcloud CLI 以使用您的联合身份。
如需了解详情,请参阅使用联合身份登录 gcloud CLI。
-
如需初始化 gcloud CLI,请运行以下命令:
gcloud init -
选择或创建项目所需的角色
- 选择项目:选择项目不需要特定的 IAM 角色,您可以选择已获授角色的任何项目。
-
创建项目:如需创建项目,您需要拥有 Project Creator 角色 (
roles/resourcemanager.projectCreator),该角色包含resourcemanager.projects.create权限。了解如何授予角色。
-
创建 Google Cloud 项目:
gcloud projects create PROJECT_ID
将
PROJECT_ID替换为您要创建的 Google Cloud 项目的名称。 -
选择您创建的 Google Cloud 项目:
gcloud config set project PROJECT_ID
将
PROJECT_ID替换为您的 Google Cloud 项目名称。
启用所需的 API:
启用 API 所需的角色
如需启用 API,您需要拥有
serviceusage.services.enable权限。如果您创建了项目,则可能已经通过 Owner 角色 (roles/owner) 获得了此权限。否则,您可以通过 Service Usage Admin 角色 (roles/serviceusage.serviceUsageAdmin) 获得此权限。了解如何授予角色。gcloud services enable container.googleapis.com
storage.googleapis.com compute.googleapis.com -
向您的用户账号授予角色。对以下每个 IAM 角色运行以下命令一次:
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:您的用户 账号。如需查看示例,请参阅 在 IAM 政策中表示员工池用户。ROLE:您向用户账号授予的 IAM 角色。
- 如果您还没有 Hugging Face 账号,请创建一个。
- 确保您拥有 Hugging Face 令牌。
- 确保您的项目具有足够的 A4X(GB200 超级芯片)、A4(B200 GPU)或 A3 Ultra(H200 GPU)配额。如需了解详情,请参阅规划 GPU 配额和 GPU 配额。
- 确保您已为 GPU 机器类型预留有效容量。如需了解详情,请参阅通过客户支持团队预留容量。
- 对于 A4X (GB200) 设置,请确保您已安装 Helm。
准备环境
在本教程中,您将使用 Cloud Shell。
前往 Google Cloud 控制台。
点击 Google Cloud 控制台窗口顶部的激活 Cloud Shell 按钮。
设置环境变量:
A4 和 A3 Ultra
Autopilot
标准
A4X
export PROJECT_ID=$(gcloud config get project) export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)") export CONTROL_PLANE_REGION=YOUR_REGION export NODE_ZONE=YOUR_ZONE export CLUSTER_NAME=YOUR_CLUSTER_NAME export KSA_NAME=YOUR_KSA_NAME export GS_BUCKET=YOUR_GCS_BUCKET-${PROJECT_ID} export NAMESPACE=default export GPU_TYPE=YOUR_GPU_TYPE export MACHINE_TYPE=YOUR_MACINE_TYPE export RESERVATION=YOUR_RESERVATION_NAME export HF_TOKEN=YOUR_HF_TOKEN # A4X (GB200 Superchips) only variables export NUM_GPU_NODES=4 export VERL_IMAGE=verlai/verl:vllm023.aarch64.dev1 export VERL_REF=ddbcdb7替换以下值:
YOUR_REGION:GKE 集群控制平面的 Compute Engine 区域。YOUR_ZONE:预留节点所在的可用区。如需了解详情,请参阅 GPU 可用性。YOUR_CLUSTER_NAME:GKE 集群的名称。YOUR_KSA_NAME:Kubernetes 服务账号的名称。YOUR_GCS_BUCKET:Cloud Storage 存储桶的基本名称。您无需指定gs://前缀。YOUR_GPU_TYPE:您在 Compute Engine 容量预留中预留的加速器。必须是以下值之一:nvidia-gb200:A4X(GB200 超级芯片)nvidia-b200:A4(B200 GPU)nvidia-h200-141gb:A3 Ultra(H200 GPU)
YOUR_MACHINE_TYPE:要使用的机器类型:- 对于 A4X(GB200 超级芯片),请使用
a4x-highgpu-4g。 - 对于 A4(B200 GPU),请使用
a4-highgpu-8g或更高版本。 - 对于 A3 Ultra(H200 GPU),请使用
a3-ultragpu-8g或更高版本。
- 对于 A4X(GB200 超级芯片),请使用
YOUR_RESERVATION_NAME:容量预留的名称。YOUR_HF_TOKEN:您的 Hugging Face 令牌。- 仅限 Google Kubernetes Engine (GKE) Standard 版:
GVNIC_NAME(仅限 GKE Standard - A4 或 A3 Ultra):gVNIC 网络名称的前缀。您可以使用任何前缀。RDMA_NAME(仅限 A4 或 A3 Ultra):远程直接内存访问 (RDMA) 网络的前缀。您可以使用任何前缀。
克隆示例代码库:
前往所选 GKE 模式的工作目录:
A4 和 A3 Ultra
Autopilot
标准
A4X
无需更改任何目录。您可以直接前往下一部分。
设置基础架构
在本部分中,您将创建标准 VPC 网络和 GKE 集群。
创建 RDMA 网络和子网(仅限 GKE Standard - A4 和 A3 Ultra)
A4 和 A3 Ultra
Autopilot
本部分仅适用于 GKE Standard A4 和 A3 Ultra GPU。
如果您使用 Autopilot,请跳过本部分,直接前往创建 GKE 集群。GKE 会自动预配必要的 VPC 网络和子网,并使用 GKE 管理的 DRANET 将这些资源分配给您的 Pod。您无需手动创建任何网络基础设施。
标准
为 gVNIC 接口创建 VPC 网络:
为 RDMA 创建 VPC 网络:
为 8 个 GPU 创建 8 个 RDMA 子网:
A4X
本部分仅适用于 GKE Standard A4 和 A3 Ultra GPU。
如果您使用 A4X (GB200) GPU,请跳过此部分,直接前往创建 GKE 集群。对于 A4X (GB200) GPU 或 Autopilot,当节点池使用 auto 加速器网络配置文件时,GKE 会自动创建网络。Cluster Toolkit 蓝图使用 enable_dranet:true 标志启用此配置文件。
创建 GKE 集群
创建与 GPU 架构对应的 GKE 集群:
A4 和 A3 Ultra
选择您要使用的 GKE 集群模式:
Autopilot
创建 Autopilot 集群:
获取集群的凭据:
标准
创建 Standard 集群:
获取集群的凭据:
创建 GPU 节点池。这些节点池使用您的预留来确保可用性。您从两个节点开始:
安装用于标准集群的 NCCL RDMA 安装程序:
A4X
使用 Cluster Toolkit
gke-a4x蓝图创建 GKE 集群和节点池。 此蓝图可预配 GKE 集群,包括绑定到预留的 A4X 节点池、加速器网络(一个额外的 gVNIC 加四个 RDMA 轨道)以及将 CX-7 NIC 作为 DRA 设备公开的托管 DRANET 驱动程序。使用蓝图的部署说明来配置参数(例如
PROJECT_ID、CONTROL_PLANE_REGION、NODE_ZONE、预留和NUM_GPU_NODES),然后部署集群。或者,您也可以按照 A4X GKE 集群创建指南手动创建集群。- 获取集群的凭据:
gcloud container clusters get-credentials ${CLUSTER_NAME} --location=${CONTROL_PLANE_REGION}验证集群是否通过 DRA 公开 RDMA NIC:
kubectl get deviceclasses输出必须包含
mrdma.google.com。验证 A4X 节点是否存在:
kubectl get nodes -l cloud.google.com/gke-accelerator=nvidia-gb200安装 gIB NCCL 插件(A4X 变体):
kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/master/gpudirect-rdma/nccl-rdma-installer-a4x.yaml安装 NVIDIA DRA 驱动程序,该驱动程序可为多节点 NVLink 提供
ComputeDomain(IMEX) 通道: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安装 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}}"
配置网络映射(仅限 GKE Standard - A4 和 A3 Ultra)
A4 和 A3 Ultra
Autopilot
对于 GKE Standard GPU 设置(仅限 A4 和 A3 Ultra),此步骤是必需的。如果您使用 A4X (GB200),GKE 会自动管理网络接口,因此请跳过本部分。
标准
检查
network-mapping.yaml清单:应用清单:
A4X
对于 GKE Standard GPU 设置(仅限 A4 和 A3 Ultra),此步骤是必需的。如果您使用 A4X (GB200),GKE 会自动管理网络接口,因此请跳过此部分。
准备数据和存储空间
配置 Cloud Storage 和 Kubernetes 资源:
创建 Cloud Storage 存储分区,请运行以下命令:
创建一个 Kubernetes 服务账号 (KSA),并将其绑定到相应存储桶:
为 Hugging Face 创建 Secret:
检查
gcsfuse-storage.yaml清单:应用清单:
设置 DRANET
配置 DRANET:
A4 和 A3 Ultra
Autopilot
创建 ComputeClass 清单:
同时应用
computeclass-dranet.yaml清单(在上一步中创建)和resourceclaim-dranet.yaml清单(包含在示例代码库中):
标准
无需设置 DRANET。您可以直接前往下一部分。
A4X
DRANET 由 Cluster Toolkit 设置。您可以直接前往下一部分。
准备模型和数据
使用模型权重和数据集填充 Cloud Storage 存储桶。 您可以在本地或在 GKE Pod 上运行这些命令,以填充相应存储桶:
A4 和 A3 Ultra
Autopilot
检查数据准备作业:
启动作业:
监控作业:
标准
检查数据准备作业:
启动作业:
监控作业:
A4X
克隆 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使用 Hugging Face CLI 下载 Qwen2.5-32B-Instruct 模型(此下载需要大约 66 GB 的磁盘空间):
hf download Qwen/Qwen2.5-32B-Instruct --local-dir Qwen2.5-32B-Instruct将模型、数据和 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 自定义资源,该资源由一个系统头 Pod 和多个 GPU 支持的工作器 Pod 组成。
A4 和 A3 Ultra
选择您用于创建集群的 GKE 集群模式:
Autopilot
检查 RayCluster 工作负载:
应用 RayCluster:
标准
检查 RayCluster 工作负载:
应用 RayCluster:
A4X
创建 RDMA
ResourceClaimTemplate和 NVIDIAComputeDomain。 每个 GPU 工作器 Pod 声明了四个 RDMA NIC(其节点的所有轨道)和一个 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应用清单:
kubectl apply -f compute-domain-a4x.yaml部署 RayCluster。Ray head 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应用 RayCluster 清单:
envsubst < ray-cluster-a4x.yaml | kubectl apply -f -等待一个头 Pod 和四个工作器 Pod 处于
Running状态:kubectl get pods -w
启动 GRPO 作业
配置并提交强化学习训练作业:
A4 和 A3 Ultra
设置 Ray 客户端:
恢复 Ray Head 服务:
设置到 Ray 信息中心节点的端口转发。请使用单独的终端窗口执行此步骤,因为此命令在运行时会阻止终端。使用
Ctrl+C 停止该命令 :检查
runtime-env.yaml清单:如果您使用 H200 GPU,请将
NCCL_TUNER_CONFIG_PATH更改为/usr/local/gib/configs/tuner_config_a3u.txtpb。此文件由 Ray 客户端使用。您无需将此清单应用于集群。
使用
ray job submit提交作业:在 Ray 信息中心或控制台输出中监控日志。寻找表示学习的
critic/score/mean以增加。训练结束后,您可以在
gs://$GS_BUCKET/verl/checkpoints中找到经过训练的模型的检查点。
A4X
获取 Ray head Pod 名称:
export HEAD_POD=$(kubectl get pod -n ${NAMESPACE} -l ray.io/node-type=head -o jsonpath='{.items[0].metadata.name}')直接在头 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'通过在 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'监控作业日志(使用
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
Autopilot
删除 Ray 集群:
删除 Cloud Storage FUSE:
删除 DRANET 资源:
删除 Cloud Storage 存储桶:
删除 GKE 集群:
标准
删除 Ray 集群:
删除 Cloud Storage FUSE:
删除 Cloud Storage 存储桶:
删除 GKE 集群:
删除 VPC 网络和子网:
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}