Ottimizza Gemma 3 su un cluster GKE A4 multi-host

Questo tutorial mostra come ottimizzare un modello linguistico di grandi dimensioni (LLM) Gemma 3 su un cluster GKE Autopilot multi-host su Google Cloud. Questo cluster utilizza due istanze di macchine virtuali (VM) A4 con un totale di 16 GPU NVIDIA B200.

I due processi principali descritti in questo tutorial sono i seguenti:

  1. Esegui il deployment di un cluster GKE multi-host ad alte prestazioni utilizzando GKE Autopilot. Nell'ambito di questo deployment, creerai un'immagine VM personalizzata con il software necessario preinstallato.
  2. Dopo aver eseguito il deployment del cluster, esegui un job di ottimizzazione distribuito utilizzando l'insieme di script che accompagnano questo tutorial. Il job utilizza la libreria Hugging Face Accelerate.

Questo tutorial è rivolto a machine learning engineer, ricercatori, amministratori e operatori di piattaforme, nonché a specialisti di dati e AI interessati a eseguire il deployment di cluster GKE su Google Cloud per addestrare LLM.

Obiettivi

  • Accedi al modello Gemma 3 utilizzando Hugging Face.

  • Prepara l'ambiente.

  • Crea un cluster GKE A4 ed eseguine il deployment.

  • Ottimizza il modello Gemma 3 utilizzando la libreria Hugging Face Accelerate con Fully Sharded Data Parallel (FSDP).

  • Monitorare il job.

  • Eseguire la pulizia.

Costi

In questo documento vengono utilizzati i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il Calcolatore prezzi.

I nuovi utenti di Google Cloud potrebbero avere diritto a una prova senza costi.

Prima di iniziare

  1. Installa Google Cloud CLI.

  2. Configura gcloud CLI per utilizzare la tua identità federata.

    Per saperne di più, consulta Accedi a gcloud CLI con la tua identità federata.

  3. Per inizializzare gcloud CLI, esegui questo comando:

    gcloud init
  4. Crea o seleziona un progetto Google Cloud .

    Ruoli richiesti per selezionare o creare un progetto

    • Seleziona un progetto: la selezione di un progetto non richiede un ruolo IAM specifico. Puoi selezionare qualsiasi progetto per cui ti è stato concesso un ruolo.
    • Crea un progetto: per creare un progetto, devi disporre del ruolo Autore progetto (roles/resourcemanager.projectCreator), che contiene l'autorizzazione resourcemanager.projects.create. Scopri come concedere i ruoli.
    • Crea un progetto Google Cloud :

      gcloud projects create PROJECT_ID

      Sostituisci PROJECT_ID con un nome per il progetto Google Cloud che stai creando.

    • Seleziona il progetto Google Cloud che hai creato:

      gcloud config set project PROJECT_ID

      Sostituisci PROJECT_ID con il nome del progetto Google Cloud .

  5. Verifica che la fatturazione sia attivata per il tuo progetto Google Cloud .

  6. Abilita le API richieste, se non sono già abilitate:

    Ruoli richiesti per abilitare le API

    Per abilitare le API, devi disporre dell'autorizzazione serviceusage.services.enable. Se hai creato il progetto, probabilmente hai già questa autorizzazione tramite il ruolo Proprietario (roles/owner). In caso contrario, puoi ottenere questa autorizzazione tramite il ruolo Amministratore di Service Usage (roles/serviceusage.serviceUsageAdmin). Scopri come concedere i ruoli.

    gcloud services enable compute.googleapis.com container.googleapis.com file.googleapis.com logging.googleapis.com cloudresourcemanager.googleapis.com servicenetworking.googleapis.com
  7. Concedi i ruoli al tuo account utente. Esegui il seguente comando una volta per ciascuno dei seguenti ruoli IAM: roles/compute.admin, roles/iam.serviceAccountUser, roles/cloudbuild.builds.editor, roles/artifactregistry.admin, roles/storage.admin, roles/serviceusage.serviceUsageAdmin

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

    Sostituisci quanto segue:

  8. Attiva il account di servizio predefinito per il tuo progetto Google Cloud :
      export PROJECT_NUMBER="$(gcloud projects describe "PROJECT_ID" --format "value(project_number)")"
      gcloud iam service-accounts enable "${PROJECT_NUMBER}-compute@ \
        --project=PROJECT_ID
  9. Concedi il ruolo Editor (roles/editor) al service account predefinito:
    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member="serviceAccount:PROJECT_NUMBER-compute@" \
        --role=roles/editor
  10. Crea credenziali di autenticazione locali per il tuo account utente:
    gcloud auth application-default login
  11. Attiva OS Login per il tuo progetto:
    gcloud compute project-info add-metadata --metadata=enable-oslogin=TRUE
  12. Accedi o crea un account Hugging Face.

