From 93ead020f461c13de0f3170eb85c7dcda182b29f Mon Sep 17 00:00:00 2001 From: Thierry T <1940947+lepiaf@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:36:42 +0200 Subject: [PATCH 1/6] feat: Article REX plateforme data --- .../fr/2023-11-13-rex-plateforme-data.md | 263 ++++++++++++++++++ .../architecture.png | Bin 0 -> 67293 bytes 2 files changed, 263 insertions(+) create mode 100644 _articles/fr/2023-11-13-rex-plateforme-data.md create mode 100644 _assets/articles/2023-11-13-rex-plateforme-data/architecture.png diff --git a/_articles/fr/2023-11-13-rex-plateforme-data.md b/_articles/fr/2023-11-13-rex-plateforme-data.md new file mode 100644 index 000000000..d5bde8ddd --- /dev/null +++ b/_articles/fr/2023-11-13-rex-plateforme-data.md @@ -0,0 +1,263 @@ +--- +contentType: article +lang: fr +date: '2023-11-13' +slug: retour-experience-construction-plateforme-data +title: "Retour d'expérience sur la construction de la plate-forme data" +excerpt: | + Les besoins en analyse de données sont de plus en plus grandissant. Avec quelques outils, il est possible de faire + des extractions, transformation et visualisation très rapidement. Cependant, pour assurer la pérénité et + l'évolutivité de ces analyse, il est nécessaire de monter une plateforme dédié et d'industrialiser les différents + processus. +authors: + - tthuon +categories: + - architecture +--- + +## Le contexte + +Dans le cadre d'une mission Data Engineer chez un client, j'ai rejoins le pôle “Data Factory” pour analyse et comprendre +le comportement des utilisateurs. Cela permet leur permet de mieux guider l’ajout des fonctionnalités et des produits à +lancer. + +Un Poc (Proof of Concept, ou preuve de concept) a été mise en oeuvre par l'équipe data. Elle s'articule autour d'un +pipeline ELT (extract, load, transform) en utilisant les technologies suivantes : Google Cloud Platform, Talend, dbt et +Power BI. + +Pour rapidement tester le PoC, le pipeline est exécuté sur Jenkins. Cependant, le détournement de l'usage de Jenkins +pour l'exécution du pipeline n'est pas sans incidence. Jenkins n'est pas adapté pour ce travail : pas de retry, écriture +du pipeline en Groovy, un seul environnement d'exécution. + +__Comment fiabiliser les traitements et industrialiser le processus de déploiement ?__ + +C'est dans ce context que ma mission commence. + +## Le pipeline ELT : Extract, Load, Transform + +Avant de commencer les travaux, je me suis intéressé au fonctionnement du pipeline actuel. + +Comment ce pipeline fonctionne ? Qu'elles sont les étapes ? Qu'elles sont les besoin de ce pipeline ? + +Dans son principe de fonctionnement, il va chercher des données dans différentes sources, les charger dans un entrepôt +de données, transformer et créer de nouvelles structure données pour qu'elles soient ensuite affichés. + +Il est nécessaire de bien comprendre le fonctionnement actuel du pipeline avec de commencer tout changement. Ci-dessous, nous allons décortiquer son fonctionnement et lister les principaux composants. + +### Extraction et chargement des données dans Google BigQuery + +La première phase commence par l'extraction des données. Le pipeline va se connecter à différente sources de données. + +En sources de données, j'ai : + +- MySQL +- MongoDB +- Appel HTTP vers des API externes + +Pour la phase d'extraction et chargement dans [Google BigQuery](https://cloud.google.com/bigquery/docs/introduction), +Talend a été mis en place pour effecter ce travail. C'est un outil qui permet de faire des pipeline ETL (Extract, +Transform, Load; à ne pas confondre avec ELT) complète. Ici, il a été utilisé pour faire +uniquement la phase d'extraction et de chargement dans Google BigQuery. + +Le développement et la modification nécessite un client lourd et une compilation manuel du pipeline d'extraction. + +Une fois que la donnée est dans BigQuery, la phase de transformation peut commencer. + +### Transformation avec dbt + +En deuxième étape, il y a la transformation des données. + +Cette transformation est effectuée par [dbt](https://www.getdbt.com/) directement dans l'entrepôt de données Google +BigQuery. + +dbt n'exécute rien dans l'entrepôt de données, mais il permet d'organiser la transformation des données et de +templatiser les requêtes SQL. C'est Google BigQuery qui exécute les requêtes SQL. + +Durant cette phase de transformation, de nouvelle structure de données sont créés stocker le resultat des calculs. Ces +aggrégats de données sont ensuite affiché par un outil de visualisation de données : Power BI. + +### Affichage des données avec Power BI + +Enfin, en dernière étape de ce pipeline, il y a l'affichage des données. + +Le but final de tout ce travail est d'éviter d'effectuer tous les calculs au moment d'afficher les rapports d'analyse. +Sans le travail en amont de calcul et d'aggrégation de données, l'affichage des graphiques seraient très longs. + +Ce pipeline est fonctionnel et déjà en place avec Jenkins. Voyons l'architecture de la nouvelle plateforme data. + +## Architecture de la plateforme data + +Nous avons vu dans la précédente partie le fonctionnement du pipeline ELT dans Jenkins. Le but est de transposer dans +une plateforme plus robuste et adapté à ce type de travail. + +Pour cela, nous avons besoin d'un outil pour orchestrer ces différentes étape du pipeline et de les relancer en cas d' +erreur. Apache Airflow est le parfait candidat. Google propose une version géré : Google Composer. + +Les pré-requis pour cette nouvelle infrastructure sont les suivantes : + +- Utiliser Google Composer +- Utiliser le maximum d'outil géré par Google pour faciliter la maintenance +- Infrastructure as Code avec Terraform +- Des environnements séparés et dédiés pour les tests +- Tout le code nécessaire pour effectuer une tâche est dans une image Docker +- Surveillance et alerte en cas d'échec + +Nous avons donc le schéma suivant : + +![architecture]({BASE_URL}/imgs/articles/2023-11-13-rex-plateforme-data/architecture.png) + +Le schéma est assez dense, nous allons le décomposer. + +Tout d'abord, il y a une ligne de séparation entre l'infrastructure data et l'infrastructure, dit devops, qui est +propriétaire des bases de données. Cette démarcation se traduit dans le code de l'infrastructure et permet de +bien délimiter les responsabilites entre les équipes. + +Nous retrouvons donc en partie supérieure du schéma les sources de données de type base de données qui sont géré par +l'équipe devops e-commerce. Nous ferons des demandes d'accès à ces sources. + +Dans la partie inférieure, nous retrouvons toute l'infrastructure data. Il y a de nombreux service géré par Google. + +Nous pouvons lister les services suivants : + +- Secret Manager +- Artifact Registry +- Composer +- Cloud Storage +- Cloud IAM +- Cloud Logging +- Cloud Monitoring +- BigQuery + +Et pour l'environnement de développement spécifiquement, nous avons : + +- Cloud SQL +- MongoDB Atlas + +Toute l'installation et la configuration de l'infrastructure est effectuée avec Terraform. + +Une fois l'architecture dessiné et communiqué à l'équipe, nous pouvons la mettre en oeuvre. + +## Conditionnement des charges de travails + +Une fois que l'infrastructure est configurée avec Terraform, il reste à déployer le pipeline ELT que nous avons décris +précédemment. Il y a deux étapes : Extraction-Chargement et Transformation. La première étape est effectué par Talend, la +seconde par dbt. + +Le service Composer utilise Kubernetes pour exécuter Apache Airflow. En quelques mot, [Apache Airflow](https://airflow.apache.org/) est un logiciel libre qui permet d'exécuter et d'ordonnancer des tâches. + +Il serait donc intéressant d'exécuter nos travaux dans Kubernetes. Pour cela, nous avons besoin d'une image Docker. + +Talend et dbt sont conditionnés dans des images Docker. Il faudra écrire les fichiers Dockerfile et construire les images qui seront stocké dans le service Artifact Registry. Ainsi, à l'aide de l'opérateur [KubernetesPodOperator](https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/stable/operators.html) fourni par Apache Airflow, les charges de travails Talend et dbt sont exécuté dans Kubernetes. + +L'usage des images Docker facilite grandement l'usage d'outil diverse et varié qui ne seraient pas compatible avec l'environnement Composer. + +Je n'ai pas rencontré de difficulté particulière, hormis le choix de l'image de base pour Talend. Il [n'existe plus d'image](https://github.com/docker-library/openjdk/issues/505) officiel OpenJDK JRE. J'ai dû chercher et selectionner une image d'une des organisations qui construit une image Docker viable. L'image Docker de base fourni par l'organisation Adoptium me semblait la plus mûre : [https://hub.docker.com/_/eclipse-temurin/](https://hub.docker.com/_/eclipse-temurin/) + +Passons à la contruction du pipeline en lui même. + +## Le pipeline avec un Graph Orienté Acyclique + +Talend et dbt sont nos deux principales briques. Il reste à les organiser dans un fichier : un DAG. DAG pour _Directed Acyclic Graph_ ou Graph Orienté Acyclique en français. Pour simplifier, le graph se lit dans un sens, il a un début et une fin, et il n'est pas possible de revenir au début du graph. + +```mermaid +flowchart LR + talend[Extraction-Chargement Talend] --> dbt_model[dbt model] --> refresh_power_bi[Mise à jour PowerBI] +``` + +Ce diagramme va se traduire de cette façon dans un DAG Airflow. + +```python +from airflow import models +from airflow.providers.cncf.kubernetes.operators.pod import ( + KubernetesPodOperator, +) + + +with models.DAG(...) as dag: + talend = KubernetesPodOperator(...) + dbt_model = KubernetesPodOperator(...) + refresh_power_bi = KubernetesPodOperator(...) + + talend >> dbt_model >> refresh_power_bi +``` + +On retrouve dans le DAG l'opérateur _KubernetesPodOperator_, et enfin l'ordre des tâches qui seront exécuté par Airflow. + +La création du DAG n'est pas complexe en soit. Il y a des petites subtilité à bien comprendre pour maitriser le fonctionnement d'Airflow. + +Je vous en cite deux ci-dessous : les différentes dates dans Airflow, et la gestion des ressources. + +Ces deux points sont essentiels pour comprendre le fonctionne de Airflow. + +### Date de déclenchement, interval de date de donnée + +En plus de la notion de date de déclenchement de traitement, il y a les date d'intervalle de données. Airflow va déclencher un traitement pour une intervalle de date de données antérieur à la date de déclenchement et de la durée de la prochaine date de déclenchement. + +Prenons l'exemple suivant, pour un DAG configuré avec `schedule="0 0 * * *"`. Airflow doit déclencher un traitement tous les jours à minuit. + +Pour le jour actuel 18 octobre 2023 00h00 UTC +- la date de déclencement : "18 octobre 2023 00h00 UTC" +- la date de début de traitement des données : 17 octobre 2023 00h00 UTC +- la date de fin de traitement des données : 17 octobre 2023 23h59 UTC +- la date de prochain déclenchement : "19 octobre 2023 00h00 UTC" + +Pour plus d'information, [https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html#data-interval](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html#data-interval) + +Cette notion n'est pas importante dans notre cas d'usage, mais il l'est lorsque les traitements doivent extraire des données sur un interval de date. Cela permet de ne prendre qu'une partie des données et non l'intégralité. Il est également possible de rejouer des traitements sur une période spécifique. + +N'oubliez pas que les dates sont dans le fuseau horaire UTC ! Si votre grappe Composer démarre à minuit au fuseau horaire Europe/Paris (donc 22h00 UTC __la veille__), il va avoir un double traitement : 1 traitement pour l'intervalle de date de donnée de la veille et 1 autre pour l'intervalle de date de données du jour du démarrage du Composer. + +### Gérer les resources + +Gérer les resources CPU et mémoire ne sont pas évidente. En particulier sur des langages que je ne connaît pas. + +De manière générale, plus la charge de travail à de la ressource, plus elle va faire le traitement rapidement. C'est le cas de Talend. + +Avec 1 CPU et 4Gio de mémoire, l'exécution était longue. En passant à 4 CPU et 8Gio, ça réduit le temps de moitié. + +Dans le DAG, cela se traduit de cette façon + +```python +from kubernetes.client import models as k8s_models +from airflow.providers.cncf.kubernetes.operators.pod import ( + KubernetesPodOperator, +) + +talend = KubernetesPodOperator( + (...) + container_resources=k8s_models.V1ResourceRequirements( + requests={ + "cpu": "4", + "memory": "8G", + }, + ), +) +``` + +Les graphiques dans Google Monitoring m'ont aidé à faire ce changement et à surveiller l'utilisation des resources. + +Il est essentiel d'avoir un système de surveillance et d'alerte au plus tôt possible dans le projet. Cela permet de voir rapidement l'évolution de l'usage des ressources et d'y remédier en amont. + +Pour le moment, cette nouvelle infrastructure n'est pas encore en production, mais elle possède tous les composants nécessaire à sa mise en production. + +## Mise en production + +Tout ce travail est inutile s'il n'est pas mis en production. Pour préparer la mise en production, j'ai mis en place une copie à l'identique de tous les dataset BigQuery d'origine vers la nouvelle infrastructure. J'ai utilisé le service _Google Data Transfers_. + +Ensuite, j'ai rédigé une liste de vérification pour m'assure de ne rien oublier. J'anticipe au maximum toutes les étapes. Le risque est une perte complète des données lors de la transition. Cette liste doit être la plus explicite et directive possible. Il faut être en mesure dérouler sans se poser de question. + +Je me suis synchronisé avec l'équipe pour planifier la mise en production. + +Le jour J, la liste est déroulé. Une fois la mise en production terminée, il y a une surveillance active des traitements. Le tableau de bord de surveillance est vérifié quotidiennement. Dès qu'il y a une erreur, elle est corrigé au plus tôt, et de nouveau il y a une surveillance active de ce correctif. + +## Et la suite ? + +Suite à cette mise en production, l'infrastructure ne va pas beaucoup changer. Il y aura principalement de la maintenance et des mises à jour à effecture. En particulier sur le service Composer. + +Un des points de souffrance sur le pipeline est Talend. Cet outil ne s'adapte pas bien à un environnement Cloud. Le projet serait de trouver une solution alternative. Quel serait l'outil adapté pour de l'extraction de données et qui serait complètement géré par Google ? + +## Pour conclure, mon retour d'expérience + +La construction de cette plateforme data à été un grand projet. Tout a été construit depuis zéro. J'ai bien cerné la problématique, cela m'a permis d'identifier tous les éléments sur le fonctionnement du pipeline. La solution adopté d'adapte à son fonctionnement et aux pré-requis. Enfin, la mise en production s'est déroulé comme prévu. La mise en place d'une surveillance active m'a permis de détecter les erreurs en amont et au plus tôt. Cela réduit considérable les temps d'indisponibilité de la plateforme. + +Pour ma part, cette mission a été très complète. J'ai été tantôt été _Architecte_ avec la conception de l'infrastructure, _Ops_ avec l'écriture du Terraform et de la bonne compréhension de Google Cloud Platform, et enfin _Dev_ avec la rédaction du DAG Airflow. J'en ressort encore plus riche de cette expérience. diff --git a/_assets/articles/2023-11-13-rex-plateforme-data/architecture.png b/_assets/articles/2023-11-13-rex-plateforme-data/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..f3791a1d6a8d321923903b09d8e28c4dc966a76d GIT binary patch literal 67293 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 literal 0 HcmV?d00001 From 49e02228797a32b6926ea0684f0fb8012a4adf77 Mon Sep 17 00:00:00 2001 From: ElevenTom Date: Mon, 13 Nov 2023 14:30:14 +0100 Subject: [PATCH 2/6] Update 2023-11-13-rex-plateforme-data.md fix typos --- .../fr/2023-11-13-rex-plateforme-data.md | 110 +++++++++--------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/_articles/fr/2023-11-13-rex-plateforme-data.md b/_articles/fr/2023-11-13-rex-plateforme-data.md index d5bde8ddd..5969fa631 100644 --- a/_articles/fr/2023-11-13-rex-plateforme-data.md +++ b/_articles/fr/2023-11-13-rex-plateforme-data.md @@ -5,10 +5,10 @@ date: '2023-11-13' slug: retour-experience-construction-plateforme-data title: "Retour d'expérience sur la construction de la plate-forme data" excerpt: | - Les besoins en analyse de données sont de plus en plus grandissant. Avec quelques outils, il est possible de faire - des extractions, transformation et visualisation très rapidement. Cependant, pour assurer la pérénité et - l'évolutivité de ces analyse, il est nécessaire de monter une plateforme dédié et d'industrialiser les différents - processus. + Les besoins en analyse de données sont grandissants. Avec quelques outils, il est possible de faire + des extractions, de la transformation et de la visualisation très rapidement. Cependant, pour assurer la pérénité et + l'évolutivité de ces analyses, il est nécessaire de monter une plateforme dédiée et d'industrialiser les différents + processus. C'est le sujet de cet article. authors: - tthuon categories: @@ -17,11 +17,11 @@ categories: ## Le contexte -Dans le cadre d'une mission Data Engineer chez un client, j'ai rejoins le pôle “Data Factory” pour analyse et comprendre -le comportement des utilisateurs. Cela permet leur permet de mieux guider l’ajout des fonctionnalités et des produits à +Dans le cadre d'une mission Data Engineer chez un client, j'ai rejoint le pôle “Data Factory” pour analyser et comprendre +le comportement des utilisateurs. Cela permet de mieux guider l’ajout des fonctionnalités et des produits à lancer. -Un Poc (Proof of Concept, ou preuve de concept) a été mise en oeuvre par l'équipe data. Elle s'articule autour d'un +Un Poc ("Proof of Concept", ou preuve de concept) a été mise en oeuvre par l'équipe data. Elle s'articule autour d'un pipeline ELT (extract, load, transform) en utilisant les technologies suivantes : Google Cloud Platform, Talend, dbt et Power BI. @@ -31,22 +31,22 @@ du pipeline en Groovy, un seul environnement d'exécution. __Comment fiabiliser les traitements et industrialiser le processus de déploiement ?__ -C'est dans ce context que ma mission commence. +C'est dans ce contexte que ma mission commence. ## Le pipeline ELT : Extract, Load, Transform Avant de commencer les travaux, je me suis intéressé au fonctionnement du pipeline actuel. -Comment ce pipeline fonctionne ? Qu'elles sont les étapes ? Qu'elles sont les besoin de ce pipeline ? +Comment ce pipeline fonctionne ? Quelles sont les étapes à respecter ? Quelles sont les besoins de ce pipeline ? Dans son principe de fonctionnement, il va chercher des données dans différentes sources, les charger dans un entrepôt -de données, transformer et créer de nouvelles structure données pour qu'elles soient ensuite affichés. +de données, transformer et créer de nouvelles structures données pour qu'elles soient ensuite affichées. Il est nécessaire de bien comprendre le fonctionnement actuel du pipeline avec de commencer tout changement. Ci-dessous, nous allons décortiquer son fonctionnement et lister les principaux composants. ### Extraction et chargement des données dans Google BigQuery -La première phase commence par l'extraction des données. Le pipeline va se connecter à différente sources de données. +La première phase commence par l'extraction des données. Le pipeline va se connecter à différentes sources de données. En sources de données, j'ai : @@ -54,12 +54,12 @@ En sources de données, j'ai : - MongoDB - Appel HTTP vers des API externes -Pour la phase d'extraction et chargement dans [Google BigQuery](https://cloud.google.com/bigquery/docs/introduction), -Talend a été mis en place pour effecter ce travail. C'est un outil qui permet de faire des pipeline ETL (Extract, -Transform, Load; à ne pas confondre avec ELT) complète. Ici, il a été utilisé pour faire +Pour la phase d'extraction et le chargement dans [Google BigQuery](https://cloud.google.com/bigquery/docs/introduction), +Talend a été mis en place pour effecter ce travail. C'est un outil qui permet de faire des pipelines ETL (Extract, +Transform, Load ; à ne pas confondre avec ELT) complètes. Ici, il a été utilisé pour faire uniquement la phase d'extraction et de chargement dans Google BigQuery. -Le développement et la modification nécessite un client lourd et une compilation manuel du pipeline d'extraction. +Le développement et la modification nécessitent un client lourd et une compilation manuelle du pipeline d'extraction. Une fois que la donnée est dans BigQuery, la phase de transformation peut commencer. @@ -73,30 +73,30 @@ BigQuery. dbt n'exécute rien dans l'entrepôt de données, mais il permet d'organiser la transformation des données et de templatiser les requêtes SQL. C'est Google BigQuery qui exécute les requêtes SQL. -Durant cette phase de transformation, de nouvelle structure de données sont créés stocker le resultat des calculs. Ces -aggrégats de données sont ensuite affiché par un outil de visualisation de données : Power BI. +Durant cette phase de transformation, de nouvelles structures de données sont créées afin de stocker le résultat des calculs. Ces +aggrégats de données sont ensuite affichés par un outil de visualisation de données : Power BI. ### Affichage des données avec Power BI Enfin, en dernière étape de ce pipeline, il y a l'affichage des données. Le but final de tout ce travail est d'éviter d'effectuer tous les calculs au moment d'afficher les rapports d'analyse. -Sans le travail en amont de calcul et d'aggrégation de données, l'affichage des graphiques seraient très longs. +Sans le travail en amont de calcul et d'aggrégation de données, l'affichage des graphiques serait très longs. Ce pipeline est fonctionnel et déjà en place avec Jenkins. Voyons l'architecture de la nouvelle plateforme data. ## Architecture de la plateforme data -Nous avons vu dans la précédente partie le fonctionnement du pipeline ELT dans Jenkins. Le but est de transposer dans -une plateforme plus robuste et adapté à ce type de travail. +Nous avons vu dans la précédente partie le fonctionnement du pipeline ELT dans Jenkins. Le but est de transposer cela dans +une plateforme plus robuste et adaptée à ce type de travail. Pour cela, nous avons besoin d'un outil pour orchestrer ces différentes étape du pipeline et de les relancer en cas d' -erreur. Apache Airflow est le parfait candidat. Google propose une version géré : Google Composer. +erreur. Apache Airflow est le parfait candidat. Google propose une version gérée : Google Composer. -Les pré-requis pour cette nouvelle infrastructure sont les suivantes : +Les pré-requis pour cette nouvelle infrastructure sont les suivants : - Utiliser Google Composer -- Utiliser le maximum d'outil géré par Google pour faciliter la maintenance +- Utiliser le maximum d'outils gérés par Google pour faciliter la maintenance - Infrastructure as Code avec Terraform - Des environnements séparés et dédiés pour les tests - Tout le code nécessaire pour effectuer une tâche est dans une image Docker @@ -108,14 +108,14 @@ Nous avons donc le schéma suivant : Le schéma est assez dense, nous allons le décomposer. -Tout d'abord, il y a une ligne de séparation entre l'infrastructure data et l'infrastructure, dit devops, qui est +Tout d'abord, il y a une ligne de séparation entre l'infrastructure data et l'infrastructure dit devops, qui est propriétaire des bases de données. Cette démarcation se traduit dans le code de l'infrastructure et permet de -bien délimiter les responsabilites entre les équipes. +bien délimiter les responsabilités entre les équipes. -Nous retrouvons donc en partie supérieure du schéma les sources de données de type base de données qui sont géré par +Nous retrouvons donc en partie supérieure du schéma les sources de données de type base de données qui sont géréeé par l'équipe devops e-commerce. Nous ferons des demandes d'accès à ces sources. -Dans la partie inférieure, nous retrouvons toute l'infrastructure data. Il y a de nombreux service géré par Google. +Dans la partie inférieure, nous retrouvons toute l'infrastructure data. Il y a de nombreux services gérés par Google. Nous pouvons lister les services suivants : @@ -135,25 +135,25 @@ Et pour l'environnement de développement spécifiquement, nous avons : Toute l'installation et la configuration de l'infrastructure est effectuée avec Terraform. -Une fois l'architecture dessiné et communiqué à l'équipe, nous pouvons la mettre en oeuvre. +Une fois l'architecture dessinée et communiquée à l'équipe, nous pouvons la mettre en oeuvre. -## Conditionnement des charges de travails +## Conditionnement des charges de travail -Une fois que l'infrastructure est configurée avec Terraform, il reste à déployer le pipeline ELT que nous avons décris -précédemment. Il y a deux étapes : Extraction-Chargement et Transformation. La première étape est effectué par Talend, la +Une fois que l'infrastructure est configurée avec Terraform, il reste à déployer le pipeline ELT que nous avons décrit +précédemment. Il y a deux étapes : Extraction-Chargement, et Transformation. La première étape est effectuée par Talend, la seconde par dbt. -Le service Composer utilise Kubernetes pour exécuter Apache Airflow. En quelques mot, [Apache Airflow](https://airflow.apache.org/) est un logiciel libre qui permet d'exécuter et d'ordonnancer des tâches. +Le service Composer utilise Kubernetes pour exécuter Apache Airflow. En quelques mots, [Apache Airflow](https://airflow.apache.org/) est un logiciel libre qui permet d'exécuter et d'ordonnancer des tâches. Il serait donc intéressant d'exécuter nos travaux dans Kubernetes. Pour cela, nous avons besoin d'une image Docker. -Talend et dbt sont conditionnés dans des images Docker. Il faudra écrire les fichiers Dockerfile et construire les images qui seront stocké dans le service Artifact Registry. Ainsi, à l'aide de l'opérateur [KubernetesPodOperator](https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/stable/operators.html) fourni par Apache Airflow, les charges de travails Talend et dbt sont exécuté dans Kubernetes. +Talend et dbt sont conditionnés dans des images Docker. Il faudra écrire les fichiers Dockerfile et construire les images qui seront stockées dans le service Artifact Registry. Ainsi, à l'aide de l'opérateur [KubernetesPodOperator](https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/stable/operators.html) fourni par Apache Airflow, les charges de travail Talend et dbt sont exécutées dans Kubernetes. -L'usage des images Docker facilite grandement l'usage d'outil diverse et varié qui ne seraient pas compatible avec l'environnement Composer. +L'usage des images Docker facilite grandement l'usage d'outils divers et variés qui ne seraient pas compatibles avec l'environnement Composer. -Je n'ai pas rencontré de difficulté particulière, hormis le choix de l'image de base pour Talend. Il [n'existe plus d'image](https://github.com/docker-library/openjdk/issues/505) officiel OpenJDK JRE. J'ai dû chercher et selectionner une image d'une des organisations qui construit une image Docker viable. L'image Docker de base fourni par l'organisation Adoptium me semblait la plus mûre : [https://hub.docker.com/_/eclipse-temurin/](https://hub.docker.com/_/eclipse-temurin/) +Je n'ai pas rencontré de difficulté particulière, hormis le choix de l'image de base pour Talend. Il [n'existe plus d'image](https://github.com/docker-library/openjdk/issues/505) officiel OpenJDK JRE. J'ai dû chercher et selectionner une image d'une des organisations qui construit une image Docker viable. L'image Docker de base fournie par l'organisation Adoptium me semblait la plus mûre : [https://hub.docker.com/_/eclipse-temurin/](https://hub.docker.com/_/eclipse-temurin/) -Passons à la contruction du pipeline en lui même. +Passons à la contruction du pipeline en lui-même. ## Le pipeline avec un Graph Orienté Acyclique @@ -181,41 +181,41 @@ with models.DAG(...) as dag: talend >> dbt_model >> refresh_power_bi ``` -On retrouve dans le DAG l'opérateur _KubernetesPodOperator_, et enfin l'ordre des tâches qui seront exécuté par Airflow. +On retrouve dans le DAG l'opérateur _KubernetesPodOperator_, et enfin l'ordre des tâches qui seront exécutées par Airflow. -La création du DAG n'est pas complexe en soit. Il y a des petites subtilité à bien comprendre pour maitriser le fonctionnement d'Airflow. +La création du DAG n'est pas complexe en soit. Il y a des petites subtilités à bien comprendre pour maitriser le fonctionnement d'Airflow. Je vous en cite deux ci-dessous : les différentes dates dans Airflow, et la gestion des ressources. -Ces deux points sont essentiels pour comprendre le fonctionne de Airflow. +Ces deux points sont essentiels pour comprendre le fonctionnement d'Airflow. -### Date de déclenchement, interval de date de donnée +### Date de déclenchement, interval de date de données -En plus de la notion de date de déclenchement de traitement, il y a les date d'intervalle de données. Airflow va déclencher un traitement pour une intervalle de date de données antérieur à la date de déclenchement et de la durée de la prochaine date de déclenchement. +En plus de la notion de date de déclenchement de traitement, il y a les dates d'intervalles de données. Airflow va déclencher un traitement pour un intervalle de dates de données antérieur à la date de déclenchement et de la durée de la prochaine date de déclenchement. Prenons l'exemple suivant, pour un DAG configuré avec `schedule="0 0 * * *"`. Airflow doit déclencher un traitement tous les jours à minuit. Pour le jour actuel 18 octobre 2023 00h00 UTC -- la date de déclencement : "18 octobre 2023 00h00 UTC" +- la date de déclenchement : "18 octobre 2023 00h00 UTC" - la date de début de traitement des données : 17 octobre 2023 00h00 UTC - la date de fin de traitement des données : 17 octobre 2023 23h59 UTC - la date de prochain déclenchement : "19 octobre 2023 00h00 UTC" -Pour plus d'information, [https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html#data-interval](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html#data-interval) +Pour plus d'informations, [https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html#data-interval](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html#data-interval) -Cette notion n'est pas importante dans notre cas d'usage, mais il l'est lorsque les traitements doivent extraire des données sur un interval de date. Cela permet de ne prendre qu'une partie des données et non l'intégralité. Il est également possible de rejouer des traitements sur une période spécifique. +Cette notion n'est pas importante dans notre cas d'usage, mais elle l'est lorsque les traitements doivent extraire des données sur un intervalle de dates. Cela permet de ne prendre qu'une partie des données et non l'intégralité. Il est également possible de rejouer des traitements sur une période spécifique. -N'oubliez pas que les dates sont dans le fuseau horaire UTC ! Si votre grappe Composer démarre à minuit au fuseau horaire Europe/Paris (donc 22h00 UTC __la veille__), il va avoir un double traitement : 1 traitement pour l'intervalle de date de donnée de la veille et 1 autre pour l'intervalle de date de données du jour du démarrage du Composer. +N'oubliez pas que les dates sont dans le fuseau horaire UTC ! Si votre grappe Composer démarre à minuit au fuseau horaire Europe/Paris (donc 22h00 UTC __la veille__), il va avoir un double traitement : 1 traitement pour l'intervalle de dates de donnée de la veille et 1 autre pour l'intervalle de dates de données du jour du démarrage du Composer. ### Gérer les resources -Gérer les resources CPU et mémoire ne sont pas évidente. En particulier sur des langages que je ne connaît pas. +Gérer les resources CPU et mémoire n'est pas évident, en particulier sur des langages inconnus. -De manière générale, plus la charge de travail à de la ressource, plus elle va faire le traitement rapidement. C'est le cas de Talend. +De manière générale, plus la charge de travail a de la ressource, plus elle va faire le traitement rapidement. C'est le cas de Talend. Avec 1 CPU et 4Gio de mémoire, l'exécution était longue. En passant à 4 CPU et 8Gio, ça réduit le temps de moitié. -Dans le DAG, cela se traduit de cette façon +Dans le DAG, cela se traduit de cette façon : ```python from kubernetes.client import models as k8s_models @@ -236,28 +236,28 @@ talend = KubernetesPodOperator( Les graphiques dans Google Monitoring m'ont aidé à faire ce changement et à surveiller l'utilisation des resources. -Il est essentiel d'avoir un système de surveillance et d'alerte au plus tôt possible dans le projet. Cela permet de voir rapidement l'évolution de l'usage des ressources et d'y remédier en amont. +Il est essentiel d'avoir un système de surveillance et d'alerte le plus tôt possible dans le projet, cela permet de voir rapidement l'évolution de l'usage des ressources et d'y remédier. -Pour le moment, cette nouvelle infrastructure n'est pas encore en production, mais elle possède tous les composants nécessaire à sa mise en production. +Pour le moment, cette nouvelle infrastructure n'est pas encore en production, mais elle possède tous les composants nécessaires à sa mise en production. ## Mise en production Tout ce travail est inutile s'il n'est pas mis en production. Pour préparer la mise en production, j'ai mis en place une copie à l'identique de tous les dataset BigQuery d'origine vers la nouvelle infrastructure. J'ai utilisé le service _Google Data Transfers_. -Ensuite, j'ai rédigé une liste de vérification pour m'assure de ne rien oublier. J'anticipe au maximum toutes les étapes. Le risque est une perte complète des données lors de la transition. Cette liste doit être la plus explicite et directive possible. Il faut être en mesure dérouler sans se poser de question. +Ensuite, j'ai rédigé une liste de vérification pour m'assurer de ne rien oublier. J'anticipe au maximum toutes les étapes. Le risque est une perte complète des données lors de la transition. Cette liste doit être la plus explicite et directive possible. Il faut être en mesure de dérouler sans se poser de question. Je me suis synchronisé avec l'équipe pour planifier la mise en production. -Le jour J, la liste est déroulé. Une fois la mise en production terminée, il y a une surveillance active des traitements. Le tableau de bord de surveillance est vérifié quotidiennement. Dès qu'il y a une erreur, elle est corrigé au plus tôt, et de nouveau il y a une surveillance active de ce correctif. +Le jour J, la liste est déroulée. Une fois la mise en production terminée, il y a une surveillance active des traitements. Le tableau de bord de surveillance est vérifié quotidiennement. Dès qu'il y a une erreur, elle est corrigée au plus tôt, et de nouveau il y a une surveillance active de ce correctif. ## Et la suite ? -Suite à cette mise en production, l'infrastructure ne va pas beaucoup changer. Il y aura principalement de la maintenance et des mises à jour à effecture. En particulier sur le service Composer. +Suite à cette mise en production, l'infrastructure ne va pas beaucoup changer. Il y aura principalement de la maintenance et des mises à jour à effectuer, en particulier sur le service Composer. Un des points de souffrance sur le pipeline est Talend. Cet outil ne s'adapte pas bien à un environnement Cloud. Le projet serait de trouver une solution alternative. Quel serait l'outil adapté pour de l'extraction de données et qui serait complètement géré par Google ? ## Pour conclure, mon retour d'expérience -La construction de cette plateforme data à été un grand projet. Tout a été construit depuis zéro. J'ai bien cerné la problématique, cela m'a permis d'identifier tous les éléments sur le fonctionnement du pipeline. La solution adopté d'adapte à son fonctionnement et aux pré-requis. Enfin, la mise en production s'est déroulé comme prévu. La mise en place d'une surveillance active m'a permis de détecter les erreurs en amont et au plus tôt. Cela réduit considérable les temps d'indisponibilité de la plateforme. +La construction de cette plateforme data à été un grand projet. Tout a été construit depuis zéro. J'ai bien cerné la problématique, cela m'a permis d'identifier tous les éléments sur le fonctionnement du pipeline. La solution a été de s'adapter à son fonctionnement et aux pré-requis. Enfin, la mise en production s'est déroulée comme prévu. La mise en place d'une surveillance active m'a permis de détecter les erreurs en amont. Cela réduit considérablement les temps d'indisponibilité de la plateforme. -Pour ma part, cette mission a été très complète. J'ai été tantôt été _Architecte_ avec la conception de l'infrastructure, _Ops_ avec l'écriture du Terraform et de la bonne compréhension de Google Cloud Platform, et enfin _Dev_ avec la rédaction du DAG Airflow. J'en ressort encore plus riche de cette expérience. +Pour ma part, cette mission a été très complète. J'ai tantôt été _Architecte_ avec la conception de l'infrastructure, _Ops_ avec l'écriture du Terraform et de la bonne compréhension de Google Cloud Platform, et enfin _Dev_ avec la rédaction du DAG Airflow. J'en ressort avec encore plus d'expérience ! From 23efa96f6070ed2bf0cd05f1f16c98de4ecc4773 Mon Sep 17 00:00:00 2001 From: Thierry T <1940947+lepiaf@users.noreply.github.com> Date: Mon, 13 Nov 2023 15:40:38 +0100 Subject: [PATCH 3/6] Apply suggestions from code review Co-authored-by: Cindyvlv <148534693+Cindyvlv@users.noreply.github.com> --- _articles/fr/2023-11-13-rex-plateforme-data.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/_articles/fr/2023-11-13-rex-plateforme-data.md b/_articles/fr/2023-11-13-rex-plateforme-data.md index 5969fa631..7b031a0e3 100644 --- a/_articles/fr/2023-11-13-rex-plateforme-data.md +++ b/_articles/fr/2023-11-13-rex-plateforme-data.md @@ -15,9 +15,9 @@ categories: - architecture --- -## Le contexte +## Le contexte du REX autour de la construction d'une plateforme Data pour notre client -Dans le cadre d'une mission Data Engineer chez un client, j'ai rejoint le pôle “Data Factory” pour analyser et comprendre +Dans le cadre d'une mission Data Engineer pour un client du [Studio Eleven Labs](https://eleven-labs.com/conception-d-application), j'ai rejoint le pôle “Data Factory” pour analyser et comprendre le comportement des utilisateurs. Cela permet de mieux guider l’ajout des fonctionnalités et des produits à lancer. @@ -258,6 +258,6 @@ Un des points de souffrance sur le pipeline est Talend. Cet outil ne s'adapte pa ## Pour conclure, mon retour d'expérience -La construction de cette plateforme data à été un grand projet. Tout a été construit depuis zéro. J'ai bien cerné la problématique, cela m'a permis d'identifier tous les éléments sur le fonctionnement du pipeline. La solution a été de s'adapter à son fonctionnement et aux pré-requis. Enfin, la mise en production s'est déroulée comme prévu. La mise en place d'une surveillance active m'a permis de détecter les erreurs en amont. Cela réduit considérablement les temps d'indisponibilité de la plateforme. +La construction de cette plateforme data a été un grand projet de notre [Studio Eleven Labs](https://eleven-labs.com/nos-publications/donnez-une-nouvelle-dimension-a-votre-equipe-produit). Tout a été construit depuis zéro. J'ai bien cerné la problématique, cela m'a permis d'identifier tous les éléments sur le fonctionnement du pipeline. La solution a été de s'adapter à son fonctionnement et aux pré-requis. Enfin, la mise en production s'est déroulée comme prévu. La mise en place d'une surveillance active m'a permis de détecter les erreurs en amont. Cela réduit considérablement les temps d'indisponibilité de la plateforme. Pour ma part, cette mission a été très complète. J'ai tantôt été _Architecte_ avec la conception de l'infrastructure, _Ops_ avec l'écriture du Terraform et de la bonne compréhension de Google Cloud Platform, et enfin _Dev_ avec la rédaction du DAG Airflow. J'en ressort avec encore plus d'expérience ! From 7d575a0ef42ba944818839357f9586d659a181d4 Mon Sep 17 00:00:00 2001 From: Thierry T <1940947+lepiaf@users.noreply.github.com> Date: Mon, 13 Nov 2023 15:44:20 +0100 Subject: [PATCH 4/6] fix: simplify explanation of dbt part --- _articles/fr/2023-11-13-rex-plateforme-data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_articles/fr/2023-11-13-rex-plateforme-data.md b/_articles/fr/2023-11-13-rex-plateforme-data.md index 7b031a0e3..b337c94de 100644 --- a/_articles/fr/2023-11-13-rex-plateforme-data.md +++ b/_articles/fr/2023-11-13-rex-plateforme-data.md @@ -70,7 +70,7 @@ En deuxième étape, il y a la transformation des données. Cette transformation est effectuée par [dbt](https://www.getdbt.com/) directement dans l'entrepôt de données Google BigQuery. -dbt n'exécute rien dans l'entrepôt de données, mais il permet d'organiser la transformation des données et de +dbt permet d'organiser la transformation des données et de templatiser les requêtes SQL. C'est Google BigQuery qui exécute les requêtes SQL. Durant cette phase de transformation, de nouvelles structures de données sont créées afin de stocker le résultat des calculs. Ces From 48409edb45e1ae5a17c01d30babf04ab8fecee9c Mon Sep 17 00:00:00 2001 From: Thierry T <1940947+lepiaf@users.noreply.github.com> Date: Tue, 14 Nov 2023 09:58:58 +0100 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Cindyvlv <148534693+Cindyvlv@users.noreply.github.com> --- _articles/fr/2023-11-13-rex-plateforme-data.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/_articles/fr/2023-11-13-rex-plateforme-data.md b/_articles/fr/2023-11-13-rex-plateforme-data.md index b337c94de..f3be8a359 100644 --- a/_articles/fr/2023-11-13-rex-plateforme-data.md +++ b/_articles/fr/2023-11-13-rex-plateforme-data.md @@ -2,8 +2,8 @@ contentType: article lang: fr date: '2023-11-13' -slug: retour-experience-construction-plateforme-data -title: "Retour d'expérience sur la construction de la plate-forme data" +slug: rex-construction-data-platform +title: "Construction d'une plateforme Data, retour d'expérience (REX)" excerpt: | Les besoins en analyse de données sont grandissants. Avec quelques outils, il est possible de faire des extractions, de la transformation et de la visualisation très rapidement. Cependant, pour assurer la pérénité et @@ -51,7 +51,7 @@ La première phase commence par l'extraction des données. Le pipeline va se con En sources de données, j'ai : - MySQL -- MongoDB +- MongoDB ([voir notre article de blog sur MongoDB](https://blog.eleven-labs.com/fr/symfony-et-mongodb-retour-aux-sources/)) - Appel HTTP vers des API externes Pour la phase d'extraction et le chargement dans [Google BigQuery](https://cloud.google.com/bigquery/docs/introduction), From 7ab662e2cbdf55ecca97a8a2515259f7053ad003 Mon Sep 17 00:00:00 2001 From: Thierry T <1940947+lepiaf@users.noreply.github.com> Date: Tue, 14 Nov 2023 15:16:09 +0100 Subject: [PATCH 6/6] update article date --- ...me-data.md => 2023-11-14-rex-plateforme-data.md} | 4 ++-- .../architecture.png | Bin 2 files changed, 2 insertions(+), 2 deletions(-) rename _articles/fr/{2023-11-13-rex-plateforme-data.md => 2023-11-14-rex-plateforme-data.md} (99%) rename _assets/articles/{2023-11-13-rex-plateforme-data => 2023-11-14-rex-plateforme-data}/architecture.png (100%) diff --git a/_articles/fr/2023-11-13-rex-plateforme-data.md b/_articles/fr/2023-11-14-rex-plateforme-data.md similarity index 99% rename from _articles/fr/2023-11-13-rex-plateforme-data.md rename to _articles/fr/2023-11-14-rex-plateforme-data.md index f3be8a359..3faf57ab9 100644 --- a/_articles/fr/2023-11-13-rex-plateforme-data.md +++ b/_articles/fr/2023-11-14-rex-plateforme-data.md @@ -1,7 +1,7 @@ --- contentType: article lang: fr -date: '2023-11-13' +date: '2023-11-14' slug: rex-construction-data-platform title: "Construction d'une plateforme Data, retour d'expérience (REX)" excerpt: | @@ -104,7 +104,7 @@ Les pré-requis pour cette nouvelle infrastructure sont les suivants : Nous avons donc le schéma suivant : -![architecture]({BASE_URL}/imgs/articles/2023-11-13-rex-plateforme-data/architecture.png) +![architecture]({BASE_URL}/imgs/articles/2023-11-14-rex-plateforme-data/architecture.png) Le schéma est assez dense, nous allons le décomposer. diff --git a/_assets/articles/2023-11-13-rex-plateforme-data/architecture.png b/_assets/articles/2023-11-14-rex-plateforme-data/architecture.png similarity index 100% rename from _assets/articles/2023-11-13-rex-plateforme-data/architecture.png rename to _assets/articles/2023-11-14-rex-plateforme-data/architecture.png