Progetta job GPU resilienti a lunga esecuzione

Questo tutorial spiega come progettare ed eseguire il deployment di job Cloud Run resilienti con GPU abilitate, in esecuzione per un massimo di 12 ore.

Sebbene Cloud Run supporti timeout delle attività estesi, non offre uno SLA per l'esecuzione senza interruzioni. Poiché non viene eseguita la migrazione live delle attività abilitate per la GPU, la manutenzione settimanale di routine le interromperà a metà esecuzione. Di conseguenza, è prevedibile che qualsiasi attività estesa venga interrotta almeno una volta. Per assicurarti che il job venga completato correttamente senza sprecare risorse di computing, progetta l'applicazione in modo che esegua il checkpoint e riprenda, anziché fare affidamento su una singola esecuzione senza interruzioni.

Questo tutorial ti guida nell'implementazione del checkpointing, nella gestione dei segnali della piattaforma SIGTERM e nella configurazione dei tentativi massimi.

Obiettivi

In questo tutorial imparerai a:

  • Prepara un'applicazione Python che implementi il checkpointing e gestisca i segnali SIGTERM.
  • Crea un bucket Cloud Storage per rendere persistenti i checkpoint.
  • Crea ed esegui il deployment di un job Cloud Run con GPU abilitata, un timeout delle attività di 12 ore e una policy di ripetizione configurata.
  • Esegui il job e scopri come monitorare gli eventi di prerilascio nei log.

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. Accedi al tuo account Google Cloud . Se non conosci Google Cloud, crea un account per valutare le prestazioni dei nostri prodotti in scenari reali. I nuovi clienti ricevono anche 300 $di crediti senza costi per l'esecuzione, il test e il deployment di workload.
  2. Installa Google Cloud CLI.

  3. Se utilizzi un provider di identità (IdP) esterno, devi prima accedere a gcloud CLI con la tua identità federata.

  4. Per inizializzare gcloud CLI, esegui questo comando:

    gcloud init
  5. 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 .

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

  7. Installa Google Cloud CLI.

  8. Se utilizzi un provider di identità (IdP) esterno, devi prima accedere a gcloud CLI con la tua identità federata.

  9. Per inizializzare gcloud CLI, esegui questo comando:

    gcloud init
  10. 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 .

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

  12. Abilita le API Cloud Run, Artifact Registry, Cloud Build e Cloud Storage, 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 disponi già di questa autorizzazione tramite il ruolo Proprietario (roles/owner). In caso contrario, puoi ottenere questa autorizzazione tramite il ruolo Amministratore Service Usage (roles/serviceusage.serviceUsageAdmin). Scopri come concedere i ruoli.

    gcloud services enable run.googleapis.com cloudbuild.googleapis.com artifactregistry.googleapis.com storage.googleapis.com
  13. Imposta la regione come variabile di ambiente:
    export REGION=REGION
  14. Crea un account di servizio per il job:
    gcloud iam service-accounts create gpu-job-sa
  15. Assicurati di disporre di una [quota GPU sufficiente](/run/docs/configuring/jobs/gpu#request-quota) (ad esempio Nvidia L4) nella regione scelta.

Prepara l'applicazione

Per rendere resiliente un job a lunga esecuzione, la tua applicazione deve:

  1. Salva periodicamente i progressi (checkpointing) in una posizione permanente, ad esempio un bucket Cloud Storage montato.

    Hai circa 10 secondi dopo SIGTERM prima che l'attività venga terminata. Questo tempo non è sufficiente per scrivere un checkpoint di un modello di grandi dimensioni. Tratta il gestore SIGTERM come un flush best-effort solo di stati di piccole dimensioni e affidati al checkpoint periodico come protezione principale. Scegli l'intervallo di checkpoint in base alla quantità di lavoro che puoi permetterti di perdere, tenendo presente che checkpoint più frequenti significano più tempo non dedicato a un lavoro utile. L'obiettivo è raggiungere il giusto equilibrio per il tuo caso d'uso.

  2. Ripristina lo stato dall'ultimo checkpoint all'avvio.

  3. Ascolta il segnale SIGTERM, che Cloud Run invia 10 secondi prima del processo di manutenzione, e tenta di salvare un checkpoint finale prima di uscire.

L'esempio seguente illustra come implementare queste funzionalità.

Crea il codice dell'applicazione

  1. Crea una directory per il progetto:

    mkdir gc-run-gpu-resilience
    cd gc-run-gpu-resilience
    
  2. Crea un file denominato task.py e incolla lo script seguente per simulare un processo di addestramento o elaborazione batch che supporta il checkpointing e la gestione dei segnali:

    import os
    import sys
    import time
    import signal
    
    # The checkpoint directory will be mounted to this path
    CHECKPOINT_DIR = "/checkpoints"
    # Cloud Run sets these variables automatically
    JOB_NAME = os.environ.get("CLOUD_RUN_JOB", "local-job")
    TASK_INDEX = os.environ.get("CLOUD_RUN_TASK_INDEX", "0")
    CHECKPOINT_FILE = os.path.join(
        CHECKPOINT_DIR, f"{JOB_NAME}-task-{TASK_INDEX}-checkpoint.txt")
    TOTAL_STEPS = 100
    current_step = 0
    is_exiting = False
    
    def save_checkpoint(step):
        print(f"Saving checkpoint at step {step} to {CHECKPOINT_FILE}...")
        try:
            # Write to a temp file and rename to ensure atomicity
            temp_file = CHECKPOINT_FILE + ".tmp"
            with open(temp_file, "w") as f:
                f.write(str(step))
            os.rename(temp_file, CHECKPOINT_FILE)
            print("Checkpoint saved successfully.")
        except Exception as e:
            print(f"Failed to save checkpoint: {e}")
    
    def load_checkpoint():
        print(f"Looking for a checkpoint at {CHECKPOINT_FILE}...")
        global current_step
        if os.path.exists(CHECKPOINT_FILE):
            try:
                with open(CHECKPOINT_FILE, "r") as f:
                    current_step = int(f.read().strip())
                print(f"Resuming from checkpointed step {current_step}.")
            except Exception as e:
                print(f"Failed to load checkpoint: {e}. Starting from scratch.")
        else:
            print("No checkpoint found. Starting from scratch.")
    
    def sigterm_handler(signum, frame):
        global is_exiting
        print("Received SIGTERM signal from platform. Initiating graceful shutdown...")
        is_exiting = True
    
    # Register the signal handler for SIGTERM
    signal.signal(signal.SIGTERM, sigterm_handler)
    
    # Load existing checkpoint if any to resume work
    load_checkpoint()
    
    # Simulate work loop
    while current_step < TOTAL_STEPS and not is_exiting:
        print(f"Processing step {current_step}/{TOTAL_STEPS}...")
        # Simulate heavy GPU computation
        time.sleep(1)
        current_step += 1
        # Periodically save checkpoint (e.g., every 5 steps)
        if current_step % 5 == 0:
            save_checkpoint(current_step)
    
    if is_exiting:
        # Save final state before exiting due to preemption
        # Best-effort only: with ~10s before SIGKILL this will not complete for
        # large state. Periodic checkpointing (above) is what actually protects
        # you.
        save_checkpoint(current_step)
        # Note that save_checkpoint already writes-then-renames for atomicity,
        # which is good, but the reason it matters here is that a truncated
        # checkpoint from an interrupted final save must not clobber the last
        # good one.
        print("Graceful shutdown complete. Exiting with error code to trigger retry.")
        # Exit with a non-zero code to signal to Cloud Run that the task
        # was interrupted and needs to be retried from the checkpoint.
        sys.exit(143)
        # Note that this statement deliberately consumes of the configured retry
        # attempts.
    
    print("Task completed successfully.")
    sys.exit(0)
    
  3. Crea un Dockerfile per containerizzare l'applicazione:

Consulta questo esempio per un'implementazione più dettagliata e realistica.

Crea un bucket Cloud Storage per i checkpoint

Crea un bucket Cloud Storage per archiviare i file di checkpoint e concedi al account di servizio del job l'autorizzazione per accedervi.

  1. Crea il bucket:

    gcloud storage buckets create gs://gpu-checkpoints-PROJECT_ID \
        --location REGION
    
  2. Concedi il ruolo Amministratore oggetti Storage al account di servizio del job:

    gcloud storage buckets add-iam-policy-binding gs://gpu-checkpoints-PROJECT_ID \
        --member="serviceAccount:gpu-job-sa@PROJECT_ID.iam.gserviceaccount.com" \
        --role="roles/storage.objectAdmin"
    

Esegui il deployment del job

Esegui il deployment del job in Cloud Run:

gcloud beta run jobs deploy gpu-resilient-job \
    --source=. \
    --region REGION \
    --gpu 1 \
    --gpu-type nvidia-l4 \
    --no-gpu-zonal-redundancy \
    --task-timeout 168h \
    --max-retries 3 \
    --service-account gpu-job-sa@PROJECT_ID.iam.gserviceaccount.com \
    --add-volume=name=checkpoint-volume,type=cloud-storage,bucket=gpu-checkpoints-PROJECT_ID \
    --add-volume-mount=volume=checkpoint-volume,mount-path=/checkpoints

Tieni presente i seguenti flag critici:

  • --task-timeout 168h: consente l'esecuzione dell'attività per un massimo di 12 ore.
  • --max-retries 3: per i job GPU con un timeout superiore a 1 ora, ti consigliamo di impostare la configurazione predefinita di 3 nuovi tentativi. Per evitare errori silenziosi durante la manutenzione della piattaforma, ti consigliamo di non impostare 0 tentativi. Infine, ti consigliamo di prevedere un budget per almeno un nuovo tentativo per gli errori relativi alla manutenzione, nonché per nuovi tentativi aggiuntivi per gli errori dell'applicazione in un periodo di esecuzione di 12 ore.
  • --add-volume e --add-volume-mount: monta il bucket Cloud Storage su /checkpoints in modo che l'applicazione possa rendere persistente lo stato tra i nuovi tentativi di attività.

Esegui e monitora il job

  1. Esegui il job:

    gcloud run jobs execute gpu-resilient-job --region REGION
    

    Questo comando restituisce un messaggio simile al seguente:

    Execution [gpu-resilient-job-abcde] has been successfully started.
    
    View details about this execution by running `gcloud run jobs executions
    describe gpu-resilient-job-abcde`.
    
    See logs for this execution at: https://console.cloud.google.com/logs/viewer?project=YOUR_PROJECT_ID&resource=cloud_run_job/job_name/my-job/location/my-region/execution_id/gpu-resilient-job-abcde
    

    La stringa gpu-resilient-job-abcde è l'ID univoco generato automaticamente per questo tentativo di esecuzione specifico. Puoi utilizzarlo per monitorare o annullare questa esecuzione specifica. L'output include anche un link diretto alla pagina Esplora log nella console Google Cloud per visualizzare l'output o gli errori delle attività del job.

  2. Monitora i log. Se si verifica un evento di manutenzione, Cloud Run registra un avviso. Vedrai:

    • I log dell'applicazione: Received SIGTERM signal from platform. Initiating graceful shutdown... seguito da Saving checkpoint....
    • Un log di sistema che indica il prerilascio: Container instance preemption initiated by the platform due to underlying hardware maintenance. con motivo INSTANCE_PREEMPTED.
    • Cloud Run riavvia automaticamente l'attività.
    • I log dell'applicazione al riavvio: Resuming from checkpointed step X..

Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi aggiuntivi, elimina tutte le risorse di cui hai eseguito il deployment con questa guida rapida.

Elimina il repository

Cloud Run addebita solo il tempo di esecuzione del job. Tuttavia, ti potrebbero comunque essere addebitati i costi di archiviazione dell'immagine container in Artifact Registry. Per eliminare i repository Artifact Registry, segui i passaggi descritti in Elimina i repository nella documentazione di Artifact Registry.

Elimina il job

I job Cloud Run comportano costi solo quando è in esecuzione un'attività del job. Per eliminare il job Cloud Run, segui uno di questi passaggi:

Console

Per eliminare un job:

  1. Nella console Google Cloud , vai a Cloud Run:

    Vai a Cloud Run

  2. Individua il job che vuoi eliminare nell'elenco dei job e fai clic sulla relativa casella di controllo per selezionarlo.

  3. Fai clic su Elimina. In questo modo, vengono terminate tutte le esecuzioni dei job in corso e tutte le istanze di container in esecuzione.

gcloud

Per eliminare un job, esegui questo comando:

gcloud run jobs delete JOB_NAME

Sostituisci JOB_NAME con il nome del job.

Elimina il progetto di test

L'eliminazione del progetto Google Cloud interrompe la fatturazione per tutte le risorse al suo interno. Per rilasciare tutte le risorse Google Cloud del progetto, segui questi passaggi:

    Elimina un progetto Google Cloud :

    gcloud projects delete PROJECT_ID

Elimina il bucket Cloud Storage:

gcloud storage buckets delete gs://gpu-checkpoints-PROJECT_ID

Passaggi successivi