Aller au contenu

FDLD — Fabrique De La Donnée

Plateforme data de l'ADEME. Elle combine orchestration (Airflow), entrepôt analytique (ClickHouse), lakehouse Iceberg (Lakekeeper + S3) et catalogue / gouvernance (OpenMetadata + OpenSearch). Les traitements sont packagés par les développeurs sous forme de produits data et exécutés par Airflow dans des pods Kubernetes dédiés.

  • Repo applicatif (produits data + DAGs) : https://gitlab.ademe.fr/ademe-group/fdld/fdld
  • Déploiement : ArgoCD — charts dans argocd-ademe-appofapps/charts/*, valeurs par environnement dans argocd-apps/fdld/helm-values/*
  • Infra Scaleway : Terraform dans scw-application/horsprod/terraform/* et scw-init-config/002.config_applications

Accès et secrets

Tous les accès web sont filtrés par IP (whitelist Traefik). Les secrets vivent dans Vault ; convention de chemin : apps/fdld/<environnement> (secrets propres à un env) ou apps/fdld/common (secrets partagés entre environnements).


Vue d'ensemble

Deux flux se combinent : une chaîne GitOps/CI-CD qui déploie la plateforme et livre les images produit, et un flux de données orchestré par Airflow.

Chaîne de livraison

Dev push (repo fdld)
    ├─ DAGs + code produit ──► git-sync ──► Airflow (DAGs)
    └─ GitLab CI (.ademe.gitlab-ci.yml, dynamique par produit)
           ├─ build  (deploy-template-v3) ──► registry Scaleway (namespace-scw-fdld)
           └─ deploy (custom, API Airflow) ──► Variables Airflow (<produit>_image, ...)

Charts Helm + helm-values ──► ArgoCD (app int-fdld) ──► Kubernetes (namespace int-fdld)

Flux de données (exécution d'un produit)

Airflow scheduler ──► KubernetesExecutor (pod worker éphémère)
        └─ KubernetesPodOperator ──► pod produit (image Scaleway)
                 SQL Server ──► S3 (Parquet brut, dlt)
                 S3 Parquet ──► tables Iceberg (Lakekeeper) / ClickHouse
                 OpenMetadata crawle les composants (lignage, qualité)

Chaque tâche Airflow tourne dans un pod éphémère (KubernetesExecutor). Les DAGs produit délèguent le traitement à un second pod via KubernetesPodOperator, qui exécute l'image du produit buildée par la CI.


Déploiement & GitOps

Élément Emplacement
Application ArgoCD argocd-apps/fdld/integration.yaml (int-fdld, projet fdld-integration, cluster kapsule_cluster_horsprod)
Charts wrapper charts/apache-airflow, charts/lakekeeper, charts/openmetadata
Valeurs par env argocd-apps/fdld/helm-values/<composant>/integration.yaml
Infra ClickHouse scw-application/horsprod/terraform/042.*, 043.*
Infra OpenSearch scw-application/horsprod/terraform/044.opensearch
Rattachement applicatif scw-init-config/002.config_applications (clickhouse, opensearch)

Les charts sont des wrappers : ils vendorent le chart upstream (.tgz) et ajoutent les templates propres à l'ADEME (ExternalSecret, ServiceMonitor, imagePullSecret, IP-whitelist).

Environnements

Seul l'environnement intégration (int-fdld) est déployé pour le moment. Les environnements rec et preprod réutilisent le même pattern (bases ClickHouse, buckets S3 et chemins Vault déjà provisionnés).


Apache Airflow — orchestration

https://int-fdld-airflow.direct.horsprod.ademe-scw.fr/

Connexion

  • Username : admin
  • Password : apps/fdld/integrationairflow_admin_password

Caractéristiques

  • Version Airflow 3.2.2, exécuteur KubernetesExecutor (chaque tâche = un pod éphémère).
  • Base de métadonnées sur le RDB managé fdld (pas de PostgreSQL embarqué), URI dans apps/fdld/integrationairflow_metadata_connection.
  • Clés stables externalisées depuis Vault pour éviter un OutOfSync permanent ArgoCD : airflow_fernet_key, airflow_api_secret_key, airflow_jwt_secret.
  • Jobs migrate-database / create-user convertis en hooks de synchro ArgoCD (sinon deadlock au sync).

DAGs via git-sync

Les DAGs sont récupérés en continu depuis le repo applicatif https://gitlab.ademe.fr/ademe-group/fdld/fdld (branche develop en intégration) par un sidecar git-sync. Ils sont éparpillés sous products/*/dags/ ; un .airflowignore à la racine du repo évite qu'Airflow tente d'importer dlt/, dbt/, shared/… comme des DAGs.

Identifiants git-sync (deploy token GitLab, scope read_repository) : apps/fdld/integrationgitlab_deploy_token_user / gitlab_deploy_token.

Deploy token GitLab

