Adrienne Vermorel

Lire aussi

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 donc un guide pour déployer dbt Core sur une Google Cloud Function.

Pré-requis

  • Disposer d’un repository pour projet dbt Core.
  • Avoir accès à un projet GCP : j’organise généralement l’orchestration dans le projet qui héberge déjà mes transformations de données via dbt

Modifier le repo dbt Core

Tout d’abord, on doit restructurer le dépôt en plaçant tous les dossiers et fichiers dbt actuels dans un sous-répertoire. Je l’ai nommé “dbt_transform”, mais on peut le nommer comme on le souhaite.

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

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

profiles.yml
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 repository, on crée un fichier main.py :

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)}"

Après, on ajoute le fichier requirements.txt pour gérer les dépendances :

requirements.txt
dbt-core
dbt-bigquery

Maintenant, notre repository a cette tête :

dbt project
|-- 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

On va avoir besoin de paramétrer 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 du projet dbt
  • BigQuery Data Editor et BigQuery User sur le projet où les transformations de données ont lieu
  • Cloud Function Invoker sur le projet où la fonction Cloud va s’exécuter

Pour créer ce compte de service, on peut exécuter le script Cloud Shell (raccourci G + S) ci-dessous.

On va avoir besoin de paramétrer les variables suivantes : TRANSFORM_PROJECT_ID, FUNCTION_PROJECT_ID et SOURCE_PROJECT_IDS.

Google Cloud Shell - Service Account Creation
# Variables to set up
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"
# Create service account in the data transformations project
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"
# Format the service account email
SERVICE_ACCOUNT_EMAIL="${SERVICE_ACCOUNT_NAME}@${TRANSFORM_PROJECT_ID}.iam.gserviceaccount.com"
echo "Service account $SERVICE_ACCOUNT_EMAIL created."
# Loop through the source project IDs to assign roles
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
# Assign roles in the transformation project
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."
# Assign Cloud Function Invoker role in the project where Cloud Function will run
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 fonction Cloud, on a besoin d’avoir le CLI gcloud installé.

Une fois installé, on s’assure qu’on est bien connecté avec le bon compte utilisateur Google en exécutant le script suivant dans le terminal local.

Local Terminal - Login to GCP
gcloud auth login

Maintenant, on s’assure qu’on est à la racine du projet on déploie la Cloud Function en exécutant le script ci-dessous.

On doit configurer les paramètres pour l’adresse du service account et la région qu’on souhaite utiliser.

Voici une liste des régions disponibles, il faut bien choisir une région où les fonctions de 2ème génération sont disponibles.

Local Terminal - Deploy a Cloud Function
gcloud functions deploy dbt_run \
--region=europe-west1 \
--service-account=dbt-transform-[email protected] \
--gen2 \
--runtime=python310 \
--entry-point=run_dbt \
--trigger-http \
--timeout=3500 \
--memory=1G

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

Configurer un Cloud Scheduler

On peut configurer le Cloud Scheduler en exécutant le script ci-dessous dans Google Cloud Shell.

On doit configurer FUNCTION_URL, SCHEDULER_JOB_NAME, PROJECT_ID, LOCATION, SERVICE_ACCOUNT_EMAIL, TIME_ZONE et CRON_SCHEDULE avec les bonnes valeurs.

On peut trouver ici une liste des fuseaux horaires au format tz et un outil pour créer des plannings CRON.

Google Cloud Shell - Create Cloud Scheduler
# 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 * * *" # Change this to your desired schedule (daily at 7 AM by default)
# Step 1: Ensure the service account has the "Service Account Token Creator" role
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."
# Step 2: Create the Cloud Scheduler job
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 finalisé le déploiement de dbt Core via une Google Cloud Function. 💪