Lección 6 — Exploiter

Coûts et nettoyage

8 min de lectura

Este contenido aún no está traducido. Se muestra la versión original.

L'argument de départ de ce projet était double : ne plus payer une VM allumée en permanence pour vingt minutes de travail par nuit, et ne plus payer deux fois les appels Gemini lors des relances. Il faut maintenant le prouver avec des chiffres mesurés, dire comment ils évoluent quand le volume est multiplié par mille, puis supprimer l'environnement de test. Un workflow planifié oublié continue de tourner chaque nuit, et d'appeler Gemini sur des entrées vides ou, pire, sur de vraies données copiées « pour essayer ».

Le mécanisme

Chaque service est facturé à l'usage, avec des unités différentes :

flowchart LR
  N["Une nuit<br/>29 avis"] --> G["Vertex AI<br/>tokens d'entrée et de sortie"]
  N --> R["Cloud Run jobs<br/>vCPU-s et Gio-s des tâches"]
  N --> W["Workflows<br/>pas exécutés"]
  N --> SC["Cloud Scheduler<br/>par planification et par mois"]
  N --> GCS["Cloud Storage<br/>Go stockés, opérations"]
  G --> F["Facture"]
  R --> F
  W --> F
  SC --> F
  GCS --> F
PosteUnitéCe qui le fait grossir
Gemini 2.5 Flash (Vertex AI)0,30 parmilliondetokensd′entreˊe,2,50par million de tokens d'entrée, 2,50 en sortie (tarif public au 2026-10-16)le nombre d'avis, la longueur des réponses, les relances qui recalculent
Cloud Run jobsdurée de chaque tâche × vCPU et mémoire allouésle démarrage des conteneurs, plus que le calcul lui-même ici
Workflowspas exécutés ; les premiers milliers de chaque mois sont gratuitsles boucles (for sur les tranches et les thèmes)
Cloud Scheduler0,10 $ par planification et par mois, 3 planifications gratuites par compte de facturationle nombre de planifications, pas leur fréquence
Cloud Storagequelques Ko par nuitnégligeable, sauf rétention de données brutes sur des années

La mise en œuvre

Les tokens sont mesurés à la source et remontés jusqu'à la notification : tokens dans chaque stats.json (somme de usage_metadata.total_token_count des lots), tokens_synthese lu dans la réponse du connecteur Vertex AI (gemini.usageMetadata.totalTokenCount). La durée des tâches est dans les journaux (duree_ms) et dans gcloud run jobs executions list.

Le nettoyage supprime dans l'ordre qui évite qu'un composant se réveille pendant la suppression : d'abord la planification, puis le workflow et le job, puis ce qu'ils utilisent.

files/infra/cloud-down.sh (extrait)
echo "==> Planification d'abord (plus aucun déclenchement), puis workflow et job"
$G scheduler jobs delete "$P-nuit" --location="$REGION" 2>/dev/null
$G workflows delete "$P" --location="$REGION" 2>/dev/null
$G run jobs delete "$P-job" --region="$REGION" 2>/dev/null
...
# Les listes IAM sont à cohérence différée : un compte supprimé peut y figurer encore quelques secondes.
for i in 1 2 3; do
  "$(dirname "$0")/cloud-status.sh" --strict && exit 0
  [ "$i" -lt 3 ] && { echo "Nouvelle vérification dans 15 s."; sleep 15; }
done
exit 1

Résultat obtenu (2026-10-16)

Une nuit de 29 avis, mesurée sur la dernière exécution planifiée :

MesureValeur
Tokens du classement (6 tâches, 6 lots)4 826
Tokens de la synthèse1 538
Durée de l'exécution du workflow74 s (déclenchement planifié forcé)
Durée de chaque exécution de job (2 tâches)18 à 22 s, dont environ 2 s de travail par tâche
Reprise après perte d'une tranche73 s, 716 tokens de classement au lieu de 4 826

