Déployer dbt Core sur Cloud Run Jobs : guide complet

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.sh

Le 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 stage
FROM python:3.11-slim as builder
WORKDIR /app
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
&& rm -rf /var/lib/apt/lists/*
# Install dbt with pinned versions
RUN pip install --no-cache-dir \
dbt-core==1.9.0 \
dbt-bigquery==1.9.0
# Copy dbt project
COPY dbt_project/ /app/dbt_project/
COPY profiles.yml /app/profiles.yml
# Runtime stage
FROM python:3.11-slim
WORKDIR /app
# Copy installed packages from builder
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /usr/local/bin/dbt /usr/local/bin/dbt
# Copy dbt project
COPY --from=builder /app/dbt_project /app/dbt_project
COPY --from=builder /app/profiles.yml /app/profiles.yml
# Set working directory to dbt project
WORKDIR /app/dbt_project
# Default command
CMD ["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 :

Terminal window
# Create repository if it doesn't exist
gcloud artifacts repositories create dbt-images \
--repository-format=docker \
--location=us-central1 \
--description="dbt Docker images"
# Build and push
gcloud builds submit \
--tag us-central1-docker.pkg.dev/PROJECT_ID/dbt-images/dbt-runner:v1.0.0

Configurer 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: 1

Le 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 :

Terminal window
# Set variables
export PROJECT_ID=your-project-id
export SA_NAME=dbt-runner
export SA_EMAIL=$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com
# Create service account
gcloud 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 :

Terminal window
# BigQuery permissions for running dbt
gcloud 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 datasets
gcloud 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 :

Terminal window
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 :

Terminal window
gcloud run jobs execute dbt-daily --region=us-central1 --wait

Le 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 :

Terminal window
# Create scheduler service account
gcloud iam service-accounts create dbt-scheduler \
--display-name="dbt Scheduler Invoker"
# Grant permission to invoke the Cloud Run Job
gcloud 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é :

Terminal window
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.com

Patterns 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 :

Terminal window
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_EMAIL

Déclencher dbt quand un job de chargement BigQuery se termine :

Terminal window
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_EMAIL

Les 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 :

Terminal window
# In your CMD or run script
dbt build --profiles-dir /app 2>&1 | tee /dev/stderr

Créez une alerte basée sur les logs en cas d’échec :

Terminal window
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_ID

Mé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/bash
set -e
export PROJECT_ID=your-project-id
export REGION=us-central1
export SA_NAME=dbt-runner
export SA_EMAIL=$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com
export IMAGE=us-central1-docker.pkg.dev/$PROJECT_ID/dbt-images/dbt-runner:v1.0.0
# Create service account
gcloud iam service-accounts create $SA_NAME \
--display-name="dbt Cloud Run Runner" \
--project=$PROJECT_ID || true
# Grant BigQuery permissions
gcloud 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 repository
gcloud artifacts repositories create dbt-images \
--repository-format=docker \
--location=$REGION \
--project=$PROJECT_ID || true
# Build and push image
gcloud builds submit \
--tag $IMAGE \
--project=$PROJECT_ID
# Create Cloud Run Job
gcloud 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 account
gcloud 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 schedule
gcloud 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.