Le deploy token est généré manuellement dans le repo, avec le scope read_repository uniquement. Il est disponible / renouvelable dans Settings > Repository > Deploy tokens > int-airflow. En cas de rotation, mettre à jour gitlab_deploy_token_user / gitlab_deploy_token dans Vault.

Connexions & variables — autonomie des devs

Un path Vault entier est mirroré (1:1) dans un Secret Kubernetes airflow-connections, injecté en envFrom dans tous les pods Airflow (workers inclus).

  • Path Vault : apps/fdld/fdld/integration/airflow
  • Les devs y ajoutent des clés AIRFLOW_CONN_<ID> (connexions) ou AIRFLOW_VAR_<KEY> (variables) — aucune modification des helm-values n'est requise.
  • Format d'une connexion (valeur sur une seule ligne) :
    {"conn_type":"postgres","host":"...internal","port":5432,"schema":"maBase","login":"user","password":"..."}
    

Connexions dans les pods produit

Le pod produit (KubernetesPodOperator) est un pod séparé : il ne reçoit que ce que le DAG lui passe. Pour que BaseHook.get_connection(...) y fonctionne, le DAG doit injecter le secret airflow-connections dans le pod produit :

from kubernetes.client import models as k8s
env_from=[k8s.V1EnvFromSource(
    secret_ref=k8s.V1SecretEnvSource(name="airflow-connections", optional=True))]

Logs distants (S3)

Les pods étant éphémères, les logs sont écrits sur S3 pour survivre et rester lisibles dans l'UI.

  • remote_logging = True, remote_base_log_folder = s3://int-fdld-airflow-logs/, remote_log_conn_id = s3_logs
  • La connexion s3_logs (clé AIRFLOW_CONN_S3_LOGS dans le path Vault) doit porter extra.endpoint_url (S3 non-AWS Scaleway) :
    {"conn_type":"aws","login":"<key>","password":"<secret>","extra":{"endpoint_url":"https://s3.fr-par.scw.cloud","region_name":"fr-par"}}
    

Pull des images produit

Les pods KubernetesPodOperator tirent l'image produit depuis le registry Scaleway. Un imagePullSecret dockerconfigjson (airflow-scw-registry) est généré depuis Vault et attaché au ServiceAccount default du namespace (via ArgoCD Server-Side Apply).

  • Registry : rg.fr-par.scw.cloud/namespace-scw-fdld
  • Identifiants : apps/fdld/commonregistry_access_key / registry_access_token

Produits data & CI/CD

Le repo applicatif est organisé par produit : products/<nom>/ contient les dags/, le code d'ingestion (dlt/), les transformations (dbt/)… shared/ regroupe le code mutualisé (ex. utilitaires d'audit).

Pipeline dynamique