Accedi a Gemma 3 utilizzando Hugging Face

Per utilizzare Hugging Face per accedere a Gemma 3, segui questi passaggi:

  1. Accedi a Hugging Face
  2. Crea un token di accesso write Hugging Face.
    Fai clic su Your Profile (Il tuo profilo) > Settings (Impostazioni) > Access tokens (Token di accesso) > +Create new token (+Crea nuovo token).
  3. Copia e salva il valore del token write access. Lo utilizzerai più avanti in questo tutorial.

prepara l'ambiente

Per preparare l'ambiente, imposta quanto segue:

export PROJECT_ID="YOUR_PROJECT_ID"
export CLUSTER_NAME="YOUR_CLUSTER_NAME"
export CLUSTER_REGION="YOUR_REGION"
export RESERVATION="YOUR_RESERVATION_NAME"
export HF_TOKEN="YOUR_HF_TOKEN"
export ARTIFACT_REPO_LOCATION="YOUR_ARTIFACT_REGISTRY_LOCATION"
export NUM_NODES="YOUR_NUMBER_OF_NODES"

gcloud config set project "${PROJECT_ID}"
gcloud config set billing/quota_project "${PROJECT_ID}"

Sostituisci quanto segue:

  • PROJECT_ID: il nome del progetto Google Cloud in cui vuoi creare il cluster GKE.

  • CLUSTER_NAME: il nome del cluster GKE da creare.

  • CLUSTER_REGION: la regione in cui vuoi creare il tuo cluster GKE. Puoi creare il cluster solo nella regione in cui esiste la prenotazione.

  • RESERVATION: l'identificatore della capacità riservata.

  • HF_TOKEN: il token di accesso Hugging Face che hai creato nella sezione precedente.

  • ARTIFACT_REPO_LOCATION: la località in cui vuoi creare il repository Artifact Registry.

  • NUM_NODES: il numero di nodi che vuoi che abbia il cluster GKE.

  • NETWORK: la rete da utilizzare.

Crea un cluster GKE multi-host in modalità Autopilot

Crea un cluster GKE multi-host in modalità Autopilot:

gcloud container clusters create-auto "${CLUSTER_NAME}" \
    --project="${PROJECT_ID}" \
    --location="${CLUSTER_REGION}" \
    --release-channel=rapid

La creazione del cluster GKE potrebbe richiedere un po' di tempo. Per verificare che Google Cloud abbia terminato la creazione del cluster, vai a Cluster Kubernetes nella console Google Cloud .

Ottieni le credenziali per il tuo cluster GKE

Configura kubectl per comunicare con il cluster GKE appena creato eseguendo questo comando:

gcloud container clusters get-credentials "${CLUSTER_NAME}" \
    --location="${CLUSTER_REGION}"

Crea un secret di Kubernetes per le credenziali di Hugging Face

Per creare un secret di Kubernetes per le credenziali di Hugging Face, segui questi passaggi:

  1. Configura kubectl per comunicare con il tuo cluster GKE:

    gcloud container clusters get-credentials "${CLUSTER_NAME}" \
        --location="${CLUSTER_REGION}"
  2. Crea un secret di Kubernetes per archiviare il token Hugging Face:

    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token="${HF_TOKEN}" \
        --dry-run=client -o yaml | kubectl apply -f -

Prepara il workload

Per preparare il workload, segui questi passaggi:

  1. Crea script per i workload.

  2. Utilizza Docker e Cloud Build per creare un container di ottimizzazione.

Crea script del workload

