Adrienne Vermorel

Déployer dbt Core sur une Google Cloud Function

Dans un article précédent, j’ai parlé des différences entre dbt Core et dbt Cloud, et comment dbt Core ne propose pas de moyen pour planifier et orchestrer les transformations de données. Donc, si dbt Cloud n’est pas votre choix pour orchestrer les données, vous devrez trouver un autre outil pour le faire.

La méthode que je préfère est d’utiliser les Google Cloud Functions car j’utilise généralement BigQuery pour mon data warehouse, qui se trouve également sur Google Cloud Platform (GCP).

D’autres options pourraient être de déployer via une machine virtuelle (mais cela serait probablement excessif pour un processus qui prend souvent seulement quelques minutes) ou via un container (mais cela nécessite des connaissances plus avancées en code et en gestion d’infrastructure).

Ci-dessous vous trouverez un guide pour déployer dbt Core sur une Google Cloud Function.

Pré-requis

  • Vous devez avoir un repository pour projet dbt Core
  • Vous devez avoir accès à un projet GCP : je configure généralement l’orchestration dans le projet qui héberge déjà mes transformations de données

Modifier le repo dbt Core

Premièrement, restructurez le repository en mettant tous les dossiers et fichiers dbt actuels dans un sous-répertoire. Je l’ai appelé “dbt_transform” mais vous pouvez le nommer comme vous voulez.

|-- dbt_transform
|-- analyses
|-- models
|-- seeds
|-- snapshots
|-- tests
|-- dbt_project.yml
|-- packages.yml
|-- profiles.yml

Notez que j’ai un fichier profiles.yml dans mon dossier dbt_transform. Dans ce fichier profiles.yml, utilisez la méthode oauth :

dbt_project_name:
outputs:
dev:
dataset: dbt
job_execution_timeout_seconds: 3500
job_retries: 1
location: EU
method: oauth
priority: interactive
project: gcp_project_name
threads: 4
type: bigquery
target: dev

Ajouter les fichiers main.py et requirements.txt

À la racine du répertoire, créez un fichier main.py :

import os
import subprocess
import logging
# Configure logging
logging.basicConfig(level=logging.INFO)
def run_dbt(request):
try:
# Set your dbt profiles directory (assuming it's in /workspace)
os.environ['DBT_PROFILES_DIR'] = '/workspace/dbt_transform'
# Log the current working directory and list files
dbt_project_dir = '/workspace/dbt_transform'
os.chdir(dbt_project_dir)
# Log the current working directory and list files
logging.info(f"Current working directory: {os.getcwd()}")
logging.info(f"Files in the current directory: {os.listdir('.')}")
# Install dbt packages
logging.info("Installing dbt packages...")
subprocess.run(['dbt', 'deps'], check=True,
capture_output=True, text=True)
# Run dbt command (e.g., dbt run)
result = subprocess.run(
['dbt', 'build'],
capture_output=True,
text=True
)
# Return dbt output
return result.stdout
except subprocess.CalledProcessError as e:
# If a command fails, log its output and error
logging.error(
f"Command '{e.cmd}' returned non-zero exit status {e.returncode}.")
logging.error(f"stdout: {e.stdout}")
logging.error(f"stderr: {e.stderr}")
return f"Error running dbt: {e.stderr}"
except Exception as e:
logging.error(f"Error running dbt: {str(e)}")
return f"Error running dbt: {str(e)}"

Ensuite, ajoutez le requirements.txt pour gérer les dépendances :

dbt-core
dbt-bigquery

Maintenant votre repository ressemble à ceci :

|-- dbt_transform
|-- analyses
|-- models
|-- seeds
|-- snapshots
|-- tests
|-- dbt_project.yml
|-- packages.yml
|-- profiles.yml
|-- main.py
|-- requirements.txt

Configurer le compte de service pour dbt

Vous devrez configurer un compte de service pour dbt avec les rôles suivants :

  • BigQuery Data Viewer et BigQuery Job User sur les projets/datasets qui hébergent les sources de votre projet dbt
  • BigQuery Data Editor et BigQuery User sur le projet où vos transformations de données ont lieu
  • Cloud Function Invoker sur le projet où votre Cloud Function va tourner

Pour créer ce compte de service, exécutez le script Cloud Shell ci-dessous.

Configurez les variables suivantes : TRANSFORM_PROJECT_ID, FUNCTION_PROJECT_ID et SOURCE_PROJECT_IDS.

