Passer en production avec Cloud Run
Cloud Run en trois phrases
Cloud Run exécute votre application dans un conteneur (un paquet qui contient le code, Python et les dépendances, décrit par un Dockerfile). Il lui donne une adresse HTTPS, démarre des copies (instances) quand les requêtes arrivent et les arrête quand il n'y en a plus. Vous payez à l'usage : au repos, avec zéro instance, le coût de calcul est nul.
flowchart LR S["Dossier files/ + Dockerfile"] -->|"gcloud run deploy --source"| B["Cloud Build : construit l'image"] B --> R["Artifact Registry : stocke l'image"] R --> C["Cloud Run : service HTTPS"] U["Utilisateurs"] --> C C -->|"compte de service"| V["Vertex AI : Gemini"] C -->|"stdout JSON"| L["Cloud Logging"]
Le conteneur
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Cloud Run fournit le port dans la variable PORT (8080 par défaut).
CMD exec uvicorn app:app --host 0.0.0.0 --port ${PORT:-8080}Les fichiers .dockerignore et .gcloudignore excluent .env et journal.jsonl : la clé ne part jamais dans l'image.
Quelle identité pour appeler Gemini ?
| Option | Comment | Quand |
|---|---|---|
| Vertex AI + compte de service (testée ici) | GOOGLE_GENAI_USE_VERTEXAI=TRUE, aucune clé : le service s'authentifie avec son compte de service (l'identité Google Cloud de l'application) | recommandé sur Google Cloud |
| Clé d'API dans Secret Manager | gcloud secrets create, puis --set-secrets GOOGLE_API_KEY=nom-du-secret:latest | vous restez sur l'API Gemini (non testée dans ce lab) |
| Clé en variable d'environnement en clair | --set-env-vars GOOGLE_API_KEY=... | jamais : visible par quiconque lit la configuration du service |
Le code ne change pas : genai.Client() lit ces variables. Seul le nom du modèle change, car le catalogue diffère entre l'API Gemini et Vertex AI (ici gemini-2.5-flash, disponible sur Vertex AI en europe-west1).
Déployer
gcloud run deploy lab-chatbot-metier --source . \
--region europe-west1 --project VOTRE_PROJET \
--labels formation-lab=chatbot-metier \
--set-env-vars GOOGLE_GENAI_USE_VERTEXAI=TRUE,GOOGLE_CLOUD_PROJECT=VOTRE_PROJET,GOOGLE_CLOUD_LOCATION=europe-west1,MODEL=gemini-2.5-flash \
--no-allow-unauthenticated --max-instances 1--source .: Cloud Build construit l'image à partir duDockerfile, sans Docker sur votre poste.--no-allow-unauthenticated: seules les personnes autorisées (avec un jeton) peuvent appeler le service, idéal pour un test. Pour un vrai chatbot public, il faudra--allow-unauthenticatedet une limite de débit.--max-instances 1: plafonne la dépense en cas d'abus.- Le compte de service du service doit avoir le rôle Utilisateur Vertex AI (
roles/aiplatform.user).
Pourquoi ne pas passer la clé d'API avec --set-env-vars ?
Toute personne qui peut décrire le service voit ses variables d'environnement. Un secret est stocké chiffré, avec des droits d'accès séparés ; un compte de service supprime même le besoin d'une clé.
La mémoire de conversation d'Alti est en RAM. Que se passe-t-il sur Cloud Run avec 3 instances ?
Chaque instance a sa propre RAM. C'est pour cela que ce lab fixe --max-instances 1 ; au-delà, la mémoire doit aller dans une base partagée (Firestore, Memorystore…).
Nettoyer
Un service Cloud Run au repos ne coûte presque rien, mais l'image stockée, si. Supprimez tout ce que le déploiement a créé :
gcloud run services delete lab-chatbot-metier --region europe-west1 --project VOTRE_PROJET --quiet
gcloud artifacts docker images delete \
europe-west1-docker.pkg.dev/VOTRE_PROJET/cloud-run-source-deploy/lab-chatbot-metier \
--delete-tags --project VOTRE_PROJET --quiet
gcloud storage rm -r gs://run-sources-VOTRE_PROJET-europe-west1/services/lab-chatbot-metier/
# Vérification : ces commandes ne doivent plus rien afficher pour lab-chatbot-metier
gcloud run services list --region europe-west1 --project VOTRE_PROJET | grep chatbot
gcloud artifacts docker images list europe-west1-docker.pkg.dev/VOTRE_PROJET/cloud-run-source-deploy | grep chatbotExercice : rendre le service public sans se ruiner
Listez trois protections à ajouter avant de passer en --allow-unauthenticated.
Voir la solution
- Plafonds :
--max-instancesbas et une alerte de budget sur le projet. - Limite de débit par adresse IP ou par session (par exemple avec Cloud Armor devant un équilibreur de charge, ou un compteur simple dans l'application) : un robot ne doit pas pouvoir enchaîner des milliers de questions.
- Limites d'entrée déjà en place :
max_length=2000sur le message, mémoire limitée à 10 messages, journal masqué.
On peut aussi exiger une vérification anti-robot (reCAPTCHA) sur la page.
Résultat obtenu
Déploiement réel le 2026-10-15 (projet de test, europe-west1), puis appels avec un jeton d'identité :
Service [lab-chatbot-metier] revision [lab-chatbot-metier-00001-vnp] has been deployed and is serving 100 percent of traffic.
$ curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" $URL/sante
{"ok":true,"modele":"gemini-2.5-flash"}
$ curl -s -o /dev/null -w "%{http_code}" $URL/sante # sans jeton
403
$ curl -N -X POST $URL/chat -H "Authorization: Bearer ..." -H 'Content-Type: application/json' -d '{"session":"cloudrun","message":"Combien coûte la carte 10 entrées ?"}'
La carte 10 entrées coûte 120 €. Elle est valable 6 mois et donne accès uniquement au plateau.Les deux événements correspondants, lus dans Cloud Logging. Le premier essai a reçu un 429 (quota Vertex AI partagé, épuisé à ce moment-là) : après deux réessais, l'utilisateur a reçu le message d'excuse ; le second a réussi en moins d'une seconde.
{"jsonPayload": {"erreur": null, "latence_ms": 923, "modele": "gemini-2.5-flash", "question": "Combien coûte la carte 10 entrées ?"}, "timestamp": "2026-10-15T14:35:39.740410Z"}
{"jsonPayload": {"erreur": "429 RESOURCE_EXHAUSTED", "latence_ms": 6985, "modele": "gemini-2.5-flash", "question": "Combien coûte la carte 10 entrées ?"}, "timestamp": "2026-10-15T14:34:43.641351Z"}Puis service, image et sources supprimés ; les deux commandes de vérification n'affichent plus rien.
Je sais faire
- Expliquer conteneur, instance et compte de service
- Déployer avec
gcloud run deploy --sourcesans exposer de clé - Retrouver les échanges dans Cloud Logging
- Supprimer service, image et sources, et vérifier la suppression