Per creare gli script utilizzati dal workload di ottimizzazione, segui questi passaggi:

  1. Crea una directory per gli script del workload. Utilizza questa directory come directory di lavoro.

    mkdir llm-finetuning-gemma
    cd llm-finetuning-gemma
  2. Crea il file cloudbuild.yaml per utilizzare Google Cloud Build. Questo file crea il container del workload e lo archivia in Artifact Registry:

    steps:
    - name: 'gcr.io/cloud-builders/docker'
      args: [ 'build', '-t', '$_ARTIFACT_REPO_LOCATION-docker.pkg.dev/$PROJECT_ID/gemma/finetune-gemma-multihost-gpu:1.0.0', '.' ]
    images:
    - '$_ARTIFACT_REPO_LOCATION-docker.pkg.dev/$PROJECT_ID/gemma/finetune-gemma-multihost-gpu:1.0.0'
  3. Crea un file Dockerfile per definire l'ambiente e installare le dipendenze necessarie per completare il job di ottimizzazione:

    FROM nvidia/cuda:12.8.1-cudnn-devel-ubuntu24.04
    RUN apt-get update && \
        apt-get -y install python3 python3-dev gcc python3-pip python3-venv git curl vim
    RUN python3 -m venv /opt/venv
    ENV PATH="/opt/venv/bin:/usr/local/nvidia/bin:$PATH"
    ENV LD_LIBRARY_PATH="/usr/local/nvidia/lib64:$LD_LIBRARY_PATH"
    RUN pip3 install setuptools wheel packaging ninja
    RUN pip3 install torch torchvision torchaudio  --index-url https://download.pytorch.org/whl/cu128
    
    RUN pip3 install \
        transformers==4.53.3 \
        datasets==4.0.0 \
        accelerate==1.9.0 \
        evaluate==0.4.5 \
        bitsandbytes==0.46.1 \
        trl==0.19.1 \
        peft==0.16.0 \
        tensorboard==2.20.0 \
        protobuf==6.31.1 \
        sentencepiece==0.2.0
    COPY finetune.py /finetune.py
    COPY accel_fsdp_gemma3_config.yaml /accel_fsdp_gemma3_config.yaml
    CMD accelerate launch --config_file accel_fsdp_gemma3_config.yaml finetune.py
  4. Crea il file accel_fsdp_gemma3_config.yaml. Questo file di configurazione indica ad Hugging Face Accelerate di suddividere il job di ottimizzazione su più GPU.

    compute_environment: LOCAL_MACHINE
    debug: false
    distributed_type: FSDP
    downcast_bf16: 'no'
    enable_cpu_affinity: false
    fsdp_config:
      fsdp_activation_checkpointing: false
      fsdp_auto_wrap_policy: TRANSFORMER_BASED_WRAP
      fsdp_cpu_ram_efficient_loading: true
      fsdp_offload_params: false
      fsdp_reshard_after_forward: true
      fsdp_state_dict_type: FULL_STATE_DICT
      fsdp_transformer_layer_cls_to_wrap: Gemma3DecoderLayer
      fsdp_version: 2
    machine_rank: 0
    main_training_function: main
    mixed_precision: bf16
    num_machines: 2
    num_processes: 16
    rdzv_backend: static
    same_network: true
    tpu_env: []
    tpu_use_cluster: false
    tpu_use_sudo: false
    use_cpu: false
  5. Crea il file finetune.yaml:

    apiVersion: resource.k8s.io/v1
    kind: ResourceClaimTemplate
    metadata:
      name: mrdma
    spec:
      spec:
        devices:
          requests:
          - name: mrdma
            exactly:
              deviceClassName: mrdma.google.com
    ---
    apiVersion: jobset.x-k8s.io/v1alpha2
    kind: JobSet
    metadata:
      name: finetune-jobset
      namespace: default
    spec:
      failurePolicy:
        maxRestarts: 2
      replicatedJobs:
      - name: workers
        replicas: 1
        template:
          spec:
            parallelism: ${NUM_NODES}
            completions: ${NUM_NODES}
            backoffLimit: 0
            template:
              metadata:
                annotations:
                  kubectl.kubernetes.io/default-container: finetuner
              spec:
                terminationGracePeriodSeconds: 600
                restartPolicy: OnFailure
                nodeSelector:
                  cloud.google.com/compute-class: "Accelerator"
                  cloud.google.com/gke-accelerator: "nvidia-b200"
                  cloud.google.com/reservation-name: ${RESERVATION}
                  cloud.google.com/reservation-affinity: "specific"
                  cloud.google.com/gke-gpu-driver-version: latest
                containers:
                - name: finetuner
                  image: $IMAGE_URL
                  command: ["bash", "-c"]
                  args:
                  - |
                    NUM_PROCESSES=$(( ${NUM_NODES} * 8 ))
                    accelerate launch \
                      --config_file accel_fsdp_gemma3_config.yaml \
                      --num_machines ${NUM_NODES} \
                      --num_processes ${NUM_PROCESSES} \
                      --machine_rank ${JOB_COMPLETION_INDEX} \
                      --main_process_ip finetune-jobset-workers-0-0.finetune-jobset.default.svc.cluster.local \
                      --main_process_port 29500 \
                      finetune.py \
                      --model_id google/gemma-3-12b-pt \
                      --output_dir gemma-12b-text-to-sql \
                      --per_device_train_batch_size 8 \
                      --gradient_accumulation_steps 8 \
                      --num_train_epochs 3 \
                      --learning_rate 1e-5 \
                      --save_strategy steps \
                      --save_steps 100
                  resources:
                    limits:
                      nvidia.com/gpu: "8"
                  env:
                  - name: HF_TOKEN
                    valueFrom:
                      secretKeyRef:
                        name: hf-secret
                        key: hf_api_token
                  - name: NUM_NODES
                    value: "${NUM_NODES}"
                  volumeMounts:
                  - mountPath: /dev/shm
                    name: dshm
                volumes:
                - name: dshm
                  emptyDir:
                    medium: Memory
  6. Crea il file finetune.py:

    import torch
    import argparse
    import subprocess
    from datasets import load_dataset
    from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig, AutoConfig
    from peft import LoraConfig, prepare_model_for_kbit_training, get_peft_model
    from trl import SFTTrainer, SFTConfig
    from huggingface_hub import login
    
    def get_args():
        def str2bool(v):
            if isinstance(v, bool):
                return v
            return v.lower() in ("yes", "true", "t", "1")
    
        parser = argparse.ArgumentParser()
        parser.add_argument("--model_id", type=str, default="google/gemma-3-12b-pt", help="Hugging Face model ID")
        parser.add_argument("--hf_token", type=str, default=None, help="Hugging Face token for private models")
        parser.add_argument("--trust_remote", type=str2bool, default=False, help="Trust remote code when loading tokenizer")
        parser.add_argument("--use_fast", type=str2bool, default=True, help="Determines if a fast Rust-based tokenizer should be used")
        parser.add_argument("--dataset_name", type=str, default="philschmid/gretel-synthetic-text-to-sql", help="Hugging Face dataset name")
        parser.add_argument("--output_dir", type=str, default="gemma-12b-text-to-sql", help="Directory to save model checkpoints")
    
        # LoRA arguments
        parser.add_argument("--lora_r", type=int, default=16, help="LoRA attention dimension")
        parser.add_argument("--lora_alpha", type=int, default=16, help="LoRA alpha scaling factor")
        parser.add_argument("--lora_dropout", type=float, default=0.05, help="LoRA dropout probability")
        # SFTConfig arguments
        parser.add_argument("--max_seq_length", type=int, default=512, help="Maximum sequence length")
        parser.add_argument("--num_train_epochs", type=int, default=3, help="Number of training epochs")
        parser.add_argument("--per_device_train_batch_size", type=int, default=8, help="Batch size per device during training")
        parser.add_argument("--gradient_accumulation_steps", type=int, default=1, help="Gradient accumulation steps")
        parser.add_argument("--learning_rate", type=float, default=1e-5, help="Learning rate")
        parser.add_argument("--logging_steps", type=int, default=10, help="Log every X steps")
        parser.add_argument("--save_strategy", type=str, default="steps", help="Checkpoint save strategy")
        parser.add_argument("--save_steps", type=int, default=100, help="Save checkpoint every X steps")
        parser.add_argument("--push_to_hub", action='store_true', help="Push model back up to HF")
        parser.add_argument("--hub_private_repo", type=str2bool, default=True, help="Push to a private repo")
        return parser.parse_args()
    
    def main():
        args = get_args()
        # --- 1. Setup and Login ---
        if args.hf_token:
            login(args.hf_token)
        # --- 2. Create and prepare the fine-tuning dataset ---
        dataset = load_dataset(args.dataset_name, split="train")
        dataset = dataset.shuffle().select(range(12500))
        dataset = dataset.train_test_split(test_size=2500/12500)
        # --- 3. Configure Model and Tokenizer ---
        if torch.cuda.is_available() and torch.cuda.get_device_capability()[0] >= 8:
            torch_dtype_obj = torch.bfloat16
            torch_dtype_str = "bfloat16"
        else:
            torch_dtype_obj = torch.float16
            torch_dtype_str = "float16"
        tokenizer = AutoTokenizer.from_pretrained(args.model_id, trust_remote_code=args.trust_remote, use_fast=args.use_fast)
        tokenizer.pad_token = tokenizer.eos_token
        gemma_chat_template = (
            "{{ bos_token }}"
            "{% if messages[0]['role'] == 'system' %}{{ messages[0]['content'] }}{% endif %}"
            "{% for message in messages %}"
            "{% if message['role'] == 'user' %}<start_of_turn>user\n{{ message['content'] }}<end_of_turn>\n{% elif message['role'] == 'assistant' %}<start_of_turn>model\n{{ message['content'] }}<end_of_turn>\n{% endif %}"
            "{% endfor %}"
        )
        tokenizer.chat_template = gemma_chat_template
        # --- 4. Define the Formatting Function ---
        def formatting_func(example):
            system_message = "You are a text to SQL query translator. Users will ask you questions in English and you will generate a SQL query based on the provided SCHEMA."
            user_prompt = "Given the <USER_QUERY> and the <SCHEMA>, generate the corresponding SQL command to retrieve the desired data, considering the query's syntax, semantics, and schema constraints.\n\n<SCHEMA>\n{context}\n</SCHEMA>\n\n<USER_QUERY>\n{question}\n</USER_QUERY>\n"
    
            messages = [
                {"role": "user", "content": user_prompt.format(question=example["sql_prompt"], context=example["sql_context"])},
                {"role": "assistant", "content": example["sql"]}
            ]
            return tokenizer.apply_chat_template(messages, tokenize=False)
        # --- 5. Load Model and Apply PEFT ---
        config = AutoConfig.from_pretrained(args.model_id)
        config.use_cache = False
        print("Loading base model...")
        model = AutoModelForCausalLM.from_pretrained(
            args.model_id,
            config=config,
            attn_implementation="eager",
            torch_dtype=torch_dtype_obj,
        )
    
        model = prepare_model_for_kbit_training(model)
        peft_config = LoraConfig(
            lora_alpha=args.lora_alpha,
            lora_dropout=args.lora_dropout,
            r=args.lora_r,
            bias="none",
            target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
            task_type="CAUSAL_LM",
        )
        print("Applying PEFT configuration...")
        model = get_peft_model(model, peft_config)
        model.print_trainable_parameters()
        # --- 6. Configure Training Arguments ---
        training_args = SFTConfig(
            output_dir=args.output_dir,
            max_seq_length=args.max_seq_length,
            num_train_epochs=args.num_train_epochs,
            per_device_train_batch_size=args.per_device_train_batch_size,
            gradient_accumulation_steps=args.gradient_accumulation_steps,
            learning_rate=args.learning_rate,
            logging_steps=args.logging_steps,
            save_strategy=args.save_strategy,
            save_steps=args.save_steps,
            packing=False,
            gradient_checkpointing=True,
            gradient_checkpointing_kwargs={"use_reentrant": False},
            optim="adamw_torch",
            fp16=True if torch_dtype_obj == torch.float16 else False,
            bf16=True if torch_dtype_obj == torch.bfloat16 else False,
            max_grad_norm=0.3,
            warmup_ratio=0.03,
            lr_scheduler_type="constant",
            push_to_hub=args.push_to_hub,
            hub_private_repo=args.hub_private_repo,
            report_to="tensorboard",
            dataset_kwargs={
                "add_special_tokens": False,
                "append_concat_token": True,
            }
        )
        # --- 7. Create Trainer and Start Training ---
        trainer = SFTTrainer(
            model=model,
            args=training_args,
            train_dataset=dataset["train"],
            eval_dataset=dataset["test"],
            formatting_func=formatting_func,
        )
        print("Starting training...")
        trainer.train()
        print("Training finished.")
        # --- 8. Save the final model ---
        print(f"Saving final model to {args.output_dir}")
        trainer.save_model(args.output_dir)
        if torch.distributed.is_initialized():
            torch.distributed.destroy_process_group()
    
    if __name__ == "__main__":
        main()

