ServicesÀ proposNotesContact Me contacter →
EN FR
Note

Conteneurisation Docker pour dbt

Patterns pour conteneuriser dbt Core en production — Dockerfiles multi-étapes, épinglage de versions, Artifact Registry, et la stratégie deux-dépôts qui sépare la logique de transformation de l'infrastructure.

Planté
dbtgcpdata engineeringautomation

Conteneuriser dbt Core pour la production signifie construire une image Docker qui encapsule votre runtime dbt, vos dépendances et le code de votre projet dans un artefact reproductible et portable. Que vous déployiez vers des Cloud Run Jobs, Kubernetes ou tout runtime de conteneurs, les patterns de conteneurisation sont identiques.

L’approche conteneur découple l’exécution dbt de toute plateforme d’orchestration spécifique. Votre projet dbt s’exécute de manière identique dans Cloud Run Jobs aujourd’hui et dans Kubernetes demain. L’orchestrateur devient interchangeable — il décide simplement quand le conteneur s’exécute, pas comment.

Dockerfile multi-étapes

Un build multi-étapes garde les images petites tout en assurant la reproductibilité. La première étape installe les dépendances (y compris les outils de build comme git) ; la seconde ne copie que les artefacts de runtime :

# Étape de build
FROM python:3.11-slim as builder
WORKDIR /app
# Installer les dépendances de build
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
&& rm -rf /var/lib/apt/lists/*
# Installer dbt avec des versions épinglées
RUN pip install --no-cache-dir \
dbt-core==1.9.0 \
dbt-bigquery==1.9.0
# Copier le projet dbt
COPY dbt_project/ /app/dbt_project/
COPY profiles.yml /app/profiles.yml
# Étape de runtime
FROM python:3.11-slim
WORKDIR /app
# Copier les packages installés depuis le 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
# Copier le projet dbt
COPY --from=builder /app/dbt_project /app/dbt_project
COPY --from=builder /app/profiles.yml /app/profiles.yml
# Définir le répertoire de travail au projet dbt
WORKDIR /app/dbt_project
# Commande par défaut
CMD ["dbt", "build", "--profiles-dir", "/app"]

L’étape de build a besoin de git pour les packages qui s’installent depuis des dépôts Git. L’étape de runtime n’en a pas besoin. Les builds multi-étapes vous permettent d’inclure des outils de build sans alourdir l’image finale. Le résultat est une image plus petite, plus rapide à télécharger et avec une surface d’attaque réduite.

Épinglage des versions

Épinglez des versions exactes de dbt-core et des adaptateurs. Toujours.

dbt-core==1.9.0
dbt-bigquery==1.9.0

Utiliser latest ou des versions non épinglées crée des cauchemars de débogage quand le comportement change entre les exécutions. Un modèle qui a réussi lundi échoue mercredi parce qu’une mise à jour mineure a changé la résolution d’une macro. Le message d’erreur pointe vers votre SQL, pas vers le changement de version. Vous passez des heures à déboguer la logique de transformation alors que le problème est une dérive d’infrastructure.

Épinglez aussi l’image de base Python. python:3.11-slim est mieux que python:3-slim car ce dernier se met à jour silencieusement quand Python 3.12 ou 3.13 devient le tag par défaut. Pour une reproductibilité maximale, épinglez le digest : python:3.11-slim@sha256:abc123.... La plupart des équipes trouvent que l’épinglage à la version mineure (3.11) constitue le bon équilibre entre reproductibilité et charge de maintenance.

La stratégie deux-dépôts

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 :

dbt-project-repo/
├── models/
├── macros/
├── tests/
├── dbt_project.yml
└── profiles.yml
dbt-runner-repo/
├── Dockerfile
├── cloudbuild.yaml
└── scripts/
└── run-dbt.sh

Les analystes de données mettent à jour les modèles SQL sans toucher à l’infrastructure. Les ingénieurs de plateforme mettent à jour le conteneur — versions Python, versions dbt, configuration du build — sans modifier la logique de transformation. La surface de conflit entre ces deux groupes tombe à zéro.

Deux approches pour intégrer le projet dbt dans le conteneur :

Intégrer les modèles dans l’image pendant le CI/CD. Le pipeline CI clone le dépôt du projet dbt et copie les modèles dans l’image au moment du build. Cela fournit le contrôle de version — chaque image est un snapshot d’un commit spécifique. Vous pouvez revenir à une image précédente si un déploiement introduit des régressions. La plupart des équipes devraient commencer ici.

Cloner le projet dbt au runtime. Le conteneur clone le dépôt quand il démarre. Cela ajoute de la flexibilité — vous pouvez pointer vers différentes branches ou tags via des variables d’environnement — mais sacrifie la reproductibilité. La même image pourrait produire des résultats différents selon ce qui se trouve dans le dépôt lors de l’exécution. Réservez cette approche aux environnements de développement ou de staging où une itération rapide est nécessaire.

Construire et pousser vers Artifact Registry

Artifact Registry de GCP stocke vos images de conteneurs. Créez un dépôt, puis utilisez Cloud Build pour construire et pousser :

Terminal window
# Créer le dépôt s'il n'existe pas
gcloud artifacts repositories create dbt-images \
--repository-format=docker \
--location=us-central1 \
--description="Images Docker dbt"
# Construire et pousser
gcloud builds submit \
--tag us-central1-docker.pkg.dev/PROJECT_ID/dbt-images/dbt-runner:v1.0.0

Taguez les images avec des versions sémantiques (v1.0.0, v1.1.0) plutôt que latest. Quand un incident se produit en production, vous devez savoir exactement quelle version de l’image s’exécute. Le tag latest est mutable — il pointe vers ce qui a été poussé le plus récemment — donc il ne vous dit rien d’utile lors de la réponse à un incident.

Pour les builds automatisés, un cloudbuild.yaml dans le dépôt runner déclenche des builds sur push :

steps:
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', '${_IMAGE_TAG}', '.']
- name: 'gcr.io/cloud-builders/docker'
args: ['push', '${_IMAGE_TAG}']
substitutions:
_IMAGE_TAG: 'us-central1-docker.pkg.dev/${PROJECT_ID}/dbt-images/dbt-runner:${SHORT_SHA}'

L’utilisation de ${SHORT_SHA} comme tag lie chaque image à son commit source. Combiné avec l’approche d’intégration, cela offre une traçabilité complète : d’un conteneur en exécution au code exact qui l’a construit.

profiles.yml pour dbt conteneurisé

Le profiles.yml à l’intérieur d’un conteneur doit être agnostique à l’environnement. Utilisez env_var() pour tout ce qui varie entre les environnements :

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

Le paramètre method: oauth indique à dbt-bigquery d’utiliser les identifiants que l’environnement d’exécution fournit. Dans Cloud Run Jobs, c’est le compte de service attaché via Workload Identity. Dans un conteneur Docker local, ce sont vos identifiants ADC montés dans le conteneur. La même image fonctionne partout.

Les valeurs par défaut dans env_var('DBT_DATASET', 'analytics') empêchent les échecs quand une variable d’environnement n’est pas définie, tout en permettant la surcharge au moment du déploiement. Cela garde l’image portable entre développement, staging et production sans reconstruire.

Quand les conteneurs ne valent pas l’effort

Tous les déploiements dbt ne nécessitent pas la conteneurisation. Si vous exécutez dbt en local ou via dbt Cloud, la surcharge des conteneurs ajoute de la complexité sans bénéfice. Les conteneurs justifient leur place quand :

  • Vous avez besoin de runs de production reproductibles avec des dépendances épinglées
  • Plusieurs environnements (dev, staging, prod) doivent s’exécuter depuis le même artefact
  • Votre plateforme d’orchestration (Cloud Run, Kubernetes, Dagster) attend des conteneurs
  • Vous souhaitez découpler la gestion des versions dbt des machines des développeurs

Pour un développeur solo exécutant dbt build depuis la ligne de commande contre un dataset de développement, un environnement virtuel avec pip install dbt-core dbt-bigquery est plus simple et suffisant.