Le fichier de CI est .ademe.gitlab-ci.yml. Il est dynamique : un premier job scanne products/*/ et génère une pipeline enfant (jobs test / build / release / deploy par produit).

Zéro job à écrire

Ajouter un dossier products/<nom>/ suffit : les jobs sont générés automatiquement. Les devs sont autonomes sur l'ajout de produits et de variables d'environnement (via le path Vault ci-dessus).

Étape Mécanisme Résultat
build / release deploy-template-v3 image poussée sur le registry Scaleway namespace-scw-fdld/<produit>
deploy custom (requête API Airflow) met à jour les Variables Airflow <produit>_image, <produit>_commit_sha, <produit>_mr_id

Le deploy ne passe pas par la deploy-template : il obtient un token JWT (POST /auth/token ou AIRFLOW_API_TOKEN) puis upserte les Variables via l'API v2 — c'est ainsi que le DAG récupère l'image à exécuter (Variable.get("<produit>_image")).

Images de base

Les images produit partent de l'image de base ADEME docker-base-images/python-for-workload/python:3.13-bookworm (glibc — requis pour psycopg2-binary / pyodbc qui n'ont pas de wheels musl ; alpine échoue), uv étant ajouté au build.

Variables CI à définir (Settings > CI/CD, protégées/masquées) : CI_IMAGE_VERSION, SCW_REGISTRY_MIGRATION, SCW_REGISTRY_MIGRATION_SECRET_KEY, AIRFLOW_API_URL, et AIRFLOW_API_TOKEN ou AIRFLOW_API_USER + AIRFLOW_API_PASSWORD.


ClickHouse — entrepôt analytique

Instance Scaleway mutualisée horsprod (accès whitelisté) : https://clickhouse-fdld.horsprod.ademe-scw.fr/play

  • 3 bases : int-fdld, rec-fdld, preprod-fdld
  • Connexion par environnement : apps/fdld/<env>clickhouse_app_username / clickhouse_app_password

Connexion TCP sécurisée (port 9440) :

clickhouse-client --host clickhouse-fdld.horsprod.ademe-scw.fr --port 9440 --secure \
  --accept-invalid-certificate --user USERNAME --password PASSWORD --query "SHOW DATABASES"

Lakekeeper — catalogue Iceberg REST

Catalogue technique du lakehouse (API REST Apache Iceberg). Les données des tables vivent sur S3, Lakekeeper en gère les métadonnées.

  • UI / API : https://int-fdld-lakekeeper.direct.horsprod.ademe-scw.fr (whitelisté)
  • URI catalogue (PyIceberg / Spark / Trino / DuckDB) : https://int-fdld-lakekeeper.direct.horsprod.ademe-scw.fr/catalog
  • Auth : aucune pour l'instant (filtrage IP uniquement)
  • Base de métadonnées : int-lakekeeper (RDB managé fdld)
  • Password : apps/fdld/integrationdb_lakekeeper_password
  • Clé de chiffrement (secret backend) : apps/fdld/integrationlakekeeper_encryption_key

Mise en service (une fois)

  1. Bootstrap du serveur : POST /management/v1/bootstrap avec {"accept-terms-of-use": true} (ou le bouton de l'UI au premier accès).
  2. Créer un warehouse pointant sur le bucket S3 (bucket + région/endpoint Scaleway + clés S3), via l'UI/API de management.

OpenSearch — moteur de recherche

Instance Scaleway managée dédiée à fdld, privée (pas d'endpoint public, résolue en-cluster). Sert de moteur de recherche à OpenMetadata.

  • Endpoint privé : <id>.<private-network-id>.internal:9200 (HTTPS) — voir l'output Terraform opensearch_private_host du run 044.opensearch
  • Dashboards Scaleway : port 5601
  • Admin : fdld_admin / apps/fdld/commonopensearch_cluster_admin_password_horsprod
  • Rattachement : opensearch = true dans scw-init-config/002.config_applications (comme pour ClickHouse)

Droits limités

L'offre managée Scaleway verrouille l'API security (rôles/users scopés impossibles à gérer par Terraform). Un unique utilisateur admin est donc utilisé.


OpenMetadata — catalogue de données & gouvernance

Découverte, lignage, qualité et gouvernance des données ; crawle les autres composants (bases, Airflow…).

  • UI : https://int-fdld-openmetadata.direct.horsprod.ademe-scw.fr/ (whitelisté)
  • Auth : basic (interne OpenMetadata)
  • Compte admin initial : admin@fdld.ademe.fr (mot de passe par défaut à changer au premier accès)
  • Auto-inscription (self-signup) activée (rôle non-admin par défaut)
  • Base de métadonnées : int-openmetadata (RDB managé fdld)
  • Password : apps/fdld/integrationdb_openmetadata_password
  • Clé Fernet : apps/fdld/integrationopenmetadata_fernet_key
  • Moteur de recherche : l'OpenSearch managé fdld ci-dessus (fdld_admin)
  • Ingestion : Jobs Kubernetes natifs (pas d'Airflow dédié)

Stockage S3

Buckets Scaleway (par environnement + usages transverses) :

Bucket Usage
int-fdld, rec-fdld, preprod-fdld Stockage objet applicatif + warehouse Iceberg (Lakekeeper)
int-fdld-airflow-logs Logs distants Airflow

Récapitulatif des secrets Vault

Composant Secret Chemin Vault
Airflow mot de passe admin apps/fdld/<env>airflow_admin_password
Airflow clés Fernet / API / JWT apps/fdld/<env>airflow_fernet_key / airflow_api_secret_key / airflow_jwt_secret
Airflow URI base de métadonnées apps/fdld/<env>airflow_metadata_connection
Airflow deploy token git-sync apps/fdld/<env>gitlab_deploy_token_user / gitlab_deploy_token
Airflow connexions / variables (path entier) apps/fdld/fdld/<env>/airflowAIRFLOW_CONN_* / AIRFLOW_VAR_*
Registry pull image produit apps/fdld/commonregistry_access_key / registry_access_token
ClickHouse user / password applicatif apps/fdld/<env>clickhouse_app_username / clickhouse_app_password
OpenSearch mot de passe admin (fdld_admin) apps/fdld/commonopensearch_cluster_admin_password_horsprod
Lakekeeper mot de passe DB apps/fdld/<env>db_lakekeeper_password
Lakekeeper clé de chiffrement apps/fdld/<env>lakekeeper_encryption_key
OpenMetadata mot de passe DB apps/fdld/<env>db_openmetadata_password
OpenMetadata clé Fernet apps/fdld/<env>openmetadata_fernet_key

Sécurité — à durcir avant tout usage hors intégration

  • Lakekeeper et OpenSearch sans authentification fine (accès protégé par l'IP-whitelist uniquement)
  • OpenMetadata en auth basic avec self-signup activé
  • Passage à l'OIDC Keycloak pour Airflow / Lakekeeper / OpenMetadata
  • Rotation du mot de passe admin OpenMetadata par défaut