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 dansargocd-apps/fdld/helm-values/* - Infra Scaleway : Terraform dans
scw-application/horsprod/terraform/*etscw-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/integration→airflow_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/integration→airflow_metadata_connection. - Clés stables externalisées depuis Vault pour éviter un
OutOfSyncpermanent ArgoCD :airflow_fernet_key,airflow_api_secret_key,airflow_jwt_secret. - Jobs
migrate-database/create-userconvertis 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/integration → gitlab_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) ouAIRFLOW_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_LOGSdans le path Vault) doit porterextra.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/common→registry_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/integration→db_lakekeeper_password - Clé de chiffrement (secret backend) :
apps/fdld/integration→lakekeeper_encryption_key
Mise en service (une fois)
- Bootstrap du serveur :
POST /management/v1/bootstrapavec{"accept-terms-of-use": true}(ou le bouton de l'UI au premier accès). - 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 Terraformopensearch_private_hostdu run044.opensearch - Dashboards Scaleway : port 5601
- Admin :
fdld_admin/apps/fdld/common→opensearch_cluster_admin_password_horsprod - Rattachement :
opensearch = truedansscw-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/integration→db_openmetadata_password - Clé Fernet :
apps/fdld/integration→openmetadata_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>/airflow → AIRFLOW_CONN_* / AIRFLOW_VAR_* |
| Registry | pull image produit | apps/fdld/common → registry_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/common → opensearch_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
basicavec self-signup activé - Passage à l'OIDC Keycloak pour Airflow / Lakekeeper / OpenMetadata
- Rotation du mot de passe admin OpenMetadata par défaut