Coût Gemini de cette nuit : entre 0,2 et 1,6 centime selon la part de tokens de sortie (les compteurs enregistrés sont des totaux ; la borne basse les facture tous au tarif d'entrée, la borne haute au tarif de sortie). Cloud Run, Workflows, Storage : moins d'un centime à eux trois. Sur un mois, la facture du traitement reste sous l'euro, quand une petite VM allumée en permanence coûte de l'ordre de la dizaine d'euros avant même le premier appel Gemini.

À 30 000 avis par nuit, avec la même proportion de tokens (environ 165 par avis pour le classement), il faut compter 5 millions de tokens, soit 1,5 à 12,5 $ par nuit selon la répartition entrée/sortie. C'est à ce volume que la prédiction par lot de Vertex AI, facturée moitié prix mais sans garantie de délai court, devient un vrai sujet (leçon 1).

L'inventaire avant suppression, puis la suppression vérifiée :

$ make cloud-status
scheduler.job lab-batch-nocturne-nuit
workflows.workflow lab-batch-nocturne
run.job lab-batch-nocturne-job
pubsub.topic lab-batch-nocturne-fin
pubsub.subscription lab-batch-nocturne-fin-sub
storage.bucket lab-batch-nocturne-<projet>
artifacts.repo lab-batch-nocturne
iam.sa lab-batch-nocturne-wf@<projet>.iam.gserviceaccount.com
iam.sa lab-batch-nocturne-sched@<projet>.iam.gserviceaccount.com
iam.sa lab-batch-nocturne-job@<projet>.iam.gserviceaccount.com
iam.binding roles/aiplatform.user
iam.binding roles/aiplatform.user
iam.binding roles/logging.logWriter
iam.binding roles/run.viewer
iam.binding roles/workflows.invoker

$ make cloud-down
==> Planification d'abord (plus aucun déclenchement), puis workflow et job
==> Pub/Sub, dépôt d'images, bucket (entrées, sorties, rapports, sources de build)
==> Droits au niveau projet, puis comptes de service
Aucune ressource du lab batch-nocturne dans le projet.
make cloud-down PROJECT=<projet>  16,49s user 2,14s system 37% cpu 49,910 total

$ make cloud-status
Aucune ressource du lab batch-nocturne dans le projet.

Un balayage indépendant de cloud-status.sh (services et jobs Cloud Run de toutes les régions, workflows de toutes les régions, sujets, abonnements et instantanés Pub/Sub, buckets, dépôts Artifact Registry, métriques et puits de journaux, politiques d'alerte, comptes de service, politique IAM du projet, datasets BigQuery, secrets) n'a trouvé aucune ressource portant le préfixe du lab. Restent les journaux d'exécution, conservés 30 jours par le bucket de journaux par défaut puis effacés.

Ce qui casse en production

  • Planification supprimée en dernier. Si le workflow est supprimé avant la planification, Scheduler continue à appeler une URL qui répond 404 chaque nuit, et échoue en silence. À l'inverse, supprimer le bucket avant le workflow peut faire échouer une exécution en cours, qui notifie un échec. Parade : planification, puis workflow et job, puis les ressources qu'ils utilisent.
  • Coût caché des relances complètes. Sans les marqueurs de tranche, chaque relance repaie toute la nuit. Le chiffre à surveiller : les tokens d'une exécution de reprise comparés à ceux d'une nuit normale (716 contre 4 826 ici).
  • Tokens de sortie non mesurés. Le projet enregistre des totaux ; impossible de dire précisément ce qu'a coûté une nuit. Parade : l'exercice ci-dessous, et un budget avec alerte sur le projet.
  • Environnement de test oublié. Un make cloud-up lancé pour une démonstration et jamais nettoyé fait une exécution par nuit, indéfiniment. Parade : make cloud-down en fin de séance, et make cloud-status dans la routine de la semaine.

La règle de décision

Mesurez les tokens à la source et faites-les remonter jusqu'à la notification ; c'est le seul poste qui grossit vraiment avec le volume. Comparez le coût d'une reprise à celui d'une nuit : s'ils sont proches, l'idempotence par tranche ne fonctionne pas. Supprimez dans l'ordre inverse du déclenchement, et prouvez la suppression par une liste vide, vérifiée deux fois.

Pratique

La facture Gemini du projet double un mois donné, alors que le volume d'avis est stable. Les notifications montrent des tokens_jobs normaux chaque nuit. Que cherchez-vous ?

Vous devez supprimer l'environnement alors qu'une exécution est en cours (ACTIVE). Que faites-vous ?

Exercice. Enregistrez séparément les tokens d'entrée et de sortie, de la tâche jusqu'à la notification, pour que le coût d'une nuit soit calculable exactement. Quels fichiers changent ?

Voir la solution
  • job/batch.py : GeminiClassifier renvoie (classements, (meta.prompt_token_count, meta.candidates_token_count)) ; classify_checked et run_task cumulent tokens_entree et tokens_sortie dans stats ;
  • tests/test_batch.py : le faux Gemini renvoie un couple, les assertions vérifient les deux sommes ;
  • infra/workflow.yaml : deux cumuls dans cumuler (tokens_entree, tokens_sortie), et pour la synthèse gemini.usageMetadata.promptTokenCount et gemini.usageMetadata.candidatesTokenCount dans resume ;
  • e2e.py : le coût calculé dans le rapport, entree * 0.30e-6 + sortie * 2.50e-6, avec le tarif en constante datée.

Gardez le total aussi : c'est lui que comparent les tableaux existants.

  • Je mesure les tokens à la source et je les fais remonter jusqu'à la notification.
  • Je compare le coût d'une reprise à celui d'une nuit complète.
  • J'extrapole un coût à un volume cible, avec ses bornes.
  • Je supprime l'environnement dans le bon ordre et je prouve qu'il ne reste rien.