Utilizza Docker e Cloud Build per creare un container di ottimizzazione

  1. Crea un repository Docker di Artifact Registry:

    gcloud artifacts repositories create gemma \
        --repository-format=docker \
        --location="${ARTIFACT_REPO_LOCATION}" \
        --description="Repository for Gemma fine tuning workload containers" || true
  2. Installa le definizioni di risorse personalizzate (CRD) JobSet necessarie per orchestrare i workload multi-host.

    kubectl apply --server-side -f https://github.com/kubernetes-sigs/jobset/releases/download/v0.12.0/manifests.yaml

    JobSet è un'estensione di Kubernetes utilizzata per gestire gruppi di job correlati. Per saperne di più su JobSet, consulta la documentazione esterna di JobSet.

  3. Nella directory llm-finetuning-gemma che hai creato in un passaggio precedente, esegui questo comando per creare l'immagine di ottimizzazione ed eseguirne il push ad Artifact Registry.

    gcloud builds submit . --substitutions=_ARTIFACT_REPO_LOCATION="${ARTIFACT_REPO_LOCATION}"
  4. Esporta l'URL dell'immagine. Lo utilizzerai in un passaggio successivo di questo tutorial:

    export IMAGE_URL="${ARTIFACT_REPO_LOCATION}-docker.pkg.dev/${PROJECT_ID}/gemma/finetune-gemma-gpu:1.0.0"