Terminal window
# Variables à configurer
TRANSFORM_PROJECT_ID="transform-project-id"
FUNCTION_PROJECT_ID="function-project-id"
SOURCE_PROJECT_IDS=("source-project-id-1" "source-project-id-2" "source-project-id-N")
SERVICE_ACCOUNT_NAME="dbt-transform-sa"
SERVICE_ACCOUNT_DISPLAY_NAME="dbt Transformation Service Account"
# Créer le compte de service dans le projet de transformations de données
gcloud iam service-accounts create "$SERVICE_ACCOUNT_NAME" \
--description="Service account for dbt data transformations" \
--display-name="$SERVICE_ACCOUNT_DISPLAY_NAME" \
--project="$TRANSFORM_PROJECT_ID"
# Formater l'email du compte de service
SERVICE_ACCOUNT_EMAIL="${SERVICE_ACCOUNT_NAME}@${TRANSFORM_PROJECT_ID}.iam.gserviceaccount.com"
echo "Service account $SERVICE_ACCOUNT_EMAIL created."
# Boucler sur les IDs de projets sources pour assigner les rôles
for PROJECT_ID in "${SOURCE_PROJECT_IDS[@]}"
do
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" \
--role="roles/bigquery.dataViewer"
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" \
--role="roles/bigquery.jobUser"
echo "Roles assigned in the source project $PROJECT_ID."
done
# Assigner les rôles dans le projet de transformation
gcloud projects add-iam-policy-binding "$TRANSFORM_PROJECT_ID" \
--member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" \
--role="roles/bigquery.dataEditor"
gcloud projects add-iam-policy-binding "$TRANSFORM_PROJECT_ID" \
--member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" \
--role="roles/bigquery.user"
echo "Roles assigned in the transformation project."
# Assigner le rôle Cloud Function Invoker dans le projet où la Cloud Function va tourner
gcloud projects add-iam-policy-binding "$FUNCTION_PROJECT_ID" \
--member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" \
--role="roles/cloudfunctions.invoker"
echo "Cloud Function Invoker role assigned."
echo "Setup complete."

Déployer la Cloud Function

Pour déployer la Cloud Function, vous devrez avoir le CLI gcloud installé.

Une fois installé, assurez-vous d’être connecté avec le bon compte utilisateur Google en exécutant le script suivant dans votre terminal local.

Terminal window
gcloud auth login

Maintenant, assurez-vous d’être à la racine de votre projet et déployez la Cloud Function en exécutant le script ci-dessous.

Mettez à jour l’adresse de votre compte de service et la région que vous voulez utiliser.

Voici une liste des régions disponibles ; choisissez une région où les fonctions 2ème génération sont disponibles.

Terminal window
gcloud functions deploy dbt_run \
--region=europe-west1 \
--service-account=dbt-transform-sa@projectid.iam.gserviceaccount.com \
--gen2 \
--runtime=python310 \
--entry-point=run_dbt \
--trigger-http \
--timeout=3500 \
--memory=1G

Maintenant que la Cloud Function est déployée, vous pouvez planifier son exécution.

Configurer Cloud Scheduler

Configurez le Cloud Scheduler en exécutant le script ci-dessous dans Google Cloud Shell.

Configurez FUNCTION_URL, SCHEDULER_JOB_NAME, PROJECT_ID, LOCATION et SERVICE_ACCOUNT_EMAIL, TIME_ZONE et CRON_SCHEDULE avec les bonnes valeurs.

Vous pouvez trouver une liste des fuseaux horaires au format tz et un outil pour créer des schedules CRON en ligne.

Terminal window
# Variables
FUNCTION_URL="YOUR_CLOUD_FUNCTION_TRIGGER_URL"
SCHEDULER_JOB_NAME="daily-dbt-run-job"
PROJECT_ID="YOUR_PROJECT_ID"
LOCATION="YOUR_LOCATION_ID"
SERVICE_ACCOUNT_EMAIL="YOUR_SERVICE_ACCOUNT_EMAIL"
TIME_ZONE="YOUR_TIME_ZONE"
CRON_SCHEDULE="0 7 * * *" # Changez ceci selon le planning désiré (quotidien à 7h par défaut)
# Étape 1: S'assurer que le compte de service a le rôle "Service Account Token Creator"
gcloud iam service-accounts add-iam-policy-binding $SERVICE_ACCOUNT_EMAIL \
--member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" \
--role="roles/iam.serviceAccountTokenCreator" \
--project=$PROJECT_ID
echo "Added Service Account Token Creator role to $SERVICE_ACCOUNT_EMAIL."
# Étape 2: Créer le job Cloud Scheduler
gcloud scheduler jobs create http $SCHEDULER_JOB_NAME \
--location=$LOCATION \
--schedule="$CRON_SCHEDULE" \
--http-method=GET \
--uri=$FUNCTION_URL \
--oidc-service-account-email=$SERVICE_ACCOUNT_EMAIL \
--oidc-token-audience=$FUNCTION_URL \
--time-zone="$TIME_ZONE" \
--project=$PROJECT_ID
echo "Cloud Scheduler job created: $SCHEDULER_JOB_NAME"

Félicitations, vous avez terminé d’orchestrer dbt Core avec une Cloud Function.