Ce tutoriel explique comment concevoir et déployer des jobs Cloud Run résilients avec GPU pouvant s'exécuter pendant 12 heures.
Bien que Cloud Run accepte des délais avant expiration des tâches prolongés, il ne propose pas de contrat de niveau de service pour une exécution ininterrompue. Étant donné que les tâches compatibles avec les GPU ne sont pas migrées à chaud, la maintenance hebdomadaire de routine les interrompt en cours d'exécution. Par conséquent, toute tâche prolongée doit être interrompue au moins une fois. Pour vous assurer que votre job se termine correctement sans gaspiller de ressources de calcul, concevez votre application de manière à ce qu'elle crée des points de contrôle et reprenne son exécution, plutôt que de vous fier à une seule exécution ininterrompue.
Ce tutoriel vous explique comment implémenter des points de contrôle, gérer les signaux SIGTERM de la plate-forme et configurer le nombre maximal de nouvelles tentatives.
Objectifs
Dans ce tutoriel, vous allez :
- Préparez une application Python qui implémente les points de contrôle et gère les signaux
SIGTERM. - Créez un bucket Cloud Storage pour conserver les points de contrôle.
- Créer et déployer un job Cloud Run avec un GPU activé, un délai avant expiration de 12 heures pour les tâches et une règle de nouvelle tentative configurée.
- Exécuter le job et comprendre comment surveiller les événements de préemption dans les journaux.
Coûts
Dans ce document, vous utilisez les composants facturables suivants de Google Cloud :
Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût.
Avant de commencer
-
Installez la Google Cloud CLI.
-
Configurez la gcloud CLI afin d'utiliser votre identité fédérée.
Pour en savoir plus, consultez Se connecter à la gcloud CLI avec votre identité fédérée.
-
Pour initialiser la gcloud CLI, exécutez la commande suivante :
gcloud init -
Créez ou sélectionnez un projet Google Cloud .
Rôles requis pour sélectionner ou créer un projet
- Sélectionner un projet : la sélection d'un projet ne nécessite pas de rôle IAM spécifique. Vous pouvez sélectionner n'importe quel projet pour lequel un rôle vous a été attribué.
-
Créer un projet : pour créer un projet, vous devez disposer du rôle "Créateur de projet" (
roles/resourcemanager.projectCreator), qui contient l'autorisationresourcemanager.projects.create. Découvrez comment attribuer des rôles.
-
Créez un projet Google Cloud :
gcloud projects create PROJECT_ID
Remplacez
PROJECT_IDpar le nom du projet Google Cloud que vous créez. -
Sélectionnez le projet Google Cloud que vous avez créé :
gcloud config set project PROJECT_ID
Remplacez
PROJECT_IDpar le nom de votre projet Google Cloud .
-
Vérifiez que la facturation est activée pour votre projet Google Cloud .
Activez les API Cloud Run, Artifact Registry, Cloud Build et Cloud Storage, si elles ne le sont pas déjà :
Rôles requis pour activer les API
Pour activer des API, vous devez disposer de l'autorisation
serviceusage.services.enable. Si vous avez créé le projet, vous disposez probablement déjà de cette autorisation via le rôle Propriétaire (roles/owner). Sinon, vous pouvez obtenir cette autorisation via le rôle Administrateur Service Usage (roles/serviceusage.serviceUsageAdmin). Découvrez comment attribuer des rôles.gcloud services enable run.googleapis.com
cloudbuild.googleapis.com artifactregistry.googleapis.com storage.googleapis.com - Définissez votre région en tant que variable d'environnement :
export REGION=REGION
- Créez un compte de service pour le job :
gcloud iam service-accounts create gpu-job-sa
- Assurez-vous de disposer d'un [quota de GPU suffisant](/run/docs/configuring/jobs/gpu#request-quota) (par exemple, Nvidia L4) dans la région de votre choix.
Préparer votre application
Pour qu'un job de longue durée soit résilient, votre application doit :
Enregistrez régulièrement la progression (point de contrôle) dans un emplacement persistant, tel qu'un bucket Cloud Storage installé.
Vous disposez d'environ 10 secondes après
SIGTERMavant que la tâche ne soit arrêtée. Ce délai n'est pas suffisant pour écrire un point de contrôle de modèle volumineux. Traitez le gestionnaireSIGTERMcomme un vidage au mieux d'un petit état uniquement, et fiez-vous aux points de contrôle périodiques comme protection principale. Choisissez l'intervalle de point de contrôle en fonction de la quantité de travail que vous pouvez vous permettre de perdre, en gardant à l'esprit que des points de contrôle plus fréquents signifient plus de temps non consacré à un travail utile. L'objectif est de trouver le bon équilibre pour votre cas d'utilisation.Restaurez l'état à partir du dernier point de contrôle au démarrage.
Écoutez le signal
SIGTERM, que Cloud Run envoie 10 secondes avant le processus de maintenance, et essayez d'enregistrer un point de contrôle final avant de quitter.
L'exemple suivant montre comment implémenter ces fonctionnalités.
Créer le code de l'application
Créez un répertoire pour votre projet :
mkdir gc-run-gpu-resilience cd gc-run-gpu-resilienceCréez un fichier nommé
task.pyet collez-y le script suivant pour simuler un processus d'entraînement ou de traitement par lot compatible avec les points de contrôle et la gestion des signaux :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)Créez un
Dockerfilepour conteneuriser l'application :
Consultez cet exemple pour une implémentation plus détaillée et réaliste.
Créer un bucket Cloud Storage pour les points de contrôle
Créez un bucket Cloud Storage pour stocker les fichiers de point de contrôle, puis accordez au compte de service de votre job l'autorisation d'y accéder.
Créez le bucket :
gcloud storage buckets create gs://gpu-checkpoints-PROJECT_ID \ --location REGIONAttribuez le rôle "Administrateur des objets Storage" au compte de service du job :
gcloud storage buckets add-iam-policy-binding gs://gpu-checkpoints-PROJECT_ID \ --member="serviceAccount:gpu-job-sa@PROJECT_ID." \ --role="roles/storage.objectAdmin"
Déployer le job
Déployez le job dans 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. \
--add-volume=name=checkpoint-volume,type=cloud-storage,bucket=gpu-checkpoints-PROJECT_ID \
--add-volume-mount=volume=checkpoint-volume,mount-path=/checkpoints
Notez les options critiques suivantes :
--task-timeout 168h: permet à la tâche de s'exécuter pendant 12 heures maximum.--max-retries 3: pour les jobs GPU dont le délai avant expiration est supérieur à une heure, nous vous recommandons de définir la configuration par défaut sur trois tentatives. Pour éviter les échecs silencieux lors de la maintenance de la plate-forme, nous vous déconseillons de définir 0 nouvelle tentative. Enfin, nous vous recommandons de prévoir un budget pour au moins une nouvelle tentative en cas d'échec lié à la maintenance, ainsi que pour des nouvelles tentatives supplémentaires en cas d'échec de l'application sur une période d'exécution de 12 heures.--add-volumeet--add-volume-mount: installe le bucket Cloud Storage sur/checkpointsafin que l'application puisse conserver l'état entre les nouvelles tentatives de tâche.
Exécuter et surveiller le job
Exécuter le job :
gcloud run jobs execute gpu-resilient-job --region REGIONCette commande renvoie un message semblable à celui-ci :
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-abcdeLa chaîne
gpu-resilient-job-abcdeest l'identifiant unique généré automatiquement pour cette tentative d'exécution spécifique. Vous pouvez l'utiliser pour surveiller ou annuler cette exécution spécifique. Le résultat inclut également un lien direct vers la page de l'explorateur de journaux dans la console Google Cloud pour afficher les résultats ou les erreurs de vos tâches de job.Surveillez les journaux. Si un événement de maintenance se produit, Cloud Run consigne un avertissement. Voici ce qui s'affiche alors :
- Les journaux de votre application :
Received SIGTERM signal from platform. Initiating graceful shutdown...suivi deSaving checkpoint.... - Un journal système indiquant la préemption :
Container instance preemption initiated by the platform due to underlying hardware maintenance.avec le motifINSTANCE_PREEMPTED. - Cloud Run redémarre automatiquement la tâche.
- Journaux de votre application au redémarrage :
Resuming from checkpointed step X..
- Les journaux de votre application :
Effectuer un nettoyage
Pour éviter que des frais supplémentaires ne soient facturés sur votre compte Google Cloud , supprimez toutes les ressources que vous avez déployées avec ce guide de démarrage rapide.
Supprimer votre dépôt
Cloud Run ne facture que la durée d'exécution de votre job. Toutefois, des frais peuvent vous être facturés pour le stockage de l'image de conteneur dans Artifact Registry. Pour supprimer des dépôts Artifact Registry, suivez les étapes de la section Supprimer des dépôts de la documentation Artifact Registry.
Supprimer votre job
Les jobs Cloud Run n'engendrent des coûts que lorsqu'une tâche de job est en cours d'exécution. Pour supprimer votre job Cloud Run, suivez l'une de ces étapes :
Console
Pour supprimer une tâche, procédez comme suit :
Dans la console Google Cloud , accédez à Cloud Run :
Recherchez le job que vous souhaitez supprimer dans la liste des jobs, puis cliquez sur la case correspondante pour le sélectionner.
Cliquez sur Supprimer. Cela met fin à toutes les exécutions de jobs en cours et à toutes les instances de conteneur en cours d'exécution.
gcloud
Pour supprimer un job, exécutez la commande suivante :
gcloud run jobs delete JOB_NAME
Remplacez JOB_NAME par le nom de la tâche.
Supprimer votre projet de test
La suppression de votre projet Google Cloud arrête la facturation de toutes les ressources de ce projet. Pour libérer toutes les ressources Google Cloud de votre projet, procédez comme suit :
Supprimer un projet Google Cloud :
gcloud projects delete PROJECT_ID
Supprimez le bucket Cloud Storage :
gcloud storage buckets delete gs://gpu-checkpoints-PROJECT_ID
Étapes suivantes
- Apprenez-en plus sur les nouvelles tentatives d'exécution et les points de contrôle de jobs.
- Consultez les bonnes pratiques pour les performances des GPU.