Avvia il workload di ottimizzazione

Per avviare il workload di ottimizzazione, segui questi passaggi:

  1. Applica il manifest di ottimizzazione per creare il job di ottimizzazione:

    envsubst '${RESERVATION} ${IMAGE_URL} ${NUM_NODES}' < finetune.yaml | kubectl apply -f -

    Poiché utilizzi i cluster in modalità GKE Autopilot, l'avvio del nodo abilitato per la GPU potrebbe richiedere alcuni minuti.

  2. Monitora il job eseguendo questo comando:

    watch kubectl get pods
  3. Controlla i log del pod del worker primario eseguendo questo comando:

    kubectl logs -l "job-name=finetune-jobset-workers-0" -f

    La risorsa del job scarica i dati del modello, quindi ottimizza il modello su tutte e otto le GPU. Il download richiede circa cinque minuti. Al termine del download, il processo di ottimizzazione richiede circa due ore e 30 minuti.

Monitora il workload

Puoi monitorare l'utilizzo delle GPU nel tuo cluster GKE per verificare che il job di ottimizzazione venga eseguito in modo efficiente. Per farlo, apri il seguente link nel browser:

echo "https://console.cloud.google.com/kubernetes/clusters/details/${CLUSTER_REGION}/${CLUSTER_NAME}/observability?mods=monitoring_api_prod&project=${PROJECT_ID}&pageState=("timeRange":("duration":"PT1H"),"nav":("section":"gpu"),"groupBy":("groupByType":"namespacesTop5"))"

Quando monitori il workload, puoi visualizzare quanto segue:

  • Utilizzo delle GPU: per un job di ottimizzazione integro, dovresti vedere l'utilizzo di tutte e 8 le GPU aumentare e stabilizzarsi a un livello elevato durante l'addestramento.
  • Durata del job: il completamento del job dovrebbe richiedere circa 10 minuti sul cluster A4 specificato.

Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, elimina il progetto che contiene le risorse oppure mantieni il progetto ed elimina le singole risorse.

Elimina le risorse

  1. Per eliminare il JobSet, esegui questo comando:

    kubectl delete jobset finetune-jobset
  2. Per eliminare il cluster GKE, esegui questo comando:

    gcloud container clusters delete "${CLUSTER_NAME}" \
        --region="${CLUSTER_REGION}"
  3. Per eliminare il repository Artifact Registry, esegui questo comando:

    gcloud artifacts repositories delete gemma \
        --location="${ARTIFACT_REPO_LOCATION}" \
        --quiet

Elimina il progetto

Elimina un progetto Google Cloud :

gcloud projects delete PROJECT_ID

Passaggi successivi