Cloud Functions dépasse le timeout au bout de 9 minutes. Cloud Composer coûte 300 à 400 $ par mois rien que pour exister. Pour la plupart des déploiements dbt Core sur GCP, aucune de ces options n’a de sens.
Cloud Run Jobs comble ce vide. Avec des limites d’exécution allant jusqu’à 168 heures, la flexibilité des conteneurs et une facturation à l’exécution, il gère les workloads dbt pour une fraction du coût de Composer. Un déploiement classique revient à moins de 5 $ par mois.
Ce guide couvre l’ensemble de la mise en place : Dockerfile, authentification avec Workload Identity, planification, déclenchement événementiel et monitoring.
Pourquoi Cloud Run Jobs pour dbt
La limite d’exécution à elle seule rend Cloud Run Jobs viable là où Cloud Functions ne l’est pas. Cloud Functions plafonne à 9 minutes (540 secondes) pour les fonctions déclenchées par HTTP. Cloud Run Jobs autorise jusqu’à 168 heures (7 jours complets), ce qui couvre n’importe quel run dbt raisonnable.
Le support des conteneurs vous donne un contrôle total sur l’environnement. Vous pouvez figer les versions exactes de dbt, des adaptateurs et des dépendances. Inclure des packages personnalisés. Exécuter des scripts avant ou après dbt dans le même conteneur.
Le modèle tarifaire récompense l’efficacité. Vous ne payez que le temps d’exécution, pas l’infrastructure inactive. Un run dbt quotidien de 10 minutes coûte quelques centimes. Comparez avec le minimum de 300 à 400 $ mensuels de Composer pour un environnement inactif plus de 23 heures par jour.
Quand Composer justifie-t-il son coût ? Lorsque vous avez besoin d’une orchestration complexe impliquant plusieurs systèmes : extraction, transformation, reverse ETL, avec une logique de retry sophistiquée et des capacités de backfill. Pour une comparaison détaillée, consultez Cloud Run Jobs vs. Composer pour dbt. Si votre projet dbt s’exécute de manière autonome selon un planning, Cloud Run Jobs est le meilleur choix.
Stratégie de dépôt et de conteneur
Séparez votre projet dbt de la définition de votre image Docker. Cette approche à deux dépôts permet des cycles de développement indépendants. Les analystes mettent à jour les modèles SQL sans toucher à l’infrastructure. Les ingénieurs plateforme mettent à jour le conteneur sans modifier la logique de transformation.
La structure ressemble à ceci :
dbt-project-repo/├── models/├── macros/├── tests/├── dbt_project.yml└── profiles.yml
dbt-runner-repo/├── Dockerfile├── cloudbuild.yaml└── scripts/ └── run-dbt.shLe dépôt runner construit une image qui clone le projet dbt au moment de l’exécution, ou vous pouvez intégrer les modèles dans l’image au moment du build. Le clonage à l’exécution apporte de la flexibilité ; l’intégration dans l’image apporte de la reproductibilité. Pour la plupart des équipes, intégrer les modèles dans l’image lors du CI/CD offre un meilleur contrôle de version.
Le Dockerfile
Les builds multi-stage gardent les images légères tout en assurant la reproductibilité :
# Build stageFROM python:3.11-slim as builder
WORKDIR /app
# Install build dependenciesRUN apt-get update && apt-get install -y --no-install-recommends \ git \ && rm -rf /var/lib/apt/lists/*
# Install dbt with pinned versionsRUN pip install --no-cache-dir \ dbt-core==1.9.0 \ dbt-bigquery==1.9.0
# Copy dbt projectCOPY dbt_project/ /app/dbt_project/COPY profiles.yml /app/profiles.yml
# Runtime stageFROM python:3.11-slim
WORKDIR /app
# Copy installed packages from builderCOPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packagesCOPY --from=builder /usr/local/bin/dbt /usr/local/bin/dbt
# Copy dbt projectCOPY --from=builder /app/dbt_project /app/dbt_projectCOPY --from=builder /app/profiles.yml /app/profiles.yml
# Set working directory to dbt projectWORKDIR /app/dbt_project
# Default commandCMD ["dbt", "build", "--profiles-dir", "/app"]Figez les versions exactes. Utiliser dbt-core==1.9.0 garantit des builds reproductibles. Les tags latest créent des cauchemars de débogage quand le comportement change d’un run à l’autre.
Construisez et poussez vers Artifact Registry :
# Create repository if it doesn't existgcloud artifacts repositories create dbt-images \ --repository-format=docker \ --location=us-central1 \ --description="dbt Docker images"
# Build and pushgcloud builds submit \ --tag us-central1-docker.pkg.dev/PROJECT_ID/dbt-images/dbt-runner:v1.0.0Configurer profiles.yml pour Cloud Run
Le fichier profiles.yml indique à dbt comment se connecter à BigQuery. Sur Cloud Run, utilisez OAuth avec Workload Identity (pas de clés de service account à gérer) :
dbt_project: target: prod outputs: prod: type: bigquery method: oauth project: "{{ env_var('GCP_PROJECT') }}" dataset: "{{ env_var('DBT_DATASET', 'analytics') }}" location: "{{ env_var('BQ_LOCATION', 'US') }}" threads: 4 timeout_seconds: 300 priority: interactive retries: 1Le paramètre method: oauth indique à dbt-bigquery d’utiliser les credentials par défaut fournis par l’environnement d’exécution. Sur Cloud Run, il s’agit des credentials du service account attaché, obtenus automatiquement via Workload Identity.
La substitution de variables d’environnement avec env_var() rend le profiles.yml agnostique de l’environnement. La même image fonctionne en dev, staging et prod en changeant les variables d’environnement au moment du déploiement.
Configurer IAM et Workload Identity
Créez un service account dédié pour votre workload dbt :
# Set variablesexport PROJECT_ID=your-project-idexport SA_NAME=dbt-runnerexport SA_EMAIL=$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com
# Create service accountgcloud iam service-accounts create $SA_NAME \ --display-name="dbt Cloud Run Runner" \ --description="Service account for dbt Cloud Run Jobs"Accordez les permissions minimales requises. Pour les transformations BigQuery :
# BigQuery permissions for running dbtgcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:$SA_EMAIL" \ --role="roles/bigquery.dataEditor"
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:$SA_EMAIL" \ --role="roles/bigquery.jobUser"
# If dbt needs to create datasetsgcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:$SA_EMAIL" \ --role="roles/bigquery.dataOwner"Workload Identity fonctionne automatiquement sur Cloud Run. Le service account que vous attachez au job fournit les credentials OAuth sans aucun fichier de clé. Cela élimine le risque de sécurité lié aux credentials à longue durée de vie et la charge opérationnelle de la rotation des clés.
Déployer le Cloud Run Job
Créez le Cloud Run Job avec votre conteneur :
gcloud run jobs create dbt-daily \ --image=us-central1-docker.pkg.dev/$PROJECT_ID/dbt-images/dbt-runner:v1.0.0 \ --region=us-central1 \ --service-account=$SA_EMAIL \ --memory=2Gi \ --cpu=2 \ --max-retries=2 \ --task-timeout=3600 \ --set-env-vars="GCP_PROJECT=$PROJECT_ID,DBT_DATASET=analytics,BQ_LOCATION=US"Points clés de la configuration :
Mémoire et CPU : Commencez avec 2 Go de mémoire et 2 CPUs. La consommation mémoire de dbt varie selon la complexité des modèles et le parallélisme. Si vous rencontrez des erreurs out-of-memory, augmentez la mémoire. Si les runs sont trop lents, augmentez les CPUs et le nombre de threads dans profiles.yml en même temps.
Timeout de tâche : Fixez-le au-dessus de la durée maximale attendue de vos runs, avec une marge de sécurité. 3600 secondes (1 heure) convient à la plupart des projets. Le maximum est de 168 heures.
Nombre de retries : Avec --max-retries=2, Cloud Run relancera les exécutions échouées deux fois. Le code de retour de dbt (non-zéro en cas d’échec) déclenche ce mécanisme automatiquement.
Testez le job manuellement :
gcloud run jobs execute dbt-daily --region=us-central1 --waitLe flag --wait bloque jusqu’à la fin de l’exécution et affiche les logs dans votre terminal.
Planification avec Cloud Scheduler
Cloud Scheduler déclenche votre job dbt selon un planning cron. Commencez par créer un service account pour le scheduler avec la permission d’invoquer le job :
# Create scheduler service accountgcloud iam service-accounts create dbt-scheduler \ --display-name="dbt Scheduler Invoker"
# Grant permission to invoke the Cloud Run Jobgcloud run jobs add-iam-policy-binding dbt-daily \ --region=us-central1 \ --member="serviceAccount:dbt-scheduler@$PROJECT_ID.iam.gserviceaccount.com" \ --role="roles/run.invoker"Créez le job planifié :
gcloud scheduler jobs create http dbt-daily-schedule \ --location=us-central1 \ --schedule="0 6 * * *" \ --uri="https://us-central1-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/$PROJECT_ID/jobs/dbt-daily:run" \ --http-method=POST \ --oauth-service-account-email=dbt-scheduler@$PROJECT_ID.iam.gserviceaccount.comPatterns cron courants :
0 6 * * *(tous les jours à 6h)0 */4 * * *(toutes les 4 heures)0 6 * * 1-5(jours ouvrés à 6h)*/30 * * * *(toutes les 30 minutes)
Les horaires correspondent au fuseau horaire de votre emplacement scheduler. Spécifiez-le explicitement avec --time-zone="America/New_York" si nécessaire.
Déclenchement événementiel avec Eventarc
Pour les pipelines où dbt doit s’exécuter à l’arrivée de données en amont, Eventarc fournit un déclenchement piloté par les événements.
Déclencher dbt quand un fichier arrive dans Cloud Storage :
gcloud eventarc triggers create dbt-on-upload \ --location=us-central1 \ --destination-run-job=dbt-daily \ --destination-run-region=us-central1 \ --event-filters="type=google.cloud.storage.object.v1.finalized" \ --event-filters="bucket=your-data-bucket" \ --service-account=$SA_EMAILDéclencher dbt quand un job de chargement BigQuery se termine :
gcloud eventarc triggers create dbt-on-bq-load \ --location=us-central1 \ --destination-run-job=dbt-daily \ --destination-run-region=us-central1 \ --event-filters="type=google.cloud.audit.log.v1.written" \ --event-filters="serviceName=bigquery.googleapis.com" \ --event-filters="methodName=google.cloud.bigquery.v2.JobService.InsertJob" \ --service-account=$SA_EMAILLes patterns événementiels fonctionnent bien pour une fraîcheur de données quasi temps réel. Combinez-les avec des runs planifiés en fallback pour garantir l’exécution des modèles même si des événements sont manqués.
Monitoring et alerting
Cloud Run Jobs envoie automatiquement ce qui s’affiche dans le conteneur vers Cloud Logging. Configurez dbt pour produire des logs utiles :
# In your CMD or run scriptdbt build --profiles-dir /app 2>&1 | tee /dev/stderrCréez une alerte basée sur les logs en cas d’échec :
gcloud logging metrics create dbt-failures \ --description="dbt Cloud Run Job failures" \ --log-filter='resource.type="cloud_run_job" AND resource.labels.job_name="dbt-daily" AND severity>=ERROR'
gcloud alpha monitoring policies create \ --display-name="dbt Job Failed" \ --condition-display-name="Error rate > 0" \ --condition-filter='metric.type="logging.googleapis.com/user/dbt-failures"' \ --condition-threshold-value=0 \ --condition-threshold-comparison=COMPARISON_GT \ --notification-channels=YOUR_CHANNEL_IDMétriques clés à suivre dans Cloud Monitoring :
- Nombre d’exécutions :
run.googleapis.com/job/completed_execution_count - Durée d’exécution :
run.googleapis.com/job/completed_execution_duration - Utilisation mémoire :
run.googleapis.com/job/memory/utilization
Configurez des alertes sur les anomalies de durée. Un run qui prend habituellement 10 minutes et passe soudainement à 45 minutes signale souvent un problème avant qu’il ne devienne un échec.
Script de déploiement complet
Voici l’ensemble en un seul script :
#!/bin/bashset -e
export PROJECT_ID=your-project-idexport REGION=us-central1export SA_NAME=dbt-runnerexport SA_EMAIL=$SA_NAME@$PROJECT_ID.iam.gserviceaccount.comexport IMAGE=us-central1-docker.pkg.dev/$PROJECT_ID/dbt-images/dbt-runner:v1.0.0
# Create service accountgcloud iam service-accounts create $SA_NAME \ --display-name="dbt Cloud Run Runner" \ --project=$PROJECT_ID || true
# Grant BigQuery permissionsgcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:$SA_EMAIL" \ --role="roles/bigquery.dataEditor"
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:$SA_EMAIL" \ --role="roles/bigquery.jobUser"
# Create Artifact Registry repositorygcloud artifacts repositories create dbt-images \ --repository-format=docker \ --location=$REGION \ --project=$PROJECT_ID || true
# Build and push imagegcloud builds submit \ --tag $IMAGE \ --project=$PROJECT_ID
# Create Cloud Run Jobgcloud run jobs create dbt-daily \ --image=$IMAGE \ --region=$REGION \ --service-account=$SA_EMAIL \ --memory=2Gi \ --cpu=2 \ --max-retries=2 \ --task-timeout=3600 \ --set-env-vars="GCP_PROJECT=$PROJECT_ID,DBT_DATASET=analytics" \ --project=$PROJECT_ID
# Create scheduler service accountgcloud iam service-accounts create dbt-scheduler \ --display-name="dbt Scheduler Invoker" \ --project=$PROJECT_ID || true
gcloud run jobs add-iam-policy-binding dbt-daily \ --region=$REGION \ --member="serviceAccount:dbt-scheduler@$PROJECT_ID.iam.gserviceaccount.com" \ --role="roles/run.invoker" \ --project=$PROJECT_ID
# Create schedulegcloud scheduler jobs create http dbt-daily-schedule \ --location=$REGION \ --schedule="0 6 * * *" \ --uri="https://$REGION-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/$PROJECT_ID/jobs/dbt-daily:run" \ --http-method=POST \ --oauth-service-account-email=dbt-scheduler@$PROJECT_ID.iam.gserviceaccount.com \ --project=$PROJECT_ID
echo "Deployment complete. Test with:"echo "gcloud run jobs execute dbt-daily --region=$REGION --wait"Analyse des coûts
Pour un job dbt quotidien de 15 minutes avec 2 vCPUs et 2 Go de mémoire :
- Exécution Cloud Run : ~0,50 $/mois
- Cloud Scheduler : ~0,10 $/mois (3 jobs gratuits, 0,10 $ par job supplémentaire)
- Stockage Artifact Registry : ~0,50 $/mois
- Cloud Build : le free tier couvre la plupart des usages
- Total : moins de 5 $/mois
À comparer avec le minimum de 300 à 400 $/mois de Cloud Composer 3 pour un environnement inactif. Cloud Run Jobs coûte 1 à 2 % du prix de Composer pour une orchestration dbt simple.
Cloud Run Jobs couvre la plupart des besoins de déploiement dbt Core sur GCP. La flexibilité des conteneurs, les limites d’exécution de 7 jours et la facturation à l’exécution éliminent le compromis entre simplicité et capacité qui caractérisait les options précédentes.
À quel moment dépasse-t-on les limites de ce setup ? Quand la complexité de l’orchestration nécessite réellement Airflow : des pipelines multi-systèmes avec des dépendances sophistiquées, des besoins de backfill ou des exigences de conformité pour un audit logging au niveau des tâches. La plupart des équipes atteignent ce seuil plus tard que prévu, si tant est qu’elles l’atteignent.
Pour une vision plus large de la place de Cloud Run Jobs dans l’écosystème data GCP, consultez Architecture Data Platform GCP : patterns stratégiques pour 2026.