Quantcast
Channel: Blog Ippon - Ippon Technologies
Viewing all 927 articles
Browse latest View live

Delta Lake et AWS : mariage forcé

$
0
0
Delta Lake et AWS : mariage forcé

Delta Lake apporte de nombreux avantages aux projets Data basés sur Spark. Il peut considérablement simplifier vos workflows grâce aux opérations UPDATE, DELETE et MERGE. Il permet également de rendre vos pipelines plus robustes grâce aux transactions ACID et au schema enforcement. Néanmoins, si votre plateforme est construite avec les services AWS et que vous souhaitez utiliser Delta Lake (projet open source sous license Apache 2.0, donné à la fondation Linux), vous allez être confrontés à quelques difficultés.

L’objectif de cet article est de vous donner des astuces pour les surmonter et des éléments pour mieux évaluer les compromis à faire lors de l’adoption de cette techno dans l’environnement AWS.

Qu’est-ce que Delta Lake ?

Delta Lake est un format de stockage de données structurées permettant d’y ajouter une couche transactionnelle.
Concrètement, Delta Lake stocke des fichiers Parquets auxquels il ajoute un journal de log. Le moteur peut ensuite utiliser ces informations pour :

  • supporter des transactions ACID,
  • supporter les opérations UPDATE, DELETE et MERGE,
  • assurer un schema enforcement,
  • scaler la lecture des metadata même sur des tables très volumineuses,
  • supporter à la fois les opérations de streaming et de batch sur les mêmes tables,
  • permettre la compaction de fichiers en tâche de fond, pendant qu’un job de streaming tourne, par exemple,
  • permettre de consulter l’historique des données (time travel).

Maintenance de table Delta

Delta Lake conserve l’historique des données et permet de consulter leur état à une date ou une transaction donnée. Cette feature est sans coût pour les opérations en append (on ajoute juste de nouveaux fichiers), cependant les opérations UPDATE/DELETE/MERGE vont conserver un historique des valeurs précédentes.

Delta Lake utilise l’approche Copy on Write, aucune opération CRUD sur une table ne supprime les fichiers sous-jacents, elles ne font qu’en rajouter. C’est un fonctionnement assez classique des systèmes transactionnels où tout nouvel état est d'abord persisté en mode append et ensuite commité dans le journal de transactions, les fichiers historiques sont supprimés de manière asynchrone par une opération de maintenance souvent appelée vacuum. Dans le cas de Delta Lake, c’est à vous de programmer l’opération VACUUM en précisant la profondeur de l’historique à garder (7 jours par défaut), sinon vous allez cumuler les fichiers à l’infini. Sachant que la capacité de stockage de S3 n’est limitée que par la taille de votre porte-monnaie, vous risquez de vous en rendre compte bien tardivement.

Le fait de cumuler les fichiers historiques ne dégrade pas les performances des opérations IO sur la table car le moteur ne lit que les fichiers constituant son état actuel. En revanche, le temps d'exécution de l’opération VACUUM dépend directement du nombre de fichiers à supprimer et à garder. J’ai pu constater également que VACUUM prend au minimum 1’30’’ de temps d'exécution sur EMR même pour une table sans aucune historique à supprimer, peu importe la taille du cluster. C’est à vous de trouver la fréquence optimale de cette opération en tenant compte de son coût EMR, du coût de stockage S3 des fichiers historiques et de la fréquence de mises à jour de votre table.

Un autre problème à résoudre est l'accumulation de très nombreux petits fichiers. Cette fois-ci, ce n’est pas directement lié au caractère transactionnel de Delta Lake mais plutôt à la possibilité de faire de multiples ajouts et mises à jour des données dans la table. Le problème devient critique si la table est alimentée en Streaming. Il est donc nécessaire de compacter les fichiers régulièrement. La version propriétaire de Delta Lake disponible exclusivement sur la plateforme Databricks met à votre disposition les outils nécessaires pour le faire. Vous pouvez programmer l’opération OPTIMIZE ou configurer la compaction et/ou optimisation automatique pour votre table. Dans tout autre environnement, c’est à vous d'implémenter la compaction et de la scheduler dans votre workflow.

Compatibilité avec Athena

Il est possible que vous souhaitiez requêter les tables Delta Lake avec Athena afin de bénéficier des avantages d’un service serverless. Athena est tout à fait adapté pour explorer vos données.

Le format Delta Lake, créé spécialement pour Spark, n’est pas encore supporté nativement par Athena. Néanmoins, il existe une solution de contournement qui consiste à générer le fichier manifest et le référencer dans la requête de création de la table Athena.

Cette approche est bien documentée et paraît assez simple, mais elle vient quand même avec son lot de complexités :

  • Le fichier manifest doit être regénéré à chaque transaction sur la table Delta Lake afin qu’Athena puisse lire la toute dernière version des données,
  • Le schéma des données n’est plus seulement porté par la table Delta Lake, vous avez le cycle de vie de la table Athena à gérer en parallèle afin d’éviter la désynchronisation des schémas.

La première contrainte est résolue dans la version 0.7.0 de Delta Lake qui permet d’activer la génération automatique du fichier manifest. Notez que cette version n’est compatible avec Spark qu’à partir de la version 3.0 qui n’est à son tour disponible que sur la toute dernière release de l’EMR (6.1.0 sortie en Septembre 2020).

Pour pallier la deuxième contrainte, je vous recommande vivement une astuce qui consiste à considérer la table Delta Lake comme l’unique source de vérité et à mettre en place un mécanisme qui recrée la table Athena à partir de son schéma suite à chaque modification de ce dernier. Exemple en Scala :

val deltaTable = DeltaTable.forPath(spark, "s3a://<pathToDeltaTable>")

val athenaQueryDDL = s"""CREATE EXTERNAL TABLE mytable(${deltaTable.toDF.schema.toDDL})
ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.SymlinkTextInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://<pathToDeltaTable>/_symlink_format_manifest/';"""

Il reste à exécuter la requête avec AthenaClient fourni avec AWS Java SDK. Notez que le protocole s3a est préférable pour Spark alors que pour Athena le chemin doit commencer par “s3://”.

Les choses se compliquent lorsque la table Delta Lake est partitionnée. Outre les éventuels problèmes de cohérence décrits dans la doc, vous devez exécuter la requête MSCK REPAIR TABLE mytable aussi souvent que de nouvelles partitions apparaissent afin qu’Athena puisse les lire. Vous allez probablement devoir l'exécuter suite à chaque transaction sur la table Delta Lake si la fréquence d'apparition de nouvelles partitions est imprévisible. Faites attention aux coûts AWS que cela va générer.

La table Athena doit être à son tour créée comme une table partitionnée. En plus du schéma des colonnes de la table, nous avons besoin de son schéma de partitionnement. L’API Delta Lake permet de récupérer le schéma de partitionnement mais cela n’est documenté nulle part. Les colonnes du schéma de partitionnement ne doivent pas être répétées dans le schéma des colonnes dans la requête Athena, sinon la requête est rejetée avec une erreur de syntaxe. En tenant compte de toutes ces contraintes et dans le souci de rendre la création de la table Athena totalement générique, mon code évolue pour prendre la forme suivante :

val deltaTablePath = "<pathToDeltaTable>"

val tableName = "mytable"

val deltaTable = DeltaTable.forPath(spark, s"s3a://$deltaTablePath")

val deltaLog = DeltaLog.forTable(spark, s"s3a://$deltaTablePath")

val partitionSchema = deltaLog.snapshot.metadata.partitionSchema

val schema = deltaTable.toDF.schema

val columns = if (partitionSchema.isEmpty)
   schema.toDDL
 else
   new StructType(schema.filterNot(partitionSchema.fields.contains(_)).toArray).toDDL

val createAthenaTableQuery = s"""CREATE EXTERNAL TABLE $tableName ($columns)
${if (partitionSchema.isEmpty) "" else s"PARTITIONED BY (${partitionSchema.toDDL})"}
ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.SymlinkTextInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://$tablePath/_symlink_format_manifest/';"""

Bug EMR

L'opération MERGE qui permet de combiner les instructions INSERT, UPDATE et DELETE dans une seule requête est une des fonctionnalités les plus intéressantes de la librairie Delta Lake. C’est bien cette fonctionnalité qui n’est pas utilisable sur les versions 5.29.0 et 6.0.0 d’EMR à cause d’un bug connu. Le fait que ce bug soit présent sur deux versions majeures d’EMR témoigne du faible intérêt que porte AWS pour Delta Lake, je vous laisse deviner pour quelle raison...

Conclusion

Vaut-il finalement le coup d’utiliser Delta Lake dans l’environnement AWS ? A mon avis, du moment où votre workflow est un peu plus complexe qu’un simple batch en mode append ou overwrite, vous avez tout intérêt à adopter cette techno. Les bénéfices qu’elle apporte couvrent largement les frais de ce mariage forcé. Enfin, pour n’en tirer que des bénéfices il faut s’orienter vers la plateforme Databricks disponible sur AWS.


Retour sur le premier JHipster Code

$
0
0
Retour sur le premier JHipster Code

Le lundi 14 septembre s’est tenu à Bordeaux le premier JHipster Code, conférence autour du projet JHipster. J’ai pu assister à cette journée qui m’intéressait particulièrement en tant que membre de la “Practice JHipster” pour Ippon Technologies et du fait que Ippon Technologies était le sponsor principal de cette conférence. Les deux autres sponsors de cette conférence étaient Heroku et Microsoft.

Cette journée était différente par rapport aux conférences habituelles (comme par exemple la JHipster Conf de l’an dernier https://jhipster-conf.github.io/) durant lesquelles on assiste à des présentations par des speakers sur différents sujets. En effet, le but de cette journée était avant tout de coder pour faire avancer le projet JHipster https://github.com/jhipster/generator-jhipster et former de nouveaux développeurs à la contribution sur le projet.

Nous étions donc 40 codeurs (venant majoritairement de France mais aussi des Pays-Bas, d’Italie et des UAE) réunis ce lundi. La journée commença par une Keynote d’ouverture réalisée par les trois leaders du projet Julien Dubois, Pascal Grimaud et Deepu K Sasidharan. Nous avons pu notamment assister à une présentation des dernières nouveautés ajoutées au projet :

  • JHipster Control Center
  • Cypress
  • JDL Studio V2

Retour sur le premier JHipster Code

Suite à cette Keynote, nous étions divisés en plusieurs groupes de travail pour avancer sur différents sujets. Le groupe le plus important était celui pour apprendre les bases de la contribution sur le projet JHipster. Le but était de pouvoir réaliser une première contribution durant la journée en ouvrant une Pull Request sur des tickets, que Pascal et moi-même avions préparés, assez simples (comme des bugs graphiques, du code généré pas nécessaire ou encore des bugs remontés par Sonar) mais importants et permettant de rentrer plus facilement dans le projet. Ce groupe était encadré par Deepu et Pascal.

D’autres sujets ont pu être avancés (par différents groupes) au cours de cette journée comme :

  • Améliorations des API REST avec l’ajout du PATCH sur les entités
  • Ajout du support de Terraform à JHipster
  • Ajout du support de OAuth2 à JHipster Quarkus
  • Amélioration de l’expérience pour les mises à jour de JHipster
  • Migration de Docker Compose en version 3

De plus, pendant toute la journée, Colin Damon était en live sur la chaîne Twitch d’Ippon Technologies https://www.twitch.tv/ippontech pour coder une application utilisant une architecture hexagonale au sein d’une application JHipster. Le replay et les interviews réalisées dans la journée seront disponibles sur la chaîne Youtube si cela vous intéresse : https://www.youtube.com/channel/UCXvQBjBXq4geZbyXVtZvQYg/

Dans l’après midi, Matt Raible a réalisé à distance une présentation sur la sécurité au sein des applications JHipster. Le replay est disponible sur YouTube https://www.youtube.com/watch?time_continue=3&v=Ze0rt6JxTfE&feature=emb_logo&ab_channel=JulienDubois

Conclusion


Les retours des participants ont été très positifs sur cet évènement assez différent de ce à quoi on est habitué sur les grandes conférences. Personnellement j’ai beaucoup apprécié le fait de pouvoir travailler physiquement sur une journée avec des gens avec qui j’ai l’habitude de travailler virtuellement, pour terminer notamment mon sujet sur la migration de Docker Compose. Cette journée a été très profitable pour le projet avec une quarantaine de Pull Requests ouvertes. Si vous voulez en découvrir plus sur cette journée, cette page est à votre disposition : https://www.jhipster.tech/jhipster-code/

Diagramme de contexte VS Hexagone

$
0
0
Diagramme de contexte VS Hexagone

L'objectif de cet article est de nous intéresser au lien entre une représentation UML de moins en moins utilisée et pourtant essentielle dans la représentation d’une architecture logicielle :  le diagramme de contexte, et l’architecture “Ports & Adapters” plus connue sous le nom d’architecture hexagonale.

Diagramme de contexte

Le diagramme de contexte est l’un des premiers diagrammes pouvant être réalisé afin de décrire à haut niveau le futur système en mode boîte noire. Il permet de se concentrer sur l’identification des différents acteurs interagissant avec lui. Il est aussi possible de représenter les interactions entre acteurs si cela semble pertinent pour la compréhension globale du système. Lors de sa réalisation on ne parle ni d’implémentation, ni de solution, mais seulement de son contexte d’utilisation.

Diagramme de contexte VS Hexagone

fig1. Diagramme de contexte

Au centre, nous avons le système dans son ensemble et gravitant autour on retrouve les acteurs. Ils ne sont pas, comme peut le laisser penser leur représentation, forcément des acteurs physiques. Ils peuvent aussi être d’autres systèmes, des instruments etc…

Ce qu’il faut noter c’est le sens des flèches :

  • Lorsqu’elles pointent vers le système on parlera des acteurs primaires :

Ils sont à l’origine de nos futurs “Use Cases”.

  • Lorsqu’elles pointent vers l’extérieur on parlera des acteurs secondaires :

Il s’agit des interactions avec l’extérieur du système afin de, par exemple, récupérer de l’information, ou pour réaliser des notifications.

Ce que l’on va comparer avec l'architecture, c’est justement le lien avec ces deux différents types d’acteurs.

Architecture hexagonale

Le but de cette partie n’est ni de rentrer dans les détails de l’architecture, ni d’expliquer dans quels cas on peut l’utiliser, mais d’effectuer le survol nécessaire à la compréhension du lien avec les acteurs primaires et secondaires.

L’architecture hexagonale est une architecture présentée par Alistair Cockburn dans son blog sous la forme d’un hexagone.

Pour l'anecdote, la forme de l’hexagone est un choix arbitraire d'Alistair Cockburn qui souhaitait obtenir une symétrie dans son architecture et le carré ne lui convenait pas car cette représentation lui laissait l’impression de devoir se limiter à une architecture en couches traditionnelle. Au final l’architecture n’est pas complètement symétrique d’où le nom plus approprié d’architecture “Ports & Adapters”.

Diagramme de contexte VS Hexagone

fig2. Architecture Hexagonale

L’architecture “Ports & Adapters” se base comme son nom l’indique sur des “Ports”. Ce sont des portes d’entrées de l’application utilisées par des “Adapters” dits “Driving Adapters”, et des portes de sorties implémentées par des ”Adapters” dits “Driven Adapters”.

Dans le cadre de cet article, c’est l’utilisation de l’hexagone comme moyen de modélisation qui nous intéresse. Il permet de se concentrer sur les “Use Cases” et les acteurs sans rentrer dans les détails de l’implémentation.

Modélise-t-on les mêmes acteurs avec l’hexagone qu’avec le diagramme de contexte ?

Modélisations équivalentes ?

A première vue, on peut remarquer que d’une part les acteurs qui interagissent avec les “Driving Adapters” sont dans le monde UML des acteurs dits primaires, et sont à l’origine des “Use Cases”. D’autre part, les acteurs qui sont appelés depuis les “Driven Adapters” sont dans le monde UML des acteurs dits secondaires car ce sont des appels vers l’extérieur.

On peut alors se demander si les deux représentations sont équivalentes?

Si nous prenons un peu de recul, on remarque que ce qui se trouve au centre des deux modélisations n’est pas du même niveau de granularité et d’abstraction.

En effet, le système entier est représenté par le diagramme de contexte qui utilise le plus haut niveau d’abstraction. Il peut être composé de plusieurs applications alors que dans l’hexagone on retrouve une seule application.

Dès lors, les acteurs dans l’architecture “Ports & Adapters” peuvent ne pas se trouver dans le diagramme de contexte et peuvent se trouver dans les détails du système.

Diagramme de contexte VS Hexagone

fig3. L’Hexagone se cache dans les détails

La figure ci-dessus en est l’illustration avec des acteurs qui émergent quand on zoome dans le système :

Diagramme de contexte VS Hexagone

On notera que dans le cas particulier où le système est composé d’un seul composant applicatif alors les acteurs des deux modélisations coïncident.

Les deux types de modélisation ne représentent pas le même niveau d’abstraction et ne possèdent pas, dans la majorité des cas, les mêmes acteurs. En revanche, elles se complètent en intervenant à deux moments différents durant le processus d’architecture logicielle.

Diagramme de contexte VS Hexagone

fig4. Modélisation possible durant le processus d’architecture logicielle

Le diagramme de contexte intervient au début et reste un incontournable permettant de saisir à haut niveau, et en une représentation, l’ensemble des différents acteurs interagissant avec un système. La modélisation sous la forme d’hexagone peut intervenir dans la suite du processus avec un niveau plus détaillé (use cases). Ce moment peut être lors de la réalisation de diagrammes de composants, lorsque la décomposition en briques applicatives est déjà réalisée.

Conclusion

Le diagramme de contexte est une modélisation permettant de mettre en avant les acteurs à haut niveau du système. Il s’agit d’une représentation UML compréhensible pouvant être construite et validée en collaboration avec les PMs et les POs du projet. Le diagramme de contexte permet de s’assurer que le contexte du système a bien été saisi avant d’aller plus de l’avant.

La représentation sous forme d’hexagone est une modélisation d’une application faisant partie d’un système plus large. Cette modélisation s’adresse à un public plus technique et permet dans le cas où l’architecture “Ports & Adapters” est utilisée de réduire ce qu’on appelle le “model-code gap” (Just Enough Architecture - George Fairbanks) en faisant apparaître les “ports”, les “adapters” et les “use cases” à implémenter directement dans le code.

Les livres qui ont changé ma vision du développement

$
0
0
Les livres qui ont changé ma vision du développement

J'ai appris le développement de manière autodidacte avec un livre : Créez votre site web - Daniel Ichbiah. Pourtant, quand j'ai commencé à travailler je ne comprenais pas l'intérêt des livres parlant de développement : "Il y a tout sur le net et tout y est plus à jour !".

Je pense que j'étais frustré de n'avoir découvert l'existence des forums que des années après cette première lecture ! A ce moment-là, j'avais un forfait 50h en 56k : la toile n'était même pas l'ombre du média omniprésent qu'elle est devenue.

Bien des années plus tard donc, un collègue m'a prêté Clean Code - Robert C. Martin (dans sa version en français). J'ai lu ce livre, je n'ai clairement pas tout compris mais je l'ai trouvé intéressant, vraiment !

En fait, je crois que c'est à ce moment-là que j'ai compris que les livres sur le développement pouvaient être passionnants et intemporels. Il me faudra encore quelques années pour m'acheter mes premiers livres. Voici un échantillon de ma bibliothèque actuelle :

Les livres qui ont changé ma vision du développement

Il n'y a pas tout (certains sont en prêt), et je n'ai pas encore tout lu mais je vais vous parler de certains d'entre eux : ceux qui m'ont marqué !

Il n'y a pas de classement particulier, j'ai juste mis ça dans l'ordre qui me passait par la tête...

Design Patterns: Elements of Reusable Object-Oriented Software

Il me semble que c'est le livre que j'ai lu après Clean Code (des années après). Ce livre est une relique du passé : Octobre 1994, dans le monde du développement c'est le paléolithique. Et pourtant, le célèbre Gang Of Four (Erich Gamma, Richard Helm, Ralph Johnson et John Vlissides) signe ici un ouvrage intemporel.

En décrivant, et en classant, les designs patterns du développement orienté objet, ils réussissent un tour de force qui marquera profondément (et durablement) l'industrie du logiciel.

Ce n'est pas une lecture facile, c'est un recueil, une référence que l'on peut sortir en cas de doute. Ce livre m'a permis de comprendre ces fameux patterns qui m'ont été enseignés mais je n'étais clairement pas prêt au moment où ça a été fait !

The Software Craftsman: Professionalism, Pragmatism, Pride

J'ai lu ce livre tout de suite après The Pragmatic Programmer: From Journeyman to Master (1ere édition) qui m'avait un peu déçu car j'avais trouvé les conseils basiques et je cherchais un livre plus "avancé", c'est le cas de celui-ci !

Le parcours de son auteur, Sandro Mancuso, est très inspirant et il le raconte très bien dans ce livre. A elle seule, la préface justifie l'achat et la lecture !

En à peine plus de 200 pages des sujets aussi éloignés que savoir dire non ou comment recruter un Software Crafter sont abordés. Si vous vous intéressez à la qualité du code : foncez, c'est passionnant !

The Clean Coder: A Code of Conduct for Professional Programmers

Il me semble que c'est le second livre de Robert "Uncle Bob" Martin que j'ai lu et donc, le premier en anglais. J'ai alors pu découvrir un style d'écriture et un sens de la formule que j'aime beaucoup.

Comme dans tous ces livres, tout n'est pas à garder et je sais que certaines personnes ont des réactions épidermiques à certains passages. Pour ma part, je les ignore simplement et je me concentre sur les anecdotes et les conseils en les prenant comme tels et pas comme des ordres.

J'ai beaucoup aimé ce livre très facile à lire et qui décrit bien des éléments d'attitude nécessaires pour faire du code de meilleure qualité. Si je ne dois recommander qu'un bouquin sur le Software Craftsmanship je recommande souvent celui-ci.

Building Microservices

Il y a quelques années, on ne mettait pas des microservices partout, pour tout. A cette époque, dans mon entreprise, on s'est vraiment posé la question de l'architecture à adopter pour nos projets.

Sans tout comprendre, nous avons fait le choix des microservices. A ce moment-là, je me suis beaucoup formé sur cette architecture mais j'avais du mal à tout remettre dans l'ordre (il n'y avait pas autant de ressources sur le sujet qu'aujourd'hui). Quand je commençais à avoir les idées claires, j'ai lu ce livre et j'ai été relativement dépité !

Si seulement j'avais lu les conseils de Sam Newman avant, j'aurais gagné un temps fou ! Ce livre explique clairement comment mettre en place ces architectures hautement distribuées. Il est dense, très dense mais complet et clair ! Je n'ai lu que la première version mais, pour moi, c'est un must read si vous voulez mettre des microservices en production.

The Phoenix Project: A Novel About IT, DevOps, and Helping Your Business Win

Ce livre est particulier, déjà parce qu'il est écrit comme un roman, on y trouve des personnages et on suit leur quotidien. Il est aussi particulier parce que ses auteurs Gene Kim, Kevin Behr et George Spafford ont voulu un livre accessible : il est donc moins cher que la moyenne des livres techniques.

Il est bien écrit, facile à lire et on y apprend beaucoup de choses sur le DevOps ! Un bémol cependant : il y a beaucoup de personnages qui représentent chacun un "poste" donné. Personnellement, pour ne pas me perdre lors de la lecture, j'ai fais un "mapping" de ces personnages vers des collègues pour savoir qui représentait quelle "caricature".

Release It! Design and Deploy Production-Ready Software

Vous avez peut-être déjà entendu le fameux : "ça marche sur mon poste". Phrase élevée au rang de blague dans certaines équipes. Cette blague, un peu triste, est un vrai problème dans le monde du développement logiciel.

Dans ce livre, Michael Nygard nous donne les clés pour penser nos applications de manière à ce qu'elles fonctionnent vraiment en production et non plus seulement sur nos postes avec un seul utilisateur.

Au travers d'exemples de vraies productions qui ont bien crashé, il nous donne les antipatterns à éviter et les patterns à mettre en place. J'ai trouvé dans ce livre quantité de conseils qui servent les solutions que je développe depuis.

Je n'ai lu que la première édition mais je suis persuadé que la seconde est très bien, si vous voulez rendre vos applications anti-fragiles : foncez !

Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation

Ce livre est sorti en 2011, c'est... lointain, et pourtant Jez Humble et David Farley décrivent clairement des approches permettant de faire réellement ce que beaucoup d'entreprises n'osent même pas envisager !

Bien sûr, les outils présentés sont largement dépassés mais les approches sont encore tout à fait valables.

Pour avoir mis en place du déploiement continu plusieurs fois depuis cette lecture je vous assure que c'est une manière de faire qui change vraiment la capacité à délivrer de la valeur. Le déploiement continu (oui, jusqu'en production, sans aucune action manuelle) augmente aussi drastiquement la qualité des solutions. L'essayer c'est l'adopter, lisez ce livre, mettez-le en place et faites-vous votre propre avis.

Effective Java

Je sous-entendais en introduction que je n'aimais pas trop les livres parlant d'une technologie en particulier. Celui-ci fait exception !

Ce recueil d'excellents conseils sur Java, ordonnés et présentés de main de maître par Joshua Bloch m'a permis de comprendre beaucoup de choses sur le langage. Il m'a aussi donné de très bonnes idées de designs que j'utilise au quotidien.

Si vous cherchez un livre pour apprendre des points spécifiques et très utiles de Java foncez. Je n'ai lu que la seconde édition mais j'ai de très bon retours sur la troisième !

Clean Agile: Back to Basics

Un livre de Uncle Bob plus court que ce à quoi il nous a habitués, ce qu'il explique dès les premières pages par le fait que le développement agile de logiciels est quelque chose de simple. Il n'y a donc pas besoin d'un gros bouquin pour en parler !

J'ai lu ce livre peu de temps après sa parution et, à ce moment-là, je me posais sérieusement des questions sur "l'agile". En tout cas cette méthodologie prônée par certains "agilistes".

J'ai vraiment apprécié lire l'histoire de la création du manifest mais j'ai surtout aimé lire que les doutes que j'avais sur ce qu'est devenu ce mouvement sont largement partagés par les personnes qui en sont à l'origine !

Ce "rappel" de comment tout ça a été imaginé, des problèmes que cela doit résoudre, des raisons d'être des différents éléments fait beaucoup de bien. C'est un petit livre de moins de 200 pages, avec le très bon style d'écriture de son auteur, il se lit sur un weekend pluvieux sans problème !

Si vous pensez que "l'agile" que vous pratiquez est cassé, que les promesses ne sont pas tenues je ne peux que vous recommander cette lecture.

Domain-Driven Design: Tackling Complexity in the Heart of Software

Ah, voilà un monument de la littérature horrifique, un livre qui a terrorisé pléthore de développeurs. Il a même un surnom qui fait peur : The Big Blue Book.

Et pourtant, Eric Evans a réussi à formaliser, nommer et organiser le bon sens de développement des premières générations de codeurs. Ce livre a lancé un mouvement, bien d'autres ont suivi, le DDD est maintenant quelque chose !

Cette approche a pris de l'ampleur avec l'avènement des microservices qui doivent être organisés autours des Bounded Contexts : une organisation définie par Evans.

Il fait peur et ce n'est pas totalement injustifié (surtout quand on attaque la partie 3) mais c'est une lecture très loin d'être insurmontable. Un développeur expérimenté pourra en tirer beaucoup. Je ne le conseillerais cependant pas aux développeurs débutants qui ne sont pas encore à l'aise avec les technologies.

Sa lecture seule ne permet pas de "faire du DDD" (ce qui ne veut pas dire grand chose quoi qu'il arrive) mais elle vous donnera les éléments pour essayer et pour aller échanger dans les groupes DDD que l'on trouve maintenant un peu partout dans le monde.

Domain-Driven Design Distilled

J'étais un peu "fâché" avec Vaughn Vernon, enfin, plutôt déçu de son Big Red Book. Je n'avais absolument pas prévu de lire celui-ci mais on m'en a dit du bien alors je l'ai acheté, et je l'ai lu en quelques jours !

J'y ai trouvé beaucoup de choses ; notamment une description claire de l'architecture que j'utilise maintenant au quotidien (un exemple sur PadBowl).

Je pense aussi qu'il s'agit d'un très bon livre à mettre dans les mains de personnes qui ne s'intéressent pas plus que ça au DDD mais qui ont quand même une curiosité. Plus qu'une très belle introduction, il donnera des armes pour se lancer sérieusement tout en venant compléter les ouvrages plus anciens (et plus conséquents).

Et alors ?

La question est légitime. Je vous ai donné une vision personnelle, basée sur mes lectures, mes choix, ma compréhension, mes ressentis à un instant donné.

Peut-être n'ai-je pas parlé du livre qui a changé votre carrière, je ne l'ai peut-être pas lu ou je l'ai peut-être lu au mauvais moment (trop tôt ou trop tard).

Et alors... rien ! Cet article n'est que mon avis. Vous pouvez choisir ou non de le prendre en compte mais l'important c'est que vous lisiez et que vous vous fassiez votre propre liste de livres marquants !

Découvrez DBT

$
0
0
Découvrez DBT

DBT fait partie de ces outils qui peuvent faire gagner beaucoup de temps sur un projet Data. Nous vous proposons de le découvrir dans cet article.

Introduction

Data build tool aka DBT est un excellent outil pour orchestrer des transformations de données en SQL.

C’est un outil de la famille des ELT (Extract, Load and Transform). Mais attention, DBT ne s’occupe que du T, c’est-à-dire que DBT exécute des requêtes SQL dans un Data Warehouse.

Il faut que l’extraction des données et le chargement du Data Warehouse aient été faits auparavant à l’aide d’autres outils.

DBT a été créé par la société Fishtown Analytics qui le propose en version open-source mais aussi en version hébergée avec DBT cloud.

La documentation est particulièrement bien faite : https://docs.getDBT.com/.

Vous avez aussi accès à un Slack ou un Discourse pour interagir directement avec la communauté (liens sur la page doc).

En quoi consiste un projet DBT ?

Un projet DBT est composé d’un fichier de configuration et de modèles.

Chaque modèle est un fichier .sql qui contient une seule requête SELECT.

Chaque requête peut se baser sur :

  • des tables ou des vues existantes,
  • une CTE (Common Table Expression) avec le mot clé “with”,
  • un autre modèle avec le mot clé “ref”.

C’est là que la magie opère : le moteur de DBT va analyser tous vos modèles pour détecter les dépendances et jouer les requêtes dans le bon ordre.

Grâce à DBT, on pourra facilement automatiser des tests unitaires pour vérifier la cohérence d’un schéma ou tester des résultats de requête comme expliqué ici.

Vous disposez de 4 assertions pour tester les résultats d’une requête, qui couvrent les domaines suivants:

  • l’unicité,
  • la non nullité,
  • la plage de valeurs acceptée,
  • les relations entre les tables (est-ce qu’une clé étrangère pointe sur une clé primaire existante).

Pour aller plus loin, on peut coder soit même ses data tests et utiliser les seeds pour injecter des données.

On trouve aussi des macros, qui sont des requêtes qu’on peut étendre avec la librairie Jinja qui permet d’utiliser des templates pour ajouter des boucles et des conditions au SQL natif.

Enfin on peut avoir la bonne idée de documenter son projet mais aussi stocker des requêtes qui ne seront pas jouées à chaque exécution (appelée analysis).

Voici un exemple d’arborescence de projet :

Découvrez DBT

Pourquoi DBT est-il si cool ?

Pour commencer, utiliser l’approche ELT quand il s’agit de manipuler des données structurées ou semi-structurées est déjà un bon côté de DBT. Dans cette approche, on n’a pas besoin de gérer des noeuds de calcul indépendamment des nœuds de stockage et on utilise la puissance et la popularité du langage SQL pour transformer les données.

DBT détecte les dépendances entre les modèles par introspection pour construire et exécuter un DAG (Direct Acyclic Graph).

Par exemple la requête suivante du modèle "orders.sql" :

select 
   orders.id, 
   orders.status, 
   sum(payments.amount) as total_amount
   from {{ ref('base_orders') }} as ordersleft 
   join {{ ref('base_payments') }} as payments on payments.order_id = orders.id

conduit au graphe suivant dans lequel on voit clairement le “data lineage” :

Découvrez DBT

DBT va déterminer tout seul qu’il faut d’abord exécuter les modèles “base_orders” et “base_payments” avant le modèle “orders”.

On peut choisir comment les résultats des requêtes seront persistés : vue (non matérialisée), table, CTE, table incrémentale ou encore éphémère.

Les moteurs SQL supportés sont Redshift, BigQuery, Snowflake, PostgreSQL, Presto et Spark. Ces deux derniers étant en support partiel.

Dans quelles situations DBT est-il une bonne solution ?

Plusieurs critères sont à considérer avant d’utiliser DBT.

D’une part,  il faut pouvoir se contenter du SQL pour toutes les transformations de données. Cela veut dire que vous ne devrez pas utiliser de librairies comme par exemple des librairies de Machine Learning. Et il sera préférable que vos analystes métiers soient capables de lire du code SQL pour collaborer avec vous.

D’autre part, il faut que la chaîne d’ingestion vers le data warehouse soit déjà en place. Comme vous l’aurez compris, DBT ne fournit pas de connecteurs pour charger les données.

Il existe des outils très pratiques pour réaliser cette alimentation comme par exemple Matillion Data Loader présenté sur le blog Ippon dans cet article, Fivetran  ou Hevo.

Si ces deux conditions sont réunies, comme c’est souvent possible pour l’informatique de gestion en général et la B.I. en particulier qui ne manipulent que des données structurées, DBT est un excellent outil à avoir dans sa Data Platform.

Comment exécuter un workflow DBT en production ?

La solution la plus simple, c’est DBT cloud qui vient avec son propre IDE pour développer, planifier et administrer ses projets DBT.

Pour un développeur seul, c’est gratuit si on se contente d’avoir un seul job en exécution concurrent. Ce sera limitant dès que vous aurez besoin de paralléliser vos requêtes mais ça permet déjà de bien avancer.

La version Team est facturée 50$ par développeur par mois et la version Enterprise est sur devis. La page avec les tarifs est ici : https://www.getDBT.com/pricing/

On peut utiliser DBT en ligne de commandes et dans ce cas orchestrer ses jobs avec son orchestrateur préféré. Mais on peut aussi intégrer DBT avec GitLab CI et dans ce cas ce sont les GitLab runners qui vont exécuter DBT.

Enfin, si vous avez déjà un workflow de jobs data en place comme Apache Airflow ou Prefect, dont on parle dans cet article sur le blog Ippon, vous pouvez les utiliser pour lancer vos flux DBT.

La preuve par l’exemple

Je ne vais pas réécrire un tutoriel d’utilisation de DBT. Par contre, je peux vous orienter vers quelques-uns qui sont excellents :

Pour commencer à utiliser DBT CLI, rien de tel que le tutoriel proposé sur le site de DBT.

Pour comprendre les bonnes pratiques de modélisation, ce post discourse est à lire absolument.

L'équipe Gitlab utilise DBT et donne plein de bons conseils ici.

Les concurrents de DBT

Avec la montée en puissance des Data Warehouses dans le cloud, Snowflake, Big Query, Amazon Redshift et Azure Synapse pour ne citer qu’eux, l’approche ELT a le vent en poupe.

De plus en plus d’outils se positionnent sur ce créneau et ce n’est pas toujours simple de s’y retrouver.

Voici des produits que nous vous recommandons :

  • Matillion : outil graphique pour rapidement construire des flux de chargement et de transformation de données. Simple et efficace.
  • Fivetran : c’est plutôt un data loader, mais il permet de coder des transformations en SQL et en DBT !
  • dataform : un environnement complet de développement SQLX qui est une extension du SQL avec des metadata et du test unitaire.

Conclusion

DBT se positionne efficacement sur la partie Transformation de l’approche ELT. C’est un excellent outil pour automatiser vos traitements batchs sur un Data Warehouse moderne.

La génération automatique de DAG et la possibilité de coder des tests unitaires sont des améliorations bienvenues au langage SQL.

À moindre coût, vous pouvez démarrer le développement de votre Data Platform avec DBT si tant est que le langage SQL vous convienne pour exprimer votre logique métier. DBT cloud permettra de booster la productivité de votre équipe.

Comme toujours, rien ne vaut un essai pour se forger une opinion !

Apache Arrow : étincelant dans l’ombre

$
0
0
Apache Arrow : étincelant dans l’ombre

Certains utilisateurs de PySpark se reconnaîtront dans ce qui suit :
- Mon algorithme est super lent… Pourtant mon code semble bon !
- Fais ça pour voir : spark.conf.set("spark.sql.execution.arrow.pyspark.enabled", "true")
… Quelques minutes après...
- Ah ouais tu m’as sauvé la vie, c’est plus rapide !
(Sauvé la vie… J’en rajoute un peu !)

Quand on regarde la quantité de librairies open-source, on y trouve de tout (et c’est ça qu’on vient chercher généralement), mais il est très rare de trouver, caché, des librairies impactant autant la manière de penser le “compute”.

Une de ces librairies dont je voudrais vous parler aujourd’hui est Apache Arrow.
On retrouve Arrow dans des frameworks comme Apache Spark, Apache Flink, Apache Parquet ou encore Pandas. C’est une petite librairie présente dans l’ombre des frameworks les plus utilisés dans le domaine de la Data.

Il est temps de la mettre en lumière !

Posons les bases

Apache Arrow est un ensemble de librairies et d’outils pour le traitement de données en mémoire (In-memory). Arrow peut être vu comme une liste de spécifications pour le transfert de données en mémoire. Mais...
Arrow n’est pas un exécutable ou un système installable, ce n’est pas une solution de cache mémoire ou d’IMDB (In Memory DataBase).

La librairie a été créée dans un but analytique et non dans la gestion de données temps réel ou dans un environnement transactionnel.

Apache Arrow propose un format de données en mémoire multilangage, multiplateforme et en colonnes. Il propose également des frameworks IPC (Inter-Process Communication) et RPC (Remote Procedure Call) pour l’échange de données entre processus et entre nœuds (d’où sa présence dans l’écosystème du Big Data et la gestion des processus distribués).

“In-memory computing”

Regardons comment cela fonctionnait avant l’arrivée d’Apache Arrow :

Apache Arrow : étincelant dans l’ombre

Prenons l’exemple de Spark s’appuyant sur des données stockées au format Parquet.
Nous devons lire et désérialiser la donnée stockée au format Parquet nous obligeant à stocker entièrement la donnée en mémoire.

  1. Nous allons d’abord lire la donnée dans un buffer en mémoire.
  2. Nous allons convertir la donnée du format Parquet vers la représentation de notre donnée dans notre langage de programmation (nécessaire car la représentation d’un nombre en Parquet, par exemple, n’est pas la même que la représentation de ce même nombre en Python).

Quelle perte de performance et de stabilité pour notre algorithme ! Pourquoi ?

  1. Nous devons copier et convertir la donnée avant de faire une quelconque opération dessus.
  2. La donnée doit tenir entièrement en mémoire (que faire si j’ai 8Gb de mémoire de libre et 10Gb de donnée en entrée de mon code Spark ?).

Voyons maintenant comment cela fonctionne avec Apache Arrow :

Apache Arrow : étincelant dans l’ombre

Au lieu de copier et convertir la donnée, Arrow sait comment lire et opérer la donnée sérialisée. Pour que cela fonctionne, la communauté Arrow a défini un nouveau format de données qui permet d'interagir directement sur la donnée sérialisée. Ce format de données peut être lu directement depuis le disque (stocké sous forme de fichiers) donc nul besoin de le stocker en mémoire. Bien sûr certaines parties de la donnée vont passer en RAM mais il n’y a plus besoin d’y stocker la totalité de cette donnée grâce à un système de memory-mapping permettant de ne remonter en mémoire que la donnée à traiter à l’instant T.

Arrow élimine le besoin de sérialisation car les données sont représentées par les mêmes octets sur chaque plateforme et langage de programmation. Ce format commun permet le transfert de données sans copie dans les systèmes Big Data, afin de maximiser les performances du transfert de données.

Format de stockage en colonnes

Le cœur d’Arrow est son format de stockage colonne.

Dans la plupart des systèmes de stockage, la donnée est stockée row-wise. C'est-à-dire que la donnée sera stockée sur disque ligne par ligne. Maintenant imaginons que l’on souhaite agréger la donnée, il faudra alors lire toutes les lignes, entièrement, une à une et extraire la donnée à agréger.
Un exemple parlant : imaginons que nous avons un site de e-commerce et qu’on travaille sur la gestion du panier. Chaque produit ajouté au panier correspond à une ligne dans notre système et une des colonnes est le “prix”. L’agrégation peut consister alors à sommer toutes les valeurs de la colonne “prix”. Il faudra donc lire TOUTES les lignes, filtrer la valeur dans la colonne “prix” et sommer ces valeurs.

Dans un domaine BI/BA (Business Intelligence/Business Analysis), les agrégations sont le cœur du métier. Le stockage row-wise est une perte de temps, d’espace de stockage, de performance.

Si on pense maintenant en format colonne où chaque colonne et ses valeurs sont stockées au même endroit, nous n’aurons besoin d’accéder qu’à la colonne à agréger pour faire notre calcul. Quel gain pour notre cas d’utilisation en BI/BA !

Le stockage colonne nous permet de gagner en efficacité pour accéder à une colonne mais permet également de profiter au maximum des avantages des architectures de CPU moderne quand on parle de caching, pipelining et d’instructions Single Instruction Multiple Data (SIMD) au niveau du processeur. Je vous laisse lire l’excellent article de Dremio sur le sujet et leur benchmark.
Qu’est ce qu’on gagne en passant sur un format colonne ? De la rapidité !

Si on souhaite mettre du visuel sur les mots voilà la représentation d’Arrow en mémoire :

Apache Arrow : étincelant dans l’ombre

Attention, ce n’est pas Arrow qui rattrapera les médiocres performances d’une application mal développée mais elle permet de faire, par son gain en performance, des choses qu’on ne pouvait pas faire avant.

Langages supportés

Voilà la liste des langages supportés par Apache Arrow :

  • C++
  • C#
  • Go
  • Java
  • JavaScript
  • Rust
  • Python (via la librairie C++)
  • Ruby (via la librairie C++)
  • R (via la librairie C++)
  • MATLAB (via la librairie C++)

Mais Apache Arrow n’est pas juste un nouveau format de fichier plus performant !

IPC (Inter-Process Communication)

La librairie Arrow offre également une interface et des moyens de communiquer entre les processus et les nœuds. Ce qui veut dire que des processus, un processus Python et un processus Java, peuvent efficacement s’échanger de la donnée sans la copier localement. Au niveau des nœuds d’un réseau, également, nous profitons d’optimisations de par la nature d’Arrow. En effet, au travers du réseau ne transitera que les colonnes nécessaires au traitement lancé sur le nœud distant.

Rappelons par ailleurs, que nous n’aurons toujours pas besoin de désérialiser la donnée, à aucun moment, vu que Arrow sait l’opérer sérialisée.

A en croire Dremio, PySpark (algorithme Python sur un moteur Spark tournant sur une JVM) a profité pleinement d’Arrow : “IBM measured a 53x speedup in data processing by Python and Spark after adding support for Arrow in PySpark”.

RPC (Remote Procedure Call)

Depuis fin 2019, la communauté Arrow porte un projet nommé Flight sous forme de librairie. Arrow Flight est un framework de développement permettant d’implémenter des services d’échange bi-directionnel de flux de données, basé sur Arrow. C’est un protocole de transfert de données optimisé pour l’analytique.

Dans un environnement distribué, Arrow Flight offre la puissance d’Arrow en exposant la donnée via des endpoints (au lieu de travailler avec la donnée localisée).
Arrow Flight tire parti du streaming bi-directionnel de gRPC (basée sur la diffusion HTTP / 2) pour permettre aux clients et aux serveurs de s’envoyer simultanément des données et des métadonnées pendant que les demandes sont traitées.

Cette parallélisation des moyens d’interactions entre services permettra, entre autres, d’accélérer les transferts réseau.

Dremio (ce n’est pas la première fois que je parle d’eux, un jour un article sortira sur leur excellente solution de Data Lake Engine) a observé lors du benchmark de leur connecteur Apache Arrow qu’en utilisant Arrow ils ont amélioré drastiquement (20-50 fois supérieur) les performances réseau comparé à une connection ODBC via TCP.

Qu’est ce qu’il manque alors à Arrow ?

La compréhension de comment fonctionne Arrow peut être primordiale dans notre quête de performances. Beaucoup de projets, comme Pandas, l’ont compris et ont misé dessus. Arrow est devenu un standard dans l’industrie de la donnée en mémoire.

Ce qu’il manque à Arrow est un moteur de requêtage natif permettant de manipuler simplement les données stockées au format Arrow. Mais quelque chose me dit que la communauté Arrow, ultra active et réactive, est déjà en train de travailler dessus…

OKD4 sur AWS, le test : Partie 1

$
0
0
OKD4 sur AWS, le test : Partie 1

OpenShift 4 est déjà sorti depuis mai 2019, mais la version opensource et gratuite OKD 4 se faisait attendre. En effet, nous étions sans nouvelle version d’OKD depuis février 2019. Plusieurs doutes subsistaient sur son avenir après le rachat de Red Hat par IBM. Il faut savoir d’ailleurs qu’OpenShift 4.x constitue le va-tout pour IBM Cloud, qui compte bien grâce à lui rattraper son retard sur Azure et AWS...

Nous avons ainsi pu profiter longtemps d’une belle version 3.11, mais celle-ci commençait à vieillir et à s’éloigner des dernières versions de Kubernetes, qui lui, continuait sur sa lancée.

Il faut savoir que pour OpenShift la sortie d’une version majeure correspond à une vraie révolution d’architecture, pas une simple évolution.

Pour rappel historique

  • Mai 2011 : Sortie OpenShift v1 suite au rachat de Makara par Red Hat
    • Produit non opensource, basé sur une technologie de conteneurs Propriétaire
  • OpenShift v2
    • Séparation en trois projets : Origin (Opensource), Openshift (commercial) et Online (en SaaS). Même architecture technique que la V1
  • 2015 : Sortie d’openshift v3
    • Passage à Docker / Kubernetes, réécriture du produit à 100%.
  • Mai 2019 : Sortie d’OpenShift v4
    • Abandon de Docker, et passage à CRI-O comme moteur de conteneur
    • Utilisation de CoreOS comme système d’exploitation des masters
    • Nouvel installateur orienté cloud et infra as code.
    • Consoles repensées
    • Intégration poussée des opérateurs Kubernetes avec Operator Hub et Operator Lifecycle management.

Mais le voile a été levé le 15 juillet 2020 sur la version OKD 4.5. A l’heure de l’écriture de cet article, la dernière version stable est : 4.5.0-0.okd-2020-09-18-202631. Cette version est basée sur Kubernetes 1.18.

Dans cette série d’articles, nous allons découvrir cette nouvelle version, depuis son installation jusqu’à son utilisation en production.

Rappels : Qu’est-ce qu’OpenShift ? Quelles sont les différences avec OKD ?

Laissons Red Hat répondre à la question de ce qu’est OpenShift par rapport à Kubernetes :

https://www.redhat.com/fr/blog/OpenShift-and-kubernetes-whats-difference

Pour résumer, on peut comparer Kubernetes au noyau d’une distribution linux, OpenShift serait quant à lui une distribution à part entière.

C’est donc un produit complet, qui intègre Kubernetes comme noyau.

En quelques mots qu’apporte OpenShift par rapport à un Kubernetes “nu” ?

  • Des consoles web, plus faciles d’accès que le dashboard Kubernetes. (une console orientée développeur et une console orientée administrateur)
  • Une authentification simplifiée (LDAP, OAuth)
  • La notion de S2I (Source To image), qui permet de prendre en charge la construction et le stockage des images.
  • Une gestion plus stricte de la sécurité (Interdiction des conteneurs root).

OpenShift (OCP) vs OKD.

OKD, est la version opensource d’OpenShift, c’est un peu comme CentOS et RHEL. Les 2 produits possèdent exactement les mêmes fonctionnalités.

OKD OCP
Coût Gratuit (License Apache) Payant (Souscription Annuelle Red Hat /IBM)
Support Communautaire Contractuel
OS Fedora CoreOS RHCOS (RHEL 7.6 supporté sur les compute Nodes)

Les dessous d’OKD 4 : Fedora CoreOS

Fedora Core OS est une distribution Linux minimaliste dont le principal but est de servir de base à un cluster de conteneurs scalable. Elle inclut Docker et Podman pour le lancement des conteneurs. Fedora Core OS (FCOS) est le successeur de Fedora Atomic Host et de CoreOS Container Linux

Elle se configure avec un fichier yaml, le FCC (Fedora CoreOS Configuration) qui s’occupe, au démarrage, du partitionnement, de la configuration réseau, utilisateurs et sécurité.

Voilà à quoi un FCC (basique) peut ressembler :

variant: fcos
version: 1.1.0
passwd:
  users:
    - name: core
      ssh_authorized_keys:
        - ssh-rsa AAAA...

Fedora CoreOS est conçu pour être géré comme une infrastructure immuable. Une fois la machine mise en service, vous ne devez pas modifier ou reconfigurer la machine. Au lieu de cela, il faut modifier la FCC et l’utiliser pour provisionner une machine de remplacement. Cela ressemble de près à la façon dont vous gérez un conteneur.

Elle se met à jour automatiquement, c’est une rolling release, il suffit pour cela, de s’abonner à un canal de mises à jour (stable, testing…).

OKD4 sur AWS, le test : Partie 1

OKD 4 : Une nouvelle manière d’installer…

Avec OKD 3.x, nous utilisions un script ansible énorme (voire monstrueux) pour installer OpenShift sur les VMs d’une infrastructure préexistante. Ceci nous obligeait à maintenir une stack Terraform différente pour chaque cloud provider.

Avec OKD 4.x Red Hat a pris le train de l’infrastructure as code, et propose un programme d’installation (en go!) qui s’occupe à la fois de la partie infrastructure, de l’installation et de la configuration des nœuds du cluster OpenShift. Il embarque sa propre version de Terraform.

L'installateur OpenShift est clairement orienté cloud, il supporte nativement les opérateurs cloud suivants :

> aws
 azure
 gcp
 openstack
 ovirt
 vsphere

L’installation sur du bare-metal n’est plus directement supportée (elle reste néanmoins possible)

OKD4 sur AWS, le test : Partie 1

Pour en savoir plus : https://docs.okd.io/latest/architecture/architecture-installation.html

L’installation des composants internes d’OpenShift, comme le routeur et la registry, utilise maintenant des opérateurs Kubernetes.

Installation sur AWS

Prérequis

Téléchargement de l’installateur :https://github.com/OpenShift/okd/releaseshttps://github.com/OpenShift/okd/releases/download/4.5.0-0.okd-2020-09-18-202631/OpenShift-install-linux-4.5.0-0.okd-2020-09-18-202631.tar.gz

L’installation

Tout d’abord on crée le fichier de configuration install-config.yaml (recommandé) :

./openshift-install create install-config

Le programme vous pose plusieurs questions (cloud provider, région, clé ssh…)

On peut ensuite personnaliser le fichier install-config.yaml, pour par exemple

  • Changer le type d’instance EC2 pour les control-planes et les workers
  • Restreindre sur des AZ’s spécifiques

Pour le détail je vous renvoie à la documentation officielle :

https://docs.openshift.com/container-platform/4.5/installing/installing_aws/installing-aws-customizations.html#installation-configuration-parameters_installing-aws-customizations

Je vous conseille ensuite de sauvegarder ce fichier, car l’installateur le supprime une fois le cluster construit

cp install-config.yaml install-config.yaml.bak

Ensuite on peut lancer l’installation à proprement parler

[xxx@xxxx okd4-install]$ ./openShift-install create cluster
INFO Consuming Install Config from target directory  
INFO Credentials loaded from the "default" profile in file "/home/xxx/.aws/credentials"  
INFO Creating infrastructure resources...          
INFO Waiting up to 20m0s for the Kubernetes API at https://api.okd4test.okd4.xxxx.fr:6443...  
INFO API v1.18.3 up                                
INFO Waiting up to 40m0s for bootstrapping to complete...  
INFO Destroying the bootstrap resources...         
INFO Waiting up to 30m0s for the cluster at https://api.okd4test.okd4.xxxxx.fr:6443 to initialize...
INFO Waiting up to 10m0s for the OpenShift-console route to be created...  
INFO Install complete!                             
INFO To access the cluster as the system:admin user when using 'oc', run 'export KUBECONFIG=/home/xxxx/Projects/okd4-install/auth/kubeconfig'  
INFO Access the OpenShift web-console here: https://console-OpenShift-console.apps.okd4test.okd4.xxxx.fr  
INFO Login to the console with user: "kubeadmin", and password: "xxxxx"  
INFO Time elapsed: 32m7s 

30 minutes après environ, vous avez accès à votre instance, et vous pouvez vous connecter à la console OKD.

OKD4 sur AWS, le test : Partie 1

Conclusion

Vous savez maintenant qu’OKD 4 est prêt à être installé sur votre cloud provider préféré. Je vous donne rendez-vous dans un prochain article, pour voir les évolutions concernant son utilisation dans un contexte DevOps.

Apache Superset : un logiciel de visualisation à suivre

$
0
0
Apache Superset : un logiciel de visualisation à suivre

Apache Superset est un outil de visualisation et d’exploration de données. Historiquement développé pour des besoins internes par Airbnb, il est devenu open-source en 2016, incubé en tant que projet Apache et compte plus de 30 000 étoiles sur GitHub (https://github.com/apache/incubator-superset). Parmi les utilisateurs notables de cet outil, on peut citer Twitter, Udemy, Zalando, Airbnb ou encore Lyft.

Superset fonctionne en tant qu’application Web sur les principaux navigateurs internet, c’est un logiciel développé en Python et qui utilise la librairie Flask comme framework Web.

Superset étant open-source, il est possible de communiquer facilement avec la communauté, la version 0.37 est sortie ce 14 août 2020.

Eléments clés d’Apache Superset

Tout d’abord, Superset possède un large panel de visuels disponibles pour mettre en valeur ses données, du simple histogramme à des graphiques sous forme de cartes interactives.

Apache Superset : un logiciel de visualisation à suivre

L’interface d’exploration et de visualisation des données est très intuitive, pour créer des graphiques et les incorporer dans des tableaux de bord, comme on peut le voir ci-dessous. On peut sélectionner la source de données, le visuel que l’on souhaite créer, la métrique, les éventuels filtres. Les options de personnalisation du graphique (couleurs, légendes…) sont modifiables dans l’onglet “Customize”.

Apache Superset : un logiciel de visualisation à suivre

En matière de sécurité, ce logiciel permet une authentification via des protocoles comme OAuth, OpenID ou encore LDAP par exemple. De plus, on peut facilement contrôler qui a accès à quelles infos grâce à un modèle de sécurité de type RBAC (Role-Based Access Control), notamment en définissant des listes d’utilisateurs et de fonctionnalités. On peut ainsi établir des autorisations de base, filtrer l’accès aux sources de données, aux tableaux de bord ou graphiques à certaines personnes seulement.

Il y a 5 rôles principaux dans Superset :

  • Admin : il dispose de toutes les autorisations, y compris donner ou révoquer celles des autres utilisateurs, et modifier leurs tableaux de bord.
  • Alpha : il peut accéder à toutes les sources de données, mais ne peut pas modifier les autorisations des autres utilisateurs. L’utilisateur Alpha ne peut modifier que les objets qu’il possède, et a la possibilité d’ajouter ou modifier des sources de données.
  • Gamma : ils ne peuvent utiliser que les données provenant de sources auxquelles ils ont accès par le biais d’un autre rôle. Ils ne peuvent consulter que les tableaux de bord générés à partir des sources de données auxquelles ils ont accès. Ce sont des consommateurs de contenu, il leur est impossible de modifier ou d’ajouter des sources de données.
  • Sql_lab : ce rôle donne l’accès à SQL Lab. Par défaut, les Admin ont accès à toutes les bases de données, alors que les Alpha et Gamma doivent accorder les accès en fonction des bases de données.
  • Public : il permet d'autoriser les utilisateurs déconnectés à accéder à certaines fonctions de Superset.

Apache Superset est aussi composé d’un éditeur SQL/IDE avec requêtage interactif, nommé SQL Lab, j’en reparle ultérieurement.

Il est aussi possible de personnaliser les tableaux de bord en important ses propres templates CSS, et il y a une mise en cache des données pour pouvoir charger plus rapidement les tableaux de bord les plus importants.

Avec quoi peut-on le connecter, comment le mettre en place / l’installer ?

Afin d’importer ses données dans Superset, il est possible de se connecter à différentes bases de données. Superset peut se connecter à une grande partie des bases de données SQL, grâce à l’utilisation de l’ORM (Object Relational Mapping) SQLAlchemy.

L’image ci-dessous provient du site de Superset et montre une partie des bases de données connectables, parmi lesquelles on peut citer Snowflake, Amazon Redshift, ou encore Apache Druid pour des analyses temps réel.

Apache Superset : un logiciel de visualisation à suivre

Les packages Python ainsi que les URI SQLAlchemy permettant de connecter les bases de données que l’on souhaite sont disponibles sur la documentation du logiciel.

Apache Superset peut être installé localement en utilisant un conteneur Docker et Docker Compose. Ce tutoriel présente la procédure d'installation.

L’installation peut aussi être effectuée from scratch, ce que j’ai fait sur une instance AWS EC2 T2 medium, là aussi Superset propose ce guide sur son site.

L'installation contient un serveur Web accessible via HTTP sur l'IP/port du conteneur/VM. Superset rend disponibles dans ses guides d’installation des données exemples pour pouvoir prendre en main l’outil facilement et commencer à explorer, analyser et visualiser celles-ci.

J’ai pu tester les différentes fonctionnalités de Superset, connecter une base de données, créer les tables, explorer et requêter les données, mettre en place des visuels et un dashboard, grâce aux procédés exposés dans ce lien :

https://superset.incubator.apache.org/docs/creating-charts-dashboards/first-dashboard

Cela permet de découvrir Superset, pour ensuite pouvoir réaliser ses propres visuels et analyses de données et connecter les bases de données dont on a besoin.

Apache Superset : un logiciel de visualisation à suivre

Superset permet de requêter ses données via son interface SQL Lab. Ainsi, on sélectionne une base de données, le schéma de celle-ci et une table à laquelle on s’est connecté précédemment avec l’URI SQLAlchemy correspondant, et on peut par la suite réaliser des requêtes interactives. De plus, il est possible de prévisualiser les données et de sauvegarder l’historique des requêtes.

En revanche, le principal point négatif de SQL Lab est l’impossibilité de requêter ou d’ajouter plusieurs tables en même temps. Une solution consiste à créer des vues pour réaliser des jointures de plusieurs tables, mais cela peut entraîner des problèmes de performance. En effet, si l’on souhaite effectuer une transformation sur cette vue, Superset exécutera une requête en plus de la requête de vue déjà existante.

Apache Superset : un logiciel de visualisation à suivre

Comparaison avec Tableau et Metabase

Après avoir présenté cet outil de visualisation qu’est Apache Superset, voici un comparatif avec deux autres outils d’analyse et de présentation graphique des données, Tableau et Metabase.

Tableau Apache Superset Metabase
Payant Open source et gratuit, incubé par l’Apache Software Foundation (ASF) Open source et gratuit, version payante d’entreprise qui apporte d’autres fonctionnalités. Offre Metabase Cloud depuis Septembre 2020
Possible d’établir des liens entre tables d’une même base ou de base de données différentes Impossible de joindre/requêter plusieurs tables en même temps Possible d’établir des liens entre tables d’une même base ou de base de données différentes
Connectable aux bases NoSQL Pas de connexion avec les bases NoSQL Connectable à la base NoSQL MongoDB
Large personnalisation des tableaux de bord via filtres, tags et légendes Personnalisation limitée (légendes, quelques filtres et tags). Beaucoup d’améliorations apportées dernièrement sur ce point Personnalisation des tableaux de bord avec un large panel de visuels, légendes, filtres
Authentification avec OAuth, OpenID, SAML ou Active Directory Authentification avec OAuth, OpenID ou LDAP Authentification avec OAuth ou LDAP
Facilité d’utilisation pour les débutants, pas de nécessité de connaître le SQL, possibilité d’approfondir les connaissances via des guides pour les plus expérimentés Interface intuitive, nécessité de connaître le SQL pour pouvoir pleinement profiter de SQL Lab notamment Facilité d’utilisation pour les débutants, pas de nécessité de connaître le SQL. Exploration des données simple à réaliser pour les personnes non techniques

Conclusion

Apache Superset est un projet open source qui évolue et grandit très rapidement, la communauté est très réactive. Ses points forts sont les nombreuses bases de données que l’on peut connecter, et des visuels variés, faciles à utiliser, il est très simple de créer des tableaux de bord. Il faut toutefois connaître le SQL pour pouvoir pleinement exploiter cet outil de visualisation. Ce logiciel open source est une bonne alternative à d’autres solutions comme Metabase, en fonction de vos critères de sélection, mais il est encore assez loin de Tableau, l’un des outils les plus utilisés en visualisation de données.

Le créateur initial de Superset, Maxime Beauchemin, a fondé l’entreprise Preset, dont la plateforme d’analyse de données se base sur l’outil open source de visualisation. C’est une manière d’accélérer un peu plus le développement de Superset, via le financement et la vitrine commerciale de Preset. Il est intéressant de suivre l’évolution générale d’Apache Superset.

C’est donc un outil à considérer comme dernière brique de votre Data Platform, qui ne vous coûtera que le prix des ressources nécessaires pour le déployer.

Références :

https://superset.incubator.apache.org/
https://preset.io/
https://github.com/apache/incubator-superset
https://www.dremio.com/tutorials/dremio-apache-superset/
https://blog.smartcat.io/2018/test-driving-apache-superset/
https://medium.com/datadriveninvestor/intoducing-apache-superset-an-open-source-data-visualizaton-tool-4684627014fd


Créer un Stepper en VueJS

$
0
0
Créer un Stepper en VueJS

Cet article a pour but de montrer comment réaliser son propre composant de stepper en VueJS, bien que le code reste très facilement adaptable pour n’importe quel autre framework front.

Ce type de stepper est généralement utilisé pour illustrer simplement des processus, par étapes, à l’utilisateur, et lui faire comprendre rapidement les étapes qu’il a réalisées et celles qu’il lui reste avant de terminer le processus. Les steppers créent un chemin clair vers la finalisation d’un processus. Il est vrai qu'offrir aux utilisateurs une idée précise du nombre d'étapes nécessaires pour atteindre la cible finale pourrait réduire considérablement le taux d'abandon. On retrouve ce type de stepper dans un tunnel d’achat par exemple, mais il peut servir à tout autre cas d’utilisation qui nécessite un processus défini par avance (enregistrement d’utilisateur, recherche avancée, constitution d’un dossier, prise de rendez-vous, …).

À la fin de cet article, vous saurez comment réaliser le composant suivant, en VueJS 2, TypeScript, HTML, CSS. Il a été réalisé avec quelques classes utilitaires de Bootstrap relatives aux flexboxes. Il est responsive puisqu’il permet de mettre environ une vingtaine d’étapes sans qu’elles soient trop serrées, ce qui est déjà bien trop pour un stepper de ce type !

Créer un Stepper en VueJS

1 - Créer une étape

Créer un Stepper en VueJS

Commençons par créer une seule étape de notre stepper. Nous allons d’abord créer un nouveau composant qui s’appellera “Stepper.vue”. Dans le template, le code suivant permet d’avoir une simple puce, ou bullet en anglais :

Stepper.vue - Template

<div class="step row no-gutters col justify-content-center align-items-center">
 <div class="bullet row justify-content-center align-items-center">
   <div>1</div>
 </div>
 <div class="title col-12">
   Étape 1
 </div>
</div>

Nous aurons aussi besoin du code SCSS suivant :

Stepper.vue - Styles

.step {
 .bullet {
   border-radius: 50%;
   background-color: #26318d;
   color: white;
   font-weight: bold;
   width: 2rem;
   height: 2rem;
 }

 .title {
   font-size: small;
   color: #26318d;
   text-align: center;
   margin-top: 0.3rem;
 }
}

Ici nous définissons donc une div step qui est un composant flexbox dont les enfants sont centrés (row no-gutters justify-content-center align-items-center). La classe no-gutters permet d’enlever les gouttières (padding) sur le côté de notre composant, ainsi que ses enfants.

Le premier enfant de step est une div bullet qui contient le numéro de l’étape que nous créons. Le texte est parfaitement centré encore une fois grâce aux flexboxes. En matière de CSS, on lui donne un border-radius de 50% pour rendre ronde notre bullet, et on fixe sa taille à 2rem.

Enfin, nous avons le dernier enfant title, qui contient le nom de l’étape actuelle. On lui donne la classe col-12 pour qu’il puisse prendre toute la largeur disponible.

2 - Agencer plusieurs étapes

Créer un Stepper en VueJS

Dans cette deuxième étape, nous allons essayer de rendre notre composant un peu plus paramétrable, en donnant en entrée une liste d’étapes à afficher. Pour cela nous allons créer notre fichier “Stepper.ts”, et y ajouter le code qui suit. Il suffit ensuite de faire un lien vers ce fichier dans notre composant Vue. Les deux méthodes fonctionnent et sont équivalentes.

Stepper.ts

@Component
export default class Stepper extends Vue {
 @Prop({ default: [] })
 public steps: string[];
}

Ici nous introduisons donc un nouvel attribut à notre composant : steps. C’est une prop (une donnée d’entrée de notre composant) qui est par défaut un tableau vide.

Stepper.vue - Template

<div class="steps-container w-100 row no-gutters justify-content-between align-items-center">
 <div class="step row no-gutters col justify-content-center align-items-center"
      v-for="(step, index) in steps"
      :key="step">
   <div class="bullet row justify-content-center align-items-center">
     <div>{{ index + 1 }}</div>
   </div>
   <div class="title col-12">
     {{ step }}
   </div>
 </div>
</div>

Dans notre template HTML, nous itérons sur ce tableau pour afficher une étape pour chaque élément de notre tableau steps. Pour cela, nous entourons notre code existant par une div steps-container qui, grâce aux flexboxes, centre ses enfants verticalement (align-items-center) et les écarte au maximum à l’horizontal (justify-content-between). Il est important de rajouter l’attribut :key="step" ici puisqu’il permet à la directive v-for de garder la trace de l’élément en cours, si jamais ce dernier change dynamiquement, en lui donnant un identifiant.

3 - Relier les bullets par une ligne

Créer un Stepper en VueJS

Cette étape est souvent la plus difficile, puisqu’elle nécessite un peu de logique et de dextérité, comparé aux étapes précédentes. C’est parti !

Stepper.vue - Template

<div class="steps-container w-100 row no-gutters justify-content-between align-items-center">
 <div class="step row no-gutters col justify-content-center align-items-center"
      v-for="(step, index) in steps"
      :key="step">
   <div class="bullet row justify-content-center align-items-center">
     <div>{{ index + 1 }}</div>
   </div>
   <div class="title col-12">
     {{ step }}
   </div>
   <div class="line"
        v-if="index < steps.length - 1">
   </div>
 </div>
</div>

Nous allons donc créer une ligne pour chaque étape que nous avons, sauf la dernière étape bien sûr, sinon on dépasserait (d’où la condition du v-if).

Stepper.vue - Styles

.step {
 position: relative;

 .line {
   width: 100%;
   height: 2px;
   background-color: #26318d;
   position: absolute;
   top: 25%;
   left: 50%;
   z-index: -1;
 }

 [...]
}

Cette ligne va prendre toute la largeur de notre bloc (donc la largeur d’une col auto-calculée par flexbox). Nous lui donnons une position absolue, qui va donc se référer à son parent relatif le plus proche, donc nous rendons son parent step positionné relativement. Ainsi, nous pouvons utiliser les propriétés left et top pour positionner librement notre ligne dans son conteneur parent, comme le montre le GIF ci-dessous.

Créer un Stepper en VueJS

4 - Gérer l’état de chaque étape

Créer un Stepper en VueJS

Enfin, il ne nous reste plus qu’à gérer l’état de chaque étape. Nous devons gérer l’état disabled des bullet, title et line lorsque l’étape est postérieure à l’étape actuelle. Les étapes précédentes, elles, doivent afficher une coche verte en lieu et place du numéro de l’étape. Pour cela, nous avons besoin d’une nouvelle prop qui identifiera l’étape actuelle.

Stepper.ts

@Component({
 components: {
   CheckIcon,
 },
})
export default class Stepper extends Vue {
 @Prop({ default: [] })
 public steps: string[];

 @Prop({ default: 0 })
 public currentStep: number;
}

Nous définissons cette prop dans notre composant Stepper, sous le nom currentStep qui sera un nombre, par défaut 0, et qui correspond à l’index de l’étape actuelle dans le tableau steps. On en profite aussi pour injecter le composant CheckIcon de vue-feather-icons qui nous servira pour afficher la coche verte.

Stepper.vue - Template

<div class="steps-container w-100 row no-gutters justify-content-between align-items-center">
  <div class="step row no-gutters col justify-content-center align-items-center"
         v-for="(step, index) in steps"
         v-bind:class="{ disabled: index > currentStep }"
         :key="step">
    <div class="bullet row justify-content-center align-items-center">
      <check-icon v-if="index < currentStep || currentStep === steps.length - 1"
                  size="1x"
                  class="check-icon"></check-icon>
      <div v-else>{{ index + 1 }}</div>
    </div>
    <div class="title col-12">
      {{ step }}
    </div>
    <div class="line"
         v-if="index < steps.length - 1"
         v-bind:class="{ disabled: index === currentStep }">
    </div>
  </div>
</div>

Nous ajoutons ici dynamiquement la classe disabled sur notre step lorsque l’index est supérieur à l’index de notre currentStep. Cette règle va permettre de donner un style disabled à tous les éléments enfants de notre step, que nous définissons ci-dessous. Cependant, puisque notre line correspond au lien entre notre étape et la suivante, nous devons la rendre disabled pour notre étape actuelle également.

Enfin, l’icône de coche verte a été ajoutée dans la bullet, avec la condition d’apparaître uniquement si nous avons déjà passé l’étape OU si le processus est terminé (auquel cas toutes les bullet auront donc une coche verte).

Stepper.vue - Styles

.step {
 position: relative;

 .line {
   [...]

   // For disabling line for currentStep
   &.disabled {
     background-color: lighten(#26318d, 40%);
   }
 }

 [...]

 .check-icon {
   color: #00d992;
 }

 // For disabling everything for next steps
 &.disabled {
   .line, .bullet {
     background-color: lighten(#26318d, 40%);
   }

   .title {
     color: lighten(#26318d, 40%);
   }
 }
}

Pour gérer l’état disabled de notre line actuelle, nous changeons juste son background-color en utilisant une fonction bien pratique de SCSS qui s’appelle lighten et permet d’éclaircir une couleur d’un certain pourcentage. Nous utilisons la même technique pour le deuxième bloc de code qui correspond à l’état disabled des étapes suivantes.

Et voilà, c’est déjà fini ! Voici le résultat sous forme d’animation pour se rendre compte de l’état de notre stepper dans chaque étape :

Créer un Stepper en VueJS

Aller plus loin

Ce stepper est plutôt simple et ne permet pas d’interaction. On pourrait rajouter des actions lors d’un clic sur une étape précédente par exemple, ce qui permettrait un retour en arrière. On pourrait aussi ajouter un peu d’animation lors du passage d’une étape à l’autre, pour rendre le composant plus agréable.

React/Redux - Améliorer sa productivité avec Redux toolkit (TypeScript)

$
0
0

React/Redux - Améliorer sa productivité avec Redux toolkit (TypeScript)

Assez souvent, on entend dire que Redux est un outil dépassé ou vieillissant et il arrive que les néophytes soient un peu découragés lors de leurs premiers pas.
En 2018, React Context est annoncé et a pour objectif de faire transiter des données au travers de l’arbre de composants sans avoir à passer par les props à chaque niveau.
Cette annonce a inquiété certains utilisateurs de Redux, ceux-ci croyant que la librairie ne serait plus maintenue après l’apparition de la nouvelle feature de React.
Redux est pourtant, comme l’a écrit Mark Erikson (mainteneur majeur de la librairie) “Not dead yet !”.

Dans cet article, je mets en avant une évolution récente de Redux qui permet de réduire considérablement le “boilerplate code” ainsi que l’aspect verbeux de la librairie : Redux toolkit.

Afin d’illustrer mes propos, un projet contenant une branche redux-vanilla et une autre Redux toolkit est mis à disposition sur GitHub. Je m'appuierai sur ce projet au fil de l’article pour comparer les deux approches et mettre en avant l’utilisation de Redux toolkit.

Afin de ne pas surcharger l’article, nous nous concentrerons uniquement sur le code lié à Redux. Bien entendu, n’hésitez pas à suivre avec le repo git en parallèle pour mieux comprendre l'interaction entre Redux et les composants.


Vue d’ensemble de l’application


Commençons tout d’abord par une vue d’ensemble de l’application sur laquelle nous allons nous baser, cela permettra de mieux comprendre le domaine utilisé, ici la gestion de couleurs, et l’intérêt de Redux.
Dans celle-ci, nous allons simplement pouvoir choisir une couleur (red, green, blue) en cliquant sur des boutons radio ainsi qu’une saturation en cliquant sur des boutons “+” et “-”.
Un message combinant ces deux sélections sera alors affiché sur la même page :

React/Redux - Améliorer sa productivité avec Redux toolkit (TypeScript)

Cette simple page est divisée en plusieurs composants :

  • ColorSelector
  • ColorSaturation
  • ColorDisplay

React/Redux - Améliorer sa productivité avec Redux toolkit (TypeScript)

Dans une architecture sans Redux, ils communiqueraient ensemble en passant des informations par leurs props de cette manière :

React/Redux - Améliorer sa productivité avec Redux toolkit (TypeScript)

Grâce à Redux, on réduit le couplage entre les composants et ceux-ci vont directement interagir avec le store :

React/Redux - Améliorer sa productivité avec Redux toolkit (TypeScript)



L’application développée avec Redux “vanilla” :

Nous allons maintenant voir dans le code les différents morceaux de l’application qui fonctionnent entre eux et qui suivent la logique du diagramme précédent. Pour rappel, le code présenté dans cette partie est disponible sur GitHub sur la branche redux-vanilla.

En utilisant Redux de manière classique, nous allons pouvoir voir différents types de fichiers : constants, actions, et reducers.

Les constants :

Les constants vont servir à nommer les actions que le reducer interceptera :

Lien GitHub : src/store/colors/constants.ts

export const SELECT_COLOR = 'SELECT_COLOR';
export const INCREASE_SATURATION = 'INCREASE_SATURATION';
export const DECREASE_SATURATION = 'DECREASE_SATURATION';

On peut d’ores et déjà voir 3 constants (“SELECT_COLOR”, “INCREASE_SATURATION”, et “DECREASE_SATURATION”) qui nous permettront d’identifier 3 actions différentes.


Les actions :

Les actions seront “dispatched” par les composants, c’est-à-dire envoyées aux reducers afin qu’ils les interprètent et mettent à jour l’état du store.

Lien GitHub : src/store/colors/actions.ts

import {Color} from "../../components/color-selector/ColorSelector";
import {DECREASE_SATURATION, INCREASE_SATURATION, SELECT_COLOR} from "./constants";

export interface SelectColorAction {
    type: typeof SELECT_COLOR
    payload: Color
}

export interface IncreaseSaturation {
    type: typeof INCREASE_SATURATION
}

export interface DecreaseSaturation {
    type: typeof DECREASE_SATURATION
}

export type ColorsActionTypes = SelectColorAction | IncreaseSaturation | DecreaseSaturation;

export const selectColor = (color: Color): SelectColorAction => ({
    type: SELECT_COLOR,
    payload: color
});

export const increaseSaturation = (): IncreaseSaturation => ({
    type: INCREASE_SATURATION
});

export const decreaseSaturation = (): DecreaseSaturation => ({
    type: DECREASE_SATURATION
});

Les reducers :

Notre unique reducer permet de “capter” les actions envoyées par les composants afin de mettre à jour l’état du store.

Lien GitHub : src/store/colors/reducers.ts

import {Color} from "../../components/color-selector/ColorSelector";
import {DECREASE_SATURATION, INCREASE_SATURATION, SELECT_COLOR} from "./constants";
import {ColorsActionTypes} from "./actions";

export interface ColorsState {
    current: Color,
    saturation: number
}

const initialState: ColorsState = {current: "Red", saturation: 50};

const MAX_SATURATION = 100;
const MIN_SATURATION = 0;

const reducers = (state: ColorsState = initialState, action: ColorsActionTypes): ColorsState => {
    const {saturation} = state;
    switch (action.type) {
        case SELECT_COLOR:
            return {
                ...state,
                current: action.payload
            }
        case INCREASE_SATURATION:
            return {
                ...state,
                saturation: saturation === MAX_SATURATION ? MAX_SATURATION : saturation + 1
            }
        case DECREASE_SATURATION:
            return {
                ...state,
                saturation: saturation === MIN_SATURATION ? MIN_SATURATION : saturation - 1
            }
        default:
            return state
    }
};

export default reducers;

On peut voir sans surprise que les reducers sont capables de gérer les 3 actions que nous avons listées plus haut.

Ci-dessous les tests unitaires des reducers :

Lien GitHub : src/store/colors/reducers.spec.ts

import {decreaseSaturation, increaseSaturation, selectColor} from "./actions";
import reducers, {ColorsState} from "./reducers";

describe('colors reducer', () => {

    const initialState: ColorsState = {current: "Red", saturation: 50};

     it.each`
     color
     ${"Blue"}
     ${"Green"}
     ${"Red"}
     `('should select the $color color', ({color}) => {
         const nextState: ColorsState = reducers(initialState, selectColor(color));
         expect(nextState).toStrictEqual({...initialState, current: color});
    });

    it('should not increase the saturation when it is already 100', () => {
        const previousState: ColorsState = {current: "Red", saturation: 100};
        const nextState: ColorsState = reducers(previousState, increaseSaturation());
        expect(nextState).toStrictEqual(previousState);
    });

    it('should increase the saturation', () => {
         const nextState: ColorsState = reducers(initialState, increaseSaturation());
         expect(nextState).toStrictEqual({...initialState, saturation: 51});
    });

    it('should not decrease the saturation when it is already 0', () => {
        const previousState: ColorsState = {current: "Red", saturation: 0};
        const nextState: ColorsState = reducers(previousState, decreaseSaturation());
        expect(nextState).toStrictEqual(previousState);
    });

    it('should decrease the saturation', () => {
         const nextState: ColorsState = reducers(initialState, decreaseSaturation());
         expect(nextState).toStrictEqual({...initialState, saturation: 49});
    });

});



L’application développée avec redux toolkit :


Nous avons pu voir dans la section précédente une utilisation relativement classique de Redux. Alors que cette utilisation fait très bien le travail, elle devient très rapidement verbeuse et écrire constamment les constants, actions et reducers correspondants demandera du temps au développeur. Pour rappel, le code présenté dans cette partie est disponible sur GitHub sur la branche redux-toolkit.

C’est justement ce temps de développement de “boilerplate” que Redux toolkit aide à éliminer. Un des outils que la librairie propose et que nous allons voir dans la section suivante est le slice.

Introduction au slice :

Lien GitHub : src/store/colors/slice.ts

import {createSlice, PayloadAction} from "@reduxjs/toolkit";
import {Color} from "../../components/color-selector/ColorSelector";

export interface ColorsState {
    current: Color,
    saturation: number
}

const initialState: ColorsState = {current: "Red", saturation: 50};

const colorsSlice = createSlice({
    name: 'colors',
    initialState: initialState,
    reducers: {
        selectColor(state, action: PayloadAction<Color>) {
            state.current = action.payload
        },
        increaseSaturation(state) {
            if (state.saturation < 100 ) state.saturation++
        },
        decreaseSaturation(state) {
            if (state.saturation > 0 ) state.saturation--
        }
    }
});

export default colorsSlice;

Ce sera à présent le seul et unique fichier à modifier pour faire fonctionner notre logique Redux.

L’objet qui nous intéresse est colorsSlice, il est créé en faisant appel à la méthode createSlice qui vient de Redux toolkit.

Dans cette méthode, il faut passer un objet qui contient plusieurs propriétés :

  • name : correspond au nom du domaine dans le store
  • initialState : l’état initial du domaine dans le store
  • reducers : les différentes fonctions pures qui ont pour rôle de mettre à jour l’état du domaine dans le store en fonction d’une action reçue

Lors de sa création, le slice va exposer plusieurs propriétés dont 2 qui nous intéressent particulièrement : actions et reducer.

Dans les actions, nous allons retrouver des actions créées automatiquement par Redux toolkit qui correspondent aux noms de nos reducers :

colorsSlice.actions.selectColor
colorsSlice.actions.increaseSaturation
colorsSlice.actions.decreaseSaturation

Ce sont ces actions que les composants pourront utiliser et dispatcher de manière classique.

Quant à reducer, c’est en fait une fonction pure qui prend en paramètre l’état du domaine ainsi qu’une action et qui renvoie un nouvel état du domaine en fonction de l’action qui lui a été passée :

colorsSlice.reducer(someState, someAction)
colorsSlice.reducer(initialState, colorsSlice.actions.decreaseSaturation)

Avoir accès au reducer nous permettra principalement de tester unitairement son comportement.

Pour résumer, un slice va permettre de créer des actions automatiquement à partir du nom du reducer qui aura comme rôle de gérer cette action. Ainsi, on peut utiliser des actions générées par notre slice comme une action classique simplement en déclarant le reducer.

Cette pratique permet de ne plus avoir à maintenir constants, actions, et reducers séparément car tout est implicitement lié à l’implémentation d’une seule entité : le slice.


L'immutabilité grâce à Immer :

Lors de la mise à jour de l’état du store via un reducer, il est impératif de renvoyer un nouvel objet et de ne pas modifier l’état précédent. On appelle cela l’immutabilité. Vous pouvez trouver des informations sur cette règle dans la documentation officielle de Redux.
Cela peut néanmoins devenir un problème lorsque l’on fait face à de nombreux objets imbriqués ou à des tableaux, et que l’utilisation du “spread operator”, une fonctionnalité ES6, devient complexe.

Vous l’avez peut-être remarqué, mais lors de la déclaration des reducers dans notre slice, nous avons modifié l’état et n’avons donc pas respecté le principe d’immutabilité et le fait qu’un reducer doit être une fonction pure.
En réalité, c’est totalement acceptable, grâce à Immer.
Immer est une librairie plutôt performante incluse par défaut dans Redux toolkit qui permet d’écrire nos reducers comme des fonctions impures (modification de l’état directement) sans briser la règle de l'immutabilité. En effet, Immer met en place tout un mécanisme, afin de maintenir ce principe.

Le principe est que l’on applique des modifications à un état “brouillon”, qui est un clone de l'état actuel. Une fois que toutes les mutations sont terminées, Immer génère l'état suivant basé sur les mutations de l'état de “brouillon”.

React/Redux - Améliorer sa productivité avec Redux toolkit (TypeScript)

Une fois de plus, cela permet au développeur d’écrire du code moins complexe, moins verbeux, plus maintenable.


Tester unitairement, rien de plus simple :

Un des avantages de l’utilisation de Redux toolkit est qu’il est possible de l’intégrer dans la base de code assez aisément. En effet, il suffit de remplacer les constants, actions, et reducers par des slices comme nous avons pu le voir, mais l’interface d’utilisation pour les composants reste globalement la même : un dispatch d’actions venant de slices plutôt que de fichiers contenant les actions directement.

Un des avantages de Redux toolkit est que la manière de tester ses reducers est similaire :

Lien GitHub : src/store/colors/slice.spec.ts

import colorsSlice, {ColorsState} from "./slice";

const { reducer, actions } = colorsSlice;
const { decreaseSaturation, increaseSaturation, selectColor } = actions;

describe('colors reducer', () => {

    const initialState: ColorsState = {current: "Red", saturation: 50};

    it.each`
     color
     ${"Blue"}
     ${"Green"}
     ${"Red"}
     `('should select the $color color', ({color}) => {
        const nextState: ColorsState = reducer(initialState, selectColor(color));
        expect(nextState).toStrictEqual({...initialState, current: color});
    });

    it('should not increase the saturation when it is already 100', () => {
        const previousState: ColorsState = {current: "Red", saturation: 100};
        const nextState: ColorsState = reducer(previousState, increaseSaturation());
        expect(nextState).toStrictEqual(previousState);
    });

    it('should increase the saturation', () => {
        const nextState: ColorsState = reducer(initialState, increaseSaturation());
        expect(nextState).toStrictEqual({...initialState, saturation: 51});
    });

    it('should not decrease the saturation when it is already 0', () => {
        const previousState: ColorsState = {current: "Red", saturation: 0};
        const nextState: ColorsState = reducer(previousState, decreaseSaturation());
        expect(nextState).toStrictEqual(previousState);
    });

    it('should decrease the saturation', () => {
        const nextState: ColorsState = reducer(initialState, decreaseSaturation());
        expect(nextState).toStrictEqual({...initialState, saturation: 49});
    });

});

Pour aller plus loin


Nous avons pu voir dans cet article comment Redux toolkit permet de réduire le temps de développement ainsi que la verbosité d’un code faisant utilisation de Redux.
Bien entendu l’article n’est pas exhaustif et présente un des atouts de cette librairie qui en cache bien d’autres.
Aussi je vous recommande de vous renseigner sur la documentation officielle de Redux toolkit qui est plutôt détaillée et qui liste bien les différentes fonctionnalités tout en les présentant avec des exemples.

Game Changer : low-code et serverless en GraphQL

$
0
0
Game Changer : low-code et serverless en GraphQL

Article co-écrit par Aïssa BENHAMIDA et Jérémy RATSIMANDRESY.

Introduction

Game Changer est  un projet qui repose sur deux idées : explorer les usages de GraphQL et en générer rapidement une architecture serverless. En quelques mots, à partir d’un schéma GraphQL, qui décrit les différents types de données, on peut générer une application complète de gestion de données. D’un côté, la partie serveur permet le traitement de requêtes GraphQL et des actions à appliquer sur nos données (modification, lecture) et de l’autre la partie cliente permet d’avoir une interface pour interagir avec ces données.

Principes de conceptions

C’est quoi le low-code ?

Le low-code c’est cette notion qui, dans un contexte de besoin de productivité et de rapidité de tests/déploiement, se développe de plus en plus et redéfinit la vision que l’on porte sur le développement logiciel. L’objectif est d’obtenir des solutions informatiques prêtes à l’emploi rapidement, tout en réduisant l’écriture de code au minimum.
Pour cela, on favorise l’usage de générateurs et d’outils templates. Le logiciel Excel est un exemple d’outil qui s’inscrit dans cette logique : initialement, le tableur n’a pas de fonction particulière, mais une fois les cellules remplies, par des fonctions mathématiques par exemple, on obtient une application fonctionnelle personnalisée selon le besoin, sans avoir eu besoin d’écrire de nombreuses lignes de code. De la même façon, on cherche à automatiser le plus possible les problématiques de traitement de données et de requêtes.

La force du cloud pour du serverless

On dit “ce qui est fait n’est plus à faire”, et c’est ce que le Cloud vient ainsi apporter dans un contexte de low-code. Des tâches comme définir des ressources mémoire, exécuter du code ou encore gérer le maintien des environnements (pour les coûts) sont facilement délégables aux services de cloud computing. Une instance Lambda par exemple, n’a pas besoin d’être administrée manuellement. Le code contenu est exécuté automatiquement, en fonction d’événements déclencheurs définis. L’utilisateur se concentre ainsi sur le contenu plutôt que sur la forme et la structure.

GraphQL pour encapsuler l’ensemble

GraphQL, pour Graph Query Language, est une spécification de description de données qui propose une alternative aux services REST traditionnels dans un environnement d’API. Trois grands principes les distinguent.

Tout d’abord, l’accès aux données en contexte GraphQL s’effectue à partir d’un point d’entrée unique, qui se termine par convention en /graphql, alors qu’en REST les points d’accès sont définis en fonction des données recherchées et des requêtes.
Ensuite, les requêtes GraphQL sont encapsulées dans la méthode HTTP POST quel que soit le type de requête, là où REST utilise l’ensemble des méthodes HTTP pour préciser la nature de la requête.
Enfin, le point fort de GraphQL réside dans la possibilité de construire dans sa requête la réponse souhaitée, en indiquant uniquement les informations sur les objets que l’on souhaite. Un serveur REST retournera au contraire l’ensemble des informations des objets correspondant à une requête même si elles ne sont pas pertinentes pour l’utilisateur.

Dans cette optique, GraphQL est une solution qui permet d’améliorer les performances (pour le transport de données notamment), d’accorder une liberté pour les clients au niveau des requêtes et de simplifier l'accessibilité grâce à l’unique point d’accès d’API.

Architecture

Le projet se présente comme un ensemble de générateurs qui s’appuient sur le framework de scaffolding Yeoman : un pour l’aspect serveur et deux pour l’aspect client.

Partie serveur

L’enjeu principal était de pouvoir obtenir une API possédant les propriétés de la spécification GraphQL, à l’aide des différents services que proposent AWS. Chacun d’entre eux occupe un rôle clé :

Lambda

C’est le cœur de notre API GraphQL, qui héberge tout le code d'exécution du serveur. Les requêtes des clients transmises par l'intermédiaire du service API Gateway sont interprétées et traitées pour interagir avec la base de données Aurora. Les données reçues en réponse ou les erreurs sont ensuite retransmises à Lambda qui réachemine ces résultats vers les clients dans le sens inverse.

Aurora Serverless

Ce système de base de données dit serverless se base sur la non nécessité pour les utilisateurs de paramétrer les différentes caractéristiques du serveur de base de données. Les ajustements de stockage et de performance sont entièrement gérés par AWS.
Pour le projet, ce service permet de stocker les données de l’application générée. Le choix de postgres comme moteur de base de données permet de faciliter la gestion relationnelle des données.

API Gateway

Ce service fournit le point d’accès du serveur GraphQL, pour permettre aux clients d’envoyer des requêtes, et déclenche l’exécution de la lambda en les transmettant.

Cognito

Avec Cognito, on dispose de la possibilité de créer et administrer des groupes d’utilisateurs, afin de leur accorder des droits et des restrictions. On restreint ainsi l’accès aux données de notre application, en imposant à notre point d’accès défini par l’API Gateway la nécessité d’un token d’authentification.

S3 et CloudFront

Ces deux services souvent complémentaires dans des environnements de développement web en cloud vont permettre le déploiement de la partie front de l’application. D’une part S3 correspond à des espaces de stockage pouvant supporter différents types de données : images, vidéos, fichiers texte. L’ensemble du code front est donc déposé dans une instance de ce service. D’autre part, CloudFront est un CDN, ce qui va faciliter l’accès aux ressources grâce à une adresse URL générée. Cette adresse pointera directement sur les fichiers contenus dans l’instance S3 précédemment créée.

Une image étant plus parlante que des mots, on obtient l’architecture suivante :

Game Changer : low-code et serverless en GraphQL

L'exécution d’une requête se déroule de la manière suivante :

Game Changer : low-code et serverless en GraphQL

Le générateur permet ainsi d’obtenir un module Node.js qui servira de code pour l’instance Lambda et un ensemble de fichiers de configuration terraform permettant de mettre en place toute la structure décrite au sein d’AWS.

Partie cliente

Associé à notre API, le client fournit une interface utilisateur permettant de réaliser l’ensemble des opérations CRUD définies selon la spécification GraphQL. L’objectif idéal est d’appliquer ces principes sur l’ensemble des frameworks front usuels dont React, Angular ou encore Vue.

Ember

Ce framework a servi de point d’entrée pour affiner le concept de la partie cliente. D’une part, chaque objet aura son ensemble de composants front (route, template). D’autre part, à travers l’addon Ember Data, on bénéficie d’une logique de gestion de données interne, à travers un store, qui supporte les relations entre entités et assure un aspect de persistance de données avec la partie serveur de l’application. Des objets Model propres à Ember sont créés pour chaque type d’objet.

Game Changer : low-code et serverless en GraphQL
Schéma fonctionnnel du système de persistance de données d'ember-data

Source : http://baptiste.meurant.io/ember/training/ember-data

Ce schéma expose parfaitement cette deuxième partie : l’application gère ses données en interne à travers  le store, mais communique aussi avec le serveur pour assurer une synchronisation des données.

Le générateur obtenu s’appuie ainsi sur le système d’addon propre à Ember et permet à partir d’un projet ember vierge de générer l’ensemble du système front (route, composant, template) pour chaque type d’objet ainsi que la logique de persistance de données.

React

À partir de l’exemple d’Ember avec Ember Data nous avons imaginé une implémentation similaire avec React. On avait donc besoin d’une gestion centralisée des données, à travers l'état de l’application, tout en maintenant une synchronisation avec notre API serverless. Redux apparaissait comme un candidat idéal pour ces opérations.
En effet, ce système de gestion d’état centralisé est conçu pour s’intégrer de manière optimale avec React. En s’inspirant du fonctionnement d’Ember Data, nous avons adapté son implémentation au sein de l’application finale afin de permettre une persistance des données entre le serveur et la partie cliente. Ainsi, pour chaque interaction avec l’application faite par l’utilisateur ayant un impact sur les données, un objet Action va être envoyé à la logique de Redux (Store). En fonction de la nature de l’action, des modifications sur l’état de l’application, c’est-à-dire la représentation de ses données, sont appliquées et l’interface utilisateur (UI) se met à jour pour transmettre visuellement les changements. Dans le même temps, ces modifications sont transmises de manière asynchrone au serveur sous forme de requêtes GraphQL et sont appliquées sur les données en base de données. Enfin, dans le cas de récupération de données depuis le serveur, les Actions vont attendre de recevoir ces données par requêtes GraphQL pour permettre ensuite au store de remplir l’état de l’application.

Le schéma ci-dessous illustre ce principe en prenant comme exemple une interaction avec trois sources d’API.

Game Changer : low-code et serverless en GraphQL
Implémentation fonctionnelle de Redux

Le générateur obtenu permet comme pour Ember d’obtenir à partir d’un projet React vierge l’ensemble du système front mais aussi la logique de gestion d’état proposée par Redux. Cette implémentation réussie amène à imaginer une application plus large sur d’autres frameworks, en utilisant Redux ou d’autres gestionnaires de store parfois mieux optimisés en fonction des frameworks (Vuex pour Vue par exemple).

Le mot de la fin

En utilisant les capacités de services du cloud et les outils front-end, on a obtenu un framework de génération d'applications serverless qui s’inscrivent dans l’écosystème de GraphQL. Aujourd’hui le projet est disponible sur son repo Github dans sa première version, avec de nombreuses pistes qui restent à creuser pour le rendre plus robuste : support des types de relations, support de l’ensemble de la spécification GraphQL, ajout de nouveaux supports front-end…
La vidéo de démonstration suivante permet de rendre compte de l’utilisation concrète du projet :

Amazon Cognito: User Pool ou Identity Pool, que choisir ?

$
0
0
Amazon Cognito: User Pool ou Identity Pool, que choisir ?

Si vous vous lancez sur Amazon Cognito pour la première fois, vous avez dû remarquer que ce service est en réalité composé de deux sous-services : User Pools (groupes d’utilisateurs) et Identity Pools (groupes d’identité), appelé également Federated Identities (Identités Fédérées).

J’étais débutant sur AWS et je souhaitais sécuriser une API Gateway avec une connexion via Google. Cependant, j’avais l’impression que les deux services répondaient à mon besoin. Je ne savais donc pas lequel choisir.

Par la suite j’ai pu découvrir que, même s’ils ont des similitudes, ils ne répondent pas aux mêmes cas d’utilisation. Nous allons voir dans cet article ce qu’ils apportent et dans quelles situations les utiliser.

TL;DR : User Pools vous permet d’ajouter des fonctionnalités d’inscription et de connexion à votre application tandis que Identity Pools peut accorder à vos utilisateurs l’accès à des services AWS.

Cognito User Pool

Les groupes d’utilisateurs Cognito permettent de découpler la gestion des utilisateurs d’une application. Ils apportent les fonctionnalités suivantes :

  • Inscription, connexion
  • Récupération du mot de passe en cas d’oubli
  • Vérification de l’email ou du numéro de téléphone
  • Stratégie de complexité du mot de passe
  • Authentification multi-facteurs (envoi d’un code par email ou SMS)
  • Connexion à travers les fournisseurs d’identité suivants : Facebook, Google, Amazon, Apple mais également à tous ceux qui utilisent les protocoles de connexion SAML et OpenID Connect

Les utilisateurs sont stockés sur AWS et accessibles via le service Cognito sur la console AWS :

Amazon Cognito: User Pool ou Identity Pool, que choisir ?

Le service propose également une interface de connexion hébergée qui permet aux utilisateurs de se connecter facilement à travers un fournisseur d’identité externe. Cela se met en place facilement avec très peu de lignes de code.

Amazon Cognito: User Pool ou Identity Pool, que choisir ?

Cette interface peut être légèrement personnalisée avec l’ajout d’un logo, et la modification de certaines propriétés CSS. Son utilisation n’est cependant pas obligatoire :

  • Si l’utilisateur se connecte avec son email et mot de passe, vous pouvez le faire directement depuis votre frontend en utilisant Amplify (ou le SDK Cognito).
  • Vous pouvez faire en sorte que l’interface hébergée redirige directement vers le fournisseur d’identité de votre choix avec un paramètre dans l’URL. L’utilisateur a l’impression que votre application redirige directement vers le fournisseur d’identité mais en réalité, il y a Cognito entre les deux. Amplify gère cela très facilement.

La connexion renvoie un jeton JWT qui doit être utilisé dans les requêtes.

Il est très facile de sécuriser une API s’appuyant sur API Gateway avec un User Pool Cognito. Il suffit de créer un mécanisme d’autorisation et de l’appliquer aux routes à sécuriser.

Amazon Cognito: User Pool ou Identity Pool, que choisir ?

Sinon, il est possible de vérifier la validité d’un token JWT au niveau applicatif en vérifiant la signature et les claims (plus d’informations ici).

Dans de nombreuses situations, quand on parle de Cognito, on parle des User Pools Cognito. Mais il existe un autre sous-service : Identity Pool, qui a un objectif totalement différent.

Cognito Identity Pool

Cognito Identity Pool a une mission principale : transformer le jeton d’un fournisseur d’identité externe en clés d’accès temporaires IAM. AWS Identity and Access Management (IAM) est le service transversal de contrôle d’accès aux ressources AWS. Des clés d’accès permettent des actions sur des ressources AWS définies par le rôle associé aux clés.

On peut utiliser un jeton provenant :

  • D’un fournisseur d’identité public classique : Amazon, Apple, Facebook, Google et Twitter
  • D’un autre fournisseur d’identité utilisant le protocole OpenID ou SAML
  • D’un User Pool Cognito : on peut donc combiner les deux services

Ces accès permettent à l’application frontend d’accéder directement à des services AWS. Les permissions associées à ces accès temporaires sont définies par un rôle IAM. Elles peuvent être très fines car on peut y inclure l’identifiant de l’utilisateur. Si on le souhaite, on peut également donner des accès temporaires (avec des permissions différentes) à des utilisateurs non-authentifiés.

Le schéma suivant résume le fonctionnement d’un Identity Pool Cognito

Amazon Cognito: User Pool ou Identity Pool, que choisir ?

On peut citer un cas d’utilisation classique :  autoriser les utilisateurs à accéder à un répertoire qui leur est propre sur un bucket S3. Cela permet au frontend de l’application de mettre des fichiers directement sur S3, de manière sécurisée, sans passer par le backend.

On peut également autoriser l’accès à une API utilisant API Gateway via une permission IAM. Dans ce cas, il faut paramétrer l’API Gateway pour utiliser la méthode d’autorisation IAM (et non pas via Cognito, ce qui correspond à un User Pool).  Par contre ce service n’est pas fait pour sécuriser une application classique.

Il faut noter que pour avoir ces accès IAM, il faut déjà avoir implémenté dans le code un mécanisme pour obtenir un jeton d’un fournisseur d’identité. Cela implique en général plus de code au niveau du frontend de l’application.

Quand utiliser un Cognito User Pool seul ?

  • Vous avez besoin de mettre en place un mécanisme d’inscription et de connexion dans votre application et vous ne souhaitez pas tout développer.
  • Vous souhaitez profiter d’un panel de fonctionnalités liées à la sécurité.
  • Vous souhaitez mettre en place de l’authentification à travers des fournisseurs d’identité externes et vous préférez le faire sans écrire de code.
  • Vous souhaitez sécuriser une API Gateway ou votre backend.
  • Vous voulez avoir accès à la liste des utilisateurs.
  • Vous avez besoin d'un fournisseur d’identité (IDP) managé.

Quand utiliser un Cognito Identity Pool seul ?

  • Vous voulez autoriser l’accès à un ou plusieurs services AWS à vos utilisateurs.
  • Vous avez déjà un jeton d’accès provenant d’un fournisseur d’identité tiers dans votre application.
  • Ou vous souhaitez développer la connexion à ce fournisseur d’identité vous même.

Quand utiliser Cognito User Pool et Cognito Identity Pool ensemble ?

On peut effectivement utiliser un Cognito User Pool en tant que fournisseur d’identité dans un Cognito Identity Pool. Cela permet par exemple à un utilisateur d’un User Pool d’obtenir des accès temporaires AWS pour récupérer du contenu sur S3 de manière sécurisée.

Il y a un autre avantage à utiliser les deux services ensemble qui est un peu moins connu : avoir un semblant de Role Based Access Control (contrôle d’accès basé sur les rôles). Dans un User Pool, on peut définir des groupes. Chaque utilisateur peut appartenir à zéro ou plusieurs groupes. On peut assigner à chaque groupe un rôle IAM. Si le User Pool est utilisé dans un Identity Pool, lors de l’échange du token JWT par des accès temporaires IAM, l'utilisateur recevra des accès avec le rôle assigné au groupe et non pas le rôle par défaut de l’Identity Pool.

On peut par exemple définir le groupe Admin qui aurait un rôle IAM leur permettant cela :

  • Appel du chemin /admin sur une API Gateway
  • Ajout et récupération d’images dans un bucket S3

Le rôle “authenticated” de l’Identity Pool permet seulement aux utilisateurs classiques les actions suivantes :

  • Appel du chemin /user sur une API Gateway
  • Récupération d’images dans un bucket S3

Il faut noter qu’utiliser un Identity Pool n’est pas le seul moyen de profiter des groupes d’un User Pool pour faire du Role Based Access Control. Le groupe étant présent dans le token JWT, il est facile d’autoriser une requête au niveau applicatif en fonction du groupe de l’utilisateur. Dans le cadre de la sécurisation d’une API Gateway, il est également possible d’utiliser une fonction Lambda en tant que “Custom Authorizer” pour vérifier le groupe de l’utilisateur.

Je tiens cependant à ajouter que faire du RBAC sur une API Gateway n’est pas tout le temps une bonne idée et qu’il est souvent plus judicieux de mettre en place un contrôle d’accès plus fin au niveau applicatif.

Conclusion

Pour résumer :

  • Amazon Cognito User Pool vous permet de découpler la gestion des utilisateurs de votre application et vous permet de vous concentrer sur la valeur que votre application apporte. A cela s’ajoute de nombreuses fonctionnalités de sécurité. La connexion renvoie un jeton JWT.
  • Amazon Cognito Identity vous permet d’échanger un jeton d’un fournisseur d’identité externe (ou un User Pool) Cognito contre un des accès temporaires IAM. Permet à vos utilisateurs d’avoir accès directement à des services AWS.

Peu importe la configuration que vous choisirez, je vous conseille d’utiliser la librairie Amplify qui vous permettra de mettre en place Cognito sur votre frontend rapidement.

Au niveau de votre backend, vous pouvez placer votre sécurité au niveau d’une API Gateway si votre application le permet. Sinon, et si vous utilisez un User Pool, vous pouvez mettre en place votre sécurité directement dans votre code applicatif en utilisant une librairie qui implémente les spécifications JWT (et ce tutoriel).

Être aux commandes de son terminal (Partie 1 : Les outils pour être plus productif)

$
0
0
Être aux commandes de son terminal  (Partie 1 : Les outils pour être plus productif)

L’invite de commande est l’outil numéro 1 d’un ingénieur informatique. Même s’il n’est au premier abord qu’un outil secondaire pour la plupart des professionnels en début de carrière, il devient, petit à petit, un outil central de leur quotidien.

Disclaimer : Cet article s'adresse surtout aux débutants qui ne voient pas forcément toute l’ampleur que peut avoir une invite de commande dans leur environnement de travail. L’article n’a pas vocation à rentrer dans les détails.

Disclaimer 2 : Les différents exemples de cet article ont été produits depuis la version 32 de Fedora. Ces derniers doivent aussi fonctionner sur toutes les plus grandes distributions Linux et sur macOS. Pour les utilisateurs Windows, il faudra passer par le sous-système Windows pour Linux (WSL).

L’invite de commande chez un ingénieur junior

Généralement, à nos débuts, l’invite de commande sert avant tout à utiliser différentes CLI (Command Line Interface) pour interagir avec d’autres systèmes via des commandes. C’est par exemple le cas de git ou d’aws. À ce stade de l'évolution d’un ingénieur, votre terminal ressemble très certainement à ceci :

Être aux commandes de son terminal  (Partie 1 : Les outils pour être plus productif)

Je suis sur la distribution Fedora mais les informations sont généralement les mêmes à savoir le nom d’utilisateur, ici ddoamaral, ainsi que le chemin actuel dans lequel toute commande sera exécutée, ici représenté par un tilde (~). Tilde est un raccourci pour désigner le répertoire Home de l’utilisateur actuel. Dans mon cas, c’est en réalité juste un alias qui représente le chemin /home/ddoamaral/.

Plus le temps passe, plus l'expérience s’accumule et plus l’invite de commande d’un ingénieur devient une application importante et récurrente. Si récurrente qu’elle est aujourd’hui incluse dans la plupart des éditeurs de code. L’invite de commande se transforme alors en gestionnaire de fichiers et de tâches, en éditeur de code, en outil de monitoring. Elle reste très proche de la machine, à contrario des divers autres outils graphiques, ce qui en fait un outil plus fiable et customisable.

Aujourd’hui, nous allons voir certains de ces aspects qui peuvent vous rendre plus productif.

La trinité : Zsh, Oh my Zsh et PowerLevel10k

L’invite de commande peut être beaucoup plus qu’un simple outil pour lancer des commandes. Généralement, on fait un tel constat avec le besoin d’avoir des informations contextuelles pour une interface donnée. Voici certains exemples qui vont très certainement vous interpeller :

  • Git suppose un certain contexte à la racine d’un projet. Il pourrait être très intéressant de savoir en tout temps la branche dans laquelle nous sommes situés, le nombre de fichiers modifiés depuis le dernier commit, si oui ou non nous sommes situés dans un projet contenant le fameux “.git”, etc.
  • Les environnements virtuels Python. Ces derniers permettent d’importer des librairies Python et ainsi de pouvoir les utiliser dans un projet donné. Malheureusement, il arrive d’avoir plusieurs environnements pour plusieurs projets en parallèle, il est donc utile de savoir à tout moment lequel de ces environnements est actuellement actif.
  • Kubectl exige d’influencer depuis l'invite de commande des clusters Kubernetes définis par deux variables, le nom du cluster ainsi qu’un namespace. Ici encore on peut, sans le faire exprès, déployer des ressources dans le mauvais contexte et l’indiquer est donc un plus.

Il est évident qu’au cours de votre carrière d’ingénieur, vous avez fait/allez faire face à au moins un de ces cas. Fort heureusement, il existe des outils permettant de répondre à cette problématique. Aujourd’hui je vais vous parler d’un ensemble de technologies qui, selon moi, répondent parfaitement à ce besoin. Ainsi, ensemble construisons l’invite de commande 2.0.

Zsh

Zsh est la base de notre nouvelle invite de commande 2.0. C’est un shell, tout comme bash ou PowerShell. Il a comme principal avantage d’avoir une communauté incroyable ayant développé des outils très complets dont certains vont être présentés aujourd’hui.

Si vous ne l’avez pas encore installé, installez-le depuis cette adresse : https://github.com/ohmyzsh/ohmyzsh/wiki/Installing-ZSH

Une fois cela fait, on peut taper zsh dans l’invite de commande pour changer de shell ou encore chsh puis renseigner /bin/zsh pour faire de ce dernier votre shell par défaut.

Être aux commandes de son terminal  (Partie 1 : Les outils pour être plus productif)

Oh My Zsh

Zsh tout seul ne fait pas une grande différence. Il ajoute tout de même de nombreuses fonctionnalités dont certaines sont décrites ici. Il se base sur le même principe que Linux, à savoir commencer avec le strict minimum et par la suite sélectionner les outils qui nous importent.

Oh My Zsh facilite l’installation de ces derniers à travers deux composantes : les plugins et les thèmes. Pour l'installer, c’est tout aussi simple, il suffit de copier coller la commande suivante :

sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

Pour commencer à interagir avec le framework, nous allons tout de suite éditer le fichier .zshrc présent à la racine de notre utilisateur.

À l'intérieur de ce dernier se trouve toute notre configuration zsh. Il faut savoir que toutes les lignes que vous rajouterez dans ce fichier seront exécutées au démarrage de chaque nouvelle invite de commande. Ainsi, si vous rajoutez un echo "Hello ZSH !" au lancement vous obtiendrez une jolie “Hello ZSH !”. Cela vous permet de mettre en place certaines variables d’environnements par exemple. Ainsi, si vous stipulez la ligne suivante : export ENV=1234, vous pouvez utiliser la valeur de la variable d’environnement n’importe où. Les alias sont aussi très intéressants pour gagner du temps. J’utilise par exemple ce dernier pour jump de mon terminal à ma fenêtre visual studio code : alias code.="code . && exit".

On va tout d’abord s'intéresser aux plugins ici. Ces derniers permettent d'ajouter des fonctionnalités à votre console. Dans le fichier .zshrc, on remarque déjà un plugin installé : le plugin git. Ce dernier rajoute énormément d’alias pour gagner du temps lorsqu’on utilise le CLI git. Pour en savoir plus, voici la liste exhaustive de ces derniers : https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/git. Bien évidemment, on peut ici en rajouter beaucoup plus, qui vont tous rendre votre expérience avec l’invite de commande plus agréable. Ainsi, je ne saurais trop vous conseiller de rajouter au moins les suivants :

  • autojump → il permet de voyager vers un dossier souvent utilisé sans devoir taper tout le chemin vers ce dernier. Ainsi, si vous accédez souvent à un dossier qui se trouve à ce chemin : a/b/c/d/my-project vous pourrez y accéder via j my-project et pourquoi pas ensuite enchaîner avec votre alias code. pour directement ouvrir votre projet dans visual studio code.
  • zsh-completions →  il permet une auto complétion plus que plaisante. La complétion est l’art de proposer à l’utilisateur les différentes possibilités qu’il peut entrevoir lorsqu’il tape une commande en particulier. Par exemple, si on tape la commande cd avec un espace on s’attend par la suite à avoir besoin d’un dossier se trouvant à la racine dans laquelle se trouve l’invite de commande. Pour savoir quels dossiers sont disponibles, on peut tout d’abord lancer la commande ls puis taper le nom précédemment inscrit ou alors on peut saisir la commande cd suivie d’un espace puis appuyer sur la touche SHIFT pour se laisser guider par la console qui va successivement vous présenter toutes les possibilités disponibles. Le plugin en plus de cette fonctionnalité native ajoute le même fonctionnement avec les makefiles, git, etc.
  • zsh-autosuggestions → à l’instar de gmail et de sa complétion automatique, l’invite de commande vous proposera la commande qui lui est la plus pertinente. Une image vaut mieux que mille mots :
Être aux commandes de son terminal  (Partie 1 : Les outils pour être plus productif)

Il existe pléthore d’autres plugins, spécifiques à un certain CLI, ajoutant généralement deux choses : la complétion et des alias. Ainsi si vous utilisez maven, il y a le plugin mvn. Si vous utilisez Kubernetes, il y a le plugin kubectl, etc.

Maintenant que notre invite de commande est intelligente, nous allons la rendre belle et encore plus utile ! C’est à ce moment-là qu’entre en jeu les thèmes et plus particulièrement PowerLevel10k.

PowerLevel10k

On n’oublie pas ce qui nous a amené jusqu’ici, le besoin d’avoir affiché constamment sur notre console des contextes particuliers. On veut savoir sur quelle branche on se trouve lorsqu’on est dans un projet git, on veut savoir dans quel cluster et dans quel namespace on se trouve si on manipule des clusters Kubernetes, etc. Tout ceci va être possible grâce à PowerLevel10k.

Rendez-vous sur ce lien : https://github.com/romkatv/PowerLevel10k.

Protip : n’oubliez pas d’installer aussi la font “meslo-nerd-font-patched-for-PowerLevel10k” pour une expérience utilisateur agréable.

La première fois que vous lancerez votre nouvelle console, vous aurez à customiser votre invite de commande. Si jamais cela ne fonctionne pas, veuillez saisir la commande p10k configure. Dans le cas où vous voulez la même configuration que celle qui va suivre, il vous suffit de dire “yes” pour tous les symboles (normalement il n’y aura pas de problème avec la bonne police d’écriture) puis sélectionnez lean(1) → Unicode(1) → 256 colors(1) → No (1) → Two lines (2) → Disconnected (1) → No frame (1) → Sparse (2) → Many icons (2) → Concise (1) → Yes (y).

Normalement, vous obtiendrez quelque chose de la sorte :

Être aux commandes de son terminal  (Partie 1 : Les outils pour être plus productif)

La console n’est plus du tout la même et ce, pour le plus grand plaisir de nos yeux ! Si on décortique les informations ci-dessus, pour l’instant on n’a pas grand chose. On a d’abord un logo, ici Fedora car je suis sur cette distribution. On a ensuite deux segments, chaque segment est généralement caractérisé par deux composants, une icône et une information. Ici, le chemin dans lequel nous nous trouvons à gauche est représenté par une maison et le tilde.

Maintenant plaçons-nous dans un projet utilisant le système git ainsi qu’un environnement virtuel Python particulier. Une fois à l'intérieur, voici à quoi devrait ressembler votre console :

Être aux commandes de son terminal  (Partie 1 : Les outils pour être plus productif)

Le voilà le contexte que l’on voulait tant depuis le début. Ici nous avons beaucoup plus d’informations sur notre projet en cours, à savoir l’environnement virtuel utilisé actuellement “article” représenté avec l'icône Python ainsi que les informations sur git, à savoir que l’on est sur la branche master et que la branche a 1 fichier ajouté. Cela fait gagner énormément de temps, plus besoin de git status ou autre !

Il nous reste un dernier contexte que l’on avait mentionné dans l’introduction, le contexte sur le cluster Kubernetes en cours. Il faut taper la commande kubectl pour le voir s’afficher. Vous verrez ainsi apparaître la fameuse barre suivante du nom du cluster, ici le cluster à noeud unique en local minikube. Normalement, ce dernier est succédé d’un “/” suivi du nom du namespace mais comme ce dernier est “default” il est omis par PowerLevel10k.

Être aux commandes de son terminal  (Partie 1 : Les outils pour être plus productif)

Un champ de possibilités infini

Cette suite d’outils est selon moi un très bon point d’entrée pour faire connaissance avec votre invite de commande et apprendre à l’apprécier à sa juste valeur. On pourrait bien entendu ne pas s'arrêter là, parler du fuzzy finder, mentionner Vim et son univers bien à lui, parler de taskwarrior qui est selon moi un des meilleurs gestionnaires de tâches, introduire tmux pour avoir plusieurs fenêtres dans la même console. Si vous êtes intéressés par ces derniers, veuillez vous référer à la partie “pour aller plus loin”. Les possibilités sont infinies.

PowerLevel10k est une des pierres angulaires de cette trinité, de plus, il est très facilement customisable. Ainsi nous allons dans une deuxième partie créer nos propres segments PowerLevel10k pour avoir toujours plus d’insight (ce serait vraiment bien de pouvoir monitorer les ressources de son compte AWS depuis notre console 😉).

Pour aller plus loin

A la découverte de GitHub Codespaces

$
0
0
A la découverte de GitHub Codespaces

Dévoilé lors de GitHub Satellite le 7 mai dernier, le service GitHub Codespaces permet aux développeurs de créer un environnement de développement VS Code depuis un repository de façon instantanée. Grâce aux containers de Microsoft vous n’aurez plus besoin de configurer vos dépendances, votre Codespace vous attend, le tout en deux clics !
Avec cette nouvelle fonctionnalité, actuellement en bêta publique, l’ambition de Codespaces est claire : Capitaliser sur le succès de VS Code pour proposer un environnement cloud familier face à ses concurrents, dont AWS, GitLab et Gitpod.

Initialement connu sous le nom de Visual Studio Codespaces sur Azure, Microsoft a annoncé que le service existant deviendra une partie intégrante de la plateforme GitHub début septembre 2020. Les principaux arguments de vente sont les suivants :

  • Créer un environnement unique pour chaque étape du processus de développement, le tout dans un navigateur.

  • Faciliter l’onboarding de nouveaux membres de l’équipe en donnant simplement accès à un repository.

  • Permettre Développement depuis tout type d’appareil, d’OS et navigateur.

  • Gérer de multiples dépendances lorsque l’on est associé à plusieurs projets en même temps, de façon simplifiée. Les contributions open source peuvent être réalisées de façon simplifiée.

Avec une base d'utilisateurs large et des améliorations récentes telles que GitHub Actions l'année dernière, la nouvelle offre Codespaces annonce ses ambitions pour être un pionnier dans le développement dans le cloud. Mon expérience utilisateur a été positive durant la bêta, et je voudrais la partager avec vous aujourd’hui. Dans cet article nous allons étudier les fonctionnalités phares, les possibilités de personnalisation et comparer la solution face à Cloud 9 d’AWS, afin de déterminer si cette nouvelle solution présente véritablement des avantages à tous les développeurs.

Création d'un Codespace

Pour cet article, j'ai créé un simple repository contenant une application JHipster, nécessitant de multiples dépendances. Le lancement d'un nouveau Codespace se fait simplement en cliquant sur "Ouvrir avec Codespaces" dans le menu déroulant "Code" de la page du repository. Aucune autre configuration n'est nécessaire et vous serez redirigé vers un environnement VS familier en moins de 60 secondes avec la configuration dont vous aurez besoin.

A la découverte de GitHub Codespaces

Pour les habitués de VS Code, vous y trouverez toutes les mêmes fonctionnalités qu’une installation locale, dont les nouvelles fonctionnalités Remote, le live reloading, le terminal intégré.
Le port-forwarding est également intégré lorsque vous essaierez d'exécuter des processus. Dans cet exemple JHipster, lors de l'exécution d'un build, Codespaces détecte une combinaison ‘localhost:port’ et l'adresse sera correctement redirigée :

A la découverte de GitHub Codespaces

Codespaces ouvre également la voie à une utilisation de VS Code sur n'importe quel appareil. Étant conçu pour fonctionner dans tout type de navigateur, il permet aux utilisateurs de se connecter à un Codespace sur un appareil mobile, tel qu'une tablette.
Les fonctionnalités à distance et le partage en direct vous permettront de faire des modifications même lorsque vous ne pouvez pas avoir accès à votre machine de développement habituelle.

Docker est fourni dans la configuration par défaut et permettra aux utilisateurs de construire rapidement leurs images. Dans cet exemple de repository, j'ai un Dockerfile pour une simple application React. J'ai pu construire l'image et grâce à la redirection de port, je peux exécuter l'image pour la tester et la pousser vers Docker Hub.

A la découverte de GitHub Codespaces

Personnalisation de votre Codespace

L'image de conteneur par défaut, basée sur Debian, convient pour une utilisation générale dont la plupart des utilisateurs pourront être satisfaits. Plusieurs langues et frameworks sont préinstallés et prêts à l'emploi.

Un fichier docker personnalisé peut également être créé afin d'avoir une image plus épurée, ou répondre à un cas d'utilisation spécifique.. Un certain nombre de Dockerfiles ont été créés par l'équipe VS et d'autres contributeurs, et sont facilement disponibles sur leur repository.

Lors de la création d'un nouveau Codespace, GitHub vérifie si le repository dispose d’un dossier ‘.devcontainer’ contenant des paramètres personnalisés. Si aucun dossier n'est trouvé, il chargera l'image standard contenant tous les runtimes. Pour cet article, nous allons créer une configuration pour un simple projet Python.

Nous devrons créer deux fichiers, le premier étant notre Dockerfile. J'ai décidé d'utiliser l'image Ubuntu de Microsoft ce fois-ci et d'installer la dernière version de Python pour obtenir une image plus légère :

FROM mcr.microsoft.com/vscode/devcontainers/base:ubuntu

# Run updates
RUN apt update -y
RUN apt install sudo -y

# Install Python and Pip
RUN sudo apt install -y python3 python3-pip

Pour la deuxième étape, nous devrons créer un fichier devcontainer.json qui sera stocké dans le même dossier que notre Dockerfile for Codespaces pour savoir quelle configuration charger. Nous pouvons également spécifier certains paramètres supplémentaires, comme nous le ferions dans nos environnements locaux, tels que les thèmes, les extensions ou les ports à exposer.

{
    // Nom de container personnalisée

    "name": "Alex's Ubuntu and Python 3 Configuration",

    "build": {

    // Nom du Dockerfile à utiliser
    
    "dockerfile": "alexubuntupy.Dockerfile",

    // Une alternative est de faire un pull d'image sur Docker Hub
    
    // image: "repo/image:tag"

    "remoteUser": "codespace",
    "workspaceMount": "source=${localWorkspaceFolder},target=/home/codespace/workspace,type=bind,consistency=cached",
    "workspaceFolder": "/home/codespace/workspace",
    "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ],
},

	// Réglages du fichier settings.json lié au conteneur à définir à la création

	"settings": { 
		"terminal.integrated.shell.linux": "/bin/bash",
		"python.pythonPath": "/usr/local/bin/python",
		"python.linting.enabled": true,
		"python.linting.pylintEnabled": true,
		"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
		"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
		"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
		"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
		"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
		"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
		"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
		"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
		"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint",
		"workbench.colorTheme": "Default Dark+"
	},
	
	// ID des extensions à installer lors de la création


	"extensions": [
                "ms-python.python", 
                "coenraads.bracket-pair-colorizer",
	]

}

Ici, j'ai décidé de définir quelques paramètres par défaut pour Python, d'installer deux extensions et de changer mon thème. Après avoir fait le commit des fichiers, nous obtenons quelque chose comme ceci :

A la découverte de GitHub Codespaces

Comme on peut le voir, le profil que nous avons créé nous permet d'exécuter un simple script Python dans le terminal intégré. Les temps de chargement sont considérablement réduits et la configuration est réutilisable ou peut être facilement modifiée, par exemple en spécifiant une version différente de l'interpréteur Python.

Dans le cas où nous disposons de plusieurs repositories, nous pouvons rapidement changer de configuration sans compliquer nos installations locales, en particulier pour les projets où nous ne souhaitons apporter uniquement de petites modifications. Les paramètres des extensions et des thèmes peuvent également être synchronisés sur tous vos repositories si vous le souhaitez.

Comparaison avec AWS Cloud 9

Le principal concurrent de Codespaces sur le marché des environnements de développement dans le Cloud est Cloud 9 d'AWS. Lancée en 2017, elle offre des fonctionnalités similaires aux utilisateurs et est fortement intégrée à l'écosystème d'Amazon. Pour mieux comparer les deux solutions nous pouvons nous intéresser sur les points suivants : performances, fonctionnalités, facilité d'utilisation et prix.

Performance

Trois configurations seront disponibles pour la version publique de Codespaces, à savoir :

  • Basic (2 vCPU, 4GB memory, 32GB SSD)

  • Standard (4vCPU, 8GB memory, 32GB SSD)

  • Premium (8vCPU, 16GB memory, 32GB SSD)

Le programme bêta actuel utilise la configuration de base et, pour les cas d'utilisation simples, peut prendre en charge les tâches présentées dans les sections précédentes de cet article. Il reste à savoir si cette configuration est suffisante pour une installation à plein temps. Microsoft semble recommander la configuration Standard pour dans ce cas dans la version actuelle sur Azure.

AWS Cloud 9, d'autre part, fonctionne avec les types d'instance T, C ou M. Cela offre aux utilisateurs beaucoup plus de possibilités. Microsoft indique qu'elle pourrait proposer d'autres configurations à l'avenir dans sa roadmap.

Caractéristiques

En comparant les caractéristiques, nous pouvons constater que les deux offres sont très similaires. La première étant les fonctionnalités de réduction des coûts, qui par défaut, suspendent les instances après 30 minutes d'inactivité. Les instances peuvent également être configurés pour une sauvegarde automatique, de manière à ne perdre aucune modification.
Docker est également pré-installé, ce qui est un avantage pour les deux parties. Un minimum d'espace disque est requis pour la build d’images Docker sur l'AWS. La documentation de Codespace n'indique pas de contraintes, mais les premiers retours des utilisateurs suggèrent que la construction d'images plus larges semble poser soucis.
La bibliothèque d'extension disponible sur Codespaces offre de nombreux possibilités aux utilisateurs, alors que Cloud 9 ne dispose pas de telles fonctionnalités, ce qui limite les outils de productivité disponibles pour les développeurs.

Les deux solutions sont fournies avec des runtimes pré-installés pour plusieurs langages. Comme indiqué précédemment, tous les principaux runtimes sont disponibles dans les deux solutions. Cependant, l'avantage de Microsoft semble être les aspects de personnalisation, qui permettent aux utilisateurs de créer des images comme nous avons pu le faire précédemment. Cloud 9 n'offre pas la possibilité de supprimer des langues et votre environnement peut avoir des langues dont vous n'avez pas besoin. En effectuant des tests pour cet article sur Cloud 9, j'ai découvert à ma grande surprise que Go et PHP étaient installés, et qu'aucune option de configuration n'était disponible pour sélectionner uniquement ce dont j'avais besoin.

Facilité d'utilisation

Les utilisateurs seront sans doute plus familiers avec VS Code et n'auront aucun mal pour commencer à travailler dans leur nouvel environnement. L'offre de Cloud 9, basée sur Ace Editor, offre moins de fonctionnalités que son rival. La configuration par défaut est sans doute très simple sur les deux plateformes, mais la possibilité de charger la configuration par défaut de Microsoft est une fonctionnalité que beaucoup apprécieront.
La facilité de mise en oeuvre, les intégrations aux autres produits, font de Codespaces un produit intéressant pour encourager les utilisateurs à utiliser les produits Microsoft sur l’ensemble de leur projet, de l'initiation d'un projet jusqu'à son déploiement (Initiation sur GitHub, développement sur Codespaces, CI/CD avec GitHub actions et Azure).

Prix

Si l'on compare le prix des deux solutions, on constate que la solution de Microsoft, à première vue est légèrement plus chère que son homologue AWS, lorsque l’on travaille avec la configuration basique. Pour une utilisation quotidienne, soit environ 100 heures de travail au total au cours du mois, soit les options Standard et Premium, le prix est équivalent entre les deux solutions. Le tableau suivant compare les prix pour le cas d'utilisation mensuelle avec des spécifications d'instance identiques. La tarification des Codespaces Github semble être compétitive, compte tenu des fonctionnalités qu'elle offre. Il reste à savoir si une offre gratuite sera disponible. Dans le cas de projets open source populaire, une formule gratuite pourrait encourager les contributions grâce à tous les avantages que nous avons couverts dans cet article.

Instance parameters (100 hours) GitHub Codespaces (monthly cost) AWS EC2 (monthly cost)
Codespaces Basic (2 cores, 4GB RAM, 32 GB SSD) vs EC2 t3.medium $8.50 $4.16
Codespaces Standard (4 cores, 8 GB RAM, 32 GB SSD) vs EC2 c5.xlarge $16.90 $17.00
Codespaces Premium (8 cores, 16 GB RAM, 32 GB SSD) vs EC2 c5.2xlarge $33.90 $34.00
Storage fees (Monthly)
10GB storage $1.00 $1.00
Total monthly cost
Basic $9.50 $5.16
Standard $17.90 $18.00
Premium $34.90 $35.00

Conclusion

Microsoft semble être très clair sur ses ambitions avec Github Codespaces. La version bêta actuelle est pleine de fonctionnalités que les développeurs seront ravis de ne pas avoir à configurer. L'environnement de travail familier dans VS Code, est un argument de poids par rapport à d'autres solutions, comme Cloud 9. Les Codespaces ont la capacité d'encourager un grand nombre d'utilisateurs à commencer à développer dans des environnements dans le cloud. Ils peuvent également encourager les utilisateurs à utiliser d'autres services GitHub tels que Actions voire Azure. Les barrières à l’entrée pour contribuer aux projets open source peuvent être facilement contournées, au travers l’image standard.
Cependant, le prix semble être l’un des seuls défauts à reprocher, AWS Cloud 9 par exemple, restent moins chères pour une utilisation basique. Des solutions telles que Gitpod, propose une solution on-premise, qui n'est pas encore dans la roadmap de Microsoft au moment de la rédaction de cet article.
Depuis le début de la beta, je me suis tourné vers plusieurs Codespaces. Lorsque j’étais éloigné de mon poste de travail standard, j’ai pu continuer où je m’étais arrêté sans avoir à installer ou configurer mon environnement. Naturellement je m’oriente vers cette solution pour des projets personnels désormais, et que je suis prêt à adopter après la phase de beta.

Si vous souhaitez en savoir plus sur Github Codespaces, vous pouvez rejoindre le programme bêta.

Soldats, sécurisez la plateforme Data. Reçu fort et clair, terminé !

$
0
0
Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

De quoi allons nous parler ? De sécurité dans un environnement Data.
Qu’elle soit big, small, fast, slow,… tout est Data !

Dans ces environnements Data, de plus en plus répandus dans les entreprises, la sécurité est au centre de toutes les attentions.

Selon l’Agence Nationale de la Sécurité des Systèmes d’Informations : “59% des entreprises déclarent que les cyberattaques ont eu un impact sur leurs activités”. Ajoutons à cela les enjeux de la CNIL (Commission nationale de l'informatique et des libertés) sur le droit à la protection des données à caractère personnel (RGPD) et ses sanctions pouvant s’élever jusqu’à 20 millions d’euros ou dans le cas d’une entreprise jusqu’à 4 % du chiffre d’affaires annuel mondial.
La note salée peut freiner ou éteindre beaucoup d’initiatives voire mettre un terme à vos ambitions entrepreneuriales.

Dans les faits, je croise régulièrement des clients, des développeurs Data, des architectes ou C-level qui ont une vision incomplète de la sécurité dans ce type d’environnements.

Cet article a pour but de remettre l’église au centre du village et d’essayer, dans une moindre mesure, de dresser le portrait de ce qu’on peut attendre d’un environnement Data sécurisé.

Les objectifs de la protection des données : CIAA

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !
Fig. 1. CIAA les objectifs de la protection des données

La principale motivation de la sécurité de l'information est de garantir la confidentialité, l'intégrité, l’authenticité et la disponibilité de vos environnements.

Le principe de confidentialité est de protéger contre tout accès non autorisé. C’est un enjeu majeur quand on parle de données personnelles dont la protection est requise par la RGPD. Mais les entreprises ont également intérêt à ce que les données sensibles ne tombent pas entre les mains de la concurrence. La confidentialité des informations peut être assurée par l'attribution appropriée d'autorisations en rapport avec les procédures d'authentification et de chiffrement.

Le terme intégrité fait référence à l'exactitude des données, dans le sens où les données sont à la fois complètes et inchangées. D’un côté l’intégrité fait référence à la connaissance et le traçage de la donnée, d’un autre côté elle fait référence au bon fonctionnement des systèmes informatiques.

Les données, les systèmes informatiques et les applications sont disponibles s'ils sont accessibles aux utilisateurs et fonctionnent comme prévu. La défaillance d’un serveur ou le chiffrement malveillant de vos données par un cybercriminel représentent des violations de la disponibilité. Les restrictions de disponibilité peuvent à bien des égards entraîner des pertes financières et de réputation pour les entreprises.

Une personne est authentique si son identité et ses déclarations sur son identité correspondent. Par exemple, en échangeant l’auteur d’un message, on perd son authenticité. Avant que l'accès ne soit accordé, un utilisateur doit pouvoir s'authentifier.

Les 5 piliers d’un environnement Data sécurisé

Je vous propose d’orienter cette étude autour de 5 piliers :

  1. Le management de la donnée,
  2. Le management des accès et des identités (IAM),
  3. La protection et la confidentialité de la donnée,
  4. La sécurité du réseau,
  5. La sécurité et l’intégrité de l’infrastructure.

Ces 5 piliers sont divisés en sous-sections, toutes critiques afin d’assurer la sécurité de votre environnement et d’atténuer les risques. Ils forment ensemble ce que je vous propose d’appeler un  “Framework de sécurité” qui se présente ainsi :

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !
Fig. 2. Framework de sécurité d’un environnement Data

Rentrons maintenant dans le détail de ce framework.

Si vous pensez avoir mis en place toutes ces briques sur votre environnement, ne lisez pas la suite, je ne souhaite pas vous frustrer. Si vous avez des doutes ou si rien n’est entrepris encore, foncez !

Data Management

Le Data Management est une discipline qui tend à valoriser la donnée en tant que ressource numérique. Dans un contexte RGPD tendu, la connaissance approfondie (classification, cartographie et traçabilité) de ces données devient un enjeu majeur.

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

Une classification efficace de la donnée est sûrement une des composantes les plus importantes pour appliquer un contrôle efficace de la donnée dans un environnement Data. Quand une entreprise traite une grosse quantité de données, il est important d’identifier quelles données sont importantes, quelles données doivent être chiffrées, quelles données méritent une protection accrue. Cette étape demande une collaboration avec les entités légales, finance, propriété intellectuelle et sécurité de votre entreprise afin de connaître les données que vous manipulez.

Sur ces données :

  • vous réaliserez un contrôle de sécurité (emplacement de la donnée, chiffrement appliqué, cartographie des accès utilisateurs et systèmes),
  • vous évaluerez la valeur de ces données en cas d’attaque (données sensibles, valeur sur le marché noir, propriété intellectuelle),
  • vous déterminerez la conformité et l’impact sur les revenus,
  • vous étudierez l’impact des données sensibles sur le propriétaire (le client par exemple) de ces données.

Par cet exercice, en premier lieu, vous aurez une meilleure visibilité de vos données et vous pourrez construire aisément une roadmap de mise en place d’un environnement sécurisé.

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

Le manque de connaissance de vos données expose l’entreprise à des risques de sécurité ou de redondance de l’information. Il est alors nécessaire de cartographier finement où se trouvent les données sensibles dans votre écosystème et par conséquent appliquer les mesures de protections appropriées comme le masquage, la documentation, la tokenization ou encore le chiffrement.

La découverte vous assurera une meilleure compréhension de ces données en :

  • définissant et validant les données et leurs schémas,
  • collectant les métriques (count, unique count) afin de déterminer si la donnée est dupliquée par exemple,
  • étiquetant les données sensibles (pour les données structurées, un étiquetage des données sensibles par champ/colonne peut être très utile).

Par ce biais, vous pourrez alors partager vos résultats avec vos Data Scientists afin de mettre en place des modèles d’analyse de menaces ou encore évaluer l'intérêt d’utiliser des formats de données portant leurs schémas (Apache Parquet par exemple). Vous pourrez également apprendre de vos expériences réussies dans le chiffrement ou la tokenisation de données hautement sensibles et l’appliquer à tout votre environnement.

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

Le data tagging permet de comprendre le flux de vos données de bout en bout en apportant une attention particulière à vos sources et vos destinations. Il est le socle pour construire une solution de Data Lineage.

Par un suivi fin de la donnée, le tagging apporte de la visibilité aux données et améliore leur compréhension.

Le balisage de vos données vous permettra alors :

  • d'assurer la réutilisabilité de vos données et éviter la duplication de vos travaux,
  • de connaître le cheminement de vos données et par quels processus elles sont manipulées afin d’identifier les impacts d’un changement de modèle par exemple,
  • d'aider à identifier les données personnelles sensibles afin que l'accès puisse être correctement géré,
  • d'aider à signaler et à filtrer les données éthiquement douteuses ou autrement questionnables avant qu'elles ne soient utilisées dans la prise de décision ou dans des solutions d'intelligence artificielle.

IAM (Identity and Access Management)

La Gestion des Identités et des Accès est l’ensemble des processus mis en œuvre par une entité pour la gestion des habilitations de ses utilisateurs à son système d’information. Il s’agit donc de gérer qui a accès à quelle information à quel moment.

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

L'authentification est le processus permettant de déterminer si l'identité des utilisateurs, services, et les hôtes sont ceux qu'ils prétendent être. Le processus d’authentification est généralement basé sur un système d’utilisateur/mot de passe.

Dans les systèmes informatiques, l'authentification est différente de l'autorisation, elle vérifie simplement que l'entité est celle qu’elle prétend être mais ne donne pas de droits d'accès aux objets système. L'authentification et l'autorisation doivent fonctionner en tandem pour assurer une sécurité efficace.

Dans les entreprises, il est fréquent de rencontrer le terme de Single-Sign-On (SSO) permettant d’accéder à plusieurs services en ne procédant qu’à une seule authentification.

Il existe un niveau de sécurité supérieur à la simple authentification, l’authentification multifactorielle (Multi-Factor Authentication ou MFA) qui est une méthode d'authentification électronique dans laquelle un utilisateur n'a accès à une application ou un service qu'après avoir présenté avec succès deux ou plusieurs éléments de preuve (ou facteurs) à un mécanisme d'authentification :

  • connaissances (ce que seul l'utilisateur sait),
  • possession (quelque chose que seul l'utilisateur possède),
  • héritage (quelque chose que seul l'utilisateur est).

Par ce biais, ce mécanisme protège l'utilisateur contre une personne inconnue qui tente d'accéder à ses données.

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

L'autorisation est le processus permettant de déterminer quelles permissions une personne est censée avoir sur des données, des services ou des systèmes.

Dans les systèmes informatiques multi-utilisateurs, la gouvernance définit les utilisateurs autorisés à accéder au système, ainsi que les privilèges d'utilisation auxquels ils sont éligibles (par exemple, accès aux répertoires de fichiers, heures d'accès, quantité d'espace de stockage alloué).

L'autorisation peut être considérée comme à la fois le paramétrage préliminaire des autorisations par un administrateur système et la vérification de ces autorisations lorsqu'un utilisateur obtient l'accès. L'autorisation est généralement précédée d'une authentification.

Il est conseillé de définir finement les autorisations au niveau des services, du réseau, des serveurs, des frameworks mais aussi au niveau des bases de données, des tables ou des vues. Certaines solutions permettent même d’appliquer des autorisations au niveau des lignes de vos bases de données ou même de vos champs.

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

Afin de rendre aisée et cohérente l’attribution des habilitations, il convient de définir certains mécanismes. On parle alors de modèles d’habilitation. Ces modèles d’habilitation permettent de garantir, au sein de votre environnement, un contrôle d’accès efficace sur les ressources et les services IT. Ils permettent également de rationaliser le processus de demande d’accès des utilisateurs.

Si je devais vous en présenter deux, je vous parlerais des contrôles d’accès basés sur les attributs (ABAC) et les rôles (RBAC).

Dans le premier modèle (ABAC), les droits d’accès sont accordés aux utilisateurs grâce à l’utilisation de règles combinant des attributs (attributs d’utilisateur, de ressource, d’environnements, etc.). Il permet de gérer les accès très finement en appliquant une logique booléenne. Cependant, attention, il est très complexe à mettre en place, je le déconseille aux néophytes.

Dans le deuxième modèle (RBAC), l’accès aux ressources du système d’information s’appuie sur des rôles tels qu’ils sont définis dans l’organisation de votre environnement. L’accès aux objets se fait via le rôle de l’utilisateur et des règles qui lui sont appliquées. C’est le plus utilisé par les entreprises mais il est moins dynamique. Ajouter un accès revient à modifier un rôle et donc autoriser des accès à toutes personnes assumant ce rôle.

Ces dernières années, nous avons vu émerger la stratégie de sécurité nommée “Zero Trust”. Chaque élément de votre environnement se voit allouer un niveau de confiance de zéro (d’où le zéro trust). Ce modèle se base sur le concept de micro-segmentation qui traite chaque connexion vers chaque application comme un environnement distinct, avec ses propres exigences de sécurité, totalement transparente pour l’utilisateur. Par définition, la micro-segmentation considère chaque paire utilisateur-ressource comme indépendante, à la fois de l’origine de la connexion, mais aussi des autres connexions applicatives qui pourraient être actives sur le même terminal. S’appuyant sur un modèle Policy Based Access Control (PBAC), nous atteignons là un niveau élevé de sécurité, de granularité mais également de complexité.

Data Protection & Privacy

La confidentialité et la protection des données sont deux problématiques de gouvernance interdépendantes. Par la protection des données, nous allons assurer leur confidentialité. La mise en œuvre de la sécurité et la confidentialité des données de l’entreprise fait partie intégrante de votre stratégie.

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

Le data masking ou anonymisation est typique dans la gestion des données sensibles et PII (Personally Identifiable Information). Il existe deux approches :

  • l’anonymisation en amont afin d’assurer qu’aucune donnée sensible n’est stockée dans votre environnement,
  • l’anonymisation dynamique où la donnée est stockée complète mais masquée à la volée en fonction du rôle de l’utilisateur connecté.

Cette étape va de pair avec l’étape de Data Tagging et permettra de garder, en cas d’audit, certaines briques de votre environnement en dehors du scope d’intervention.

L’anonymisation consiste à remplacer les données sensibles par des données fictives là où ces données sensibles ne sont pas utiles. A partir du moment où les informations privées (nom, prénom, adresse, etc.) ne sont plus utiles, masquez les ou supprimez les.

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

La tokenization a pour but de substituer une donnée par une valeur aléatoire unique, nommée token. Cette pratique permet, entre autres, de supprimer les PII facilement en supprimant toutes les données référencées par cette clé.

Lors de la tokenization, un serveur stocke les relations entre les valeurs d'origine et de jeton. Lorsqu'une application a besoin des données d'origine, le système recherche la valeur du jeton (token) pour la récupérer.

Ce chiffrement permet d’offrir une excellente granularité de sécurité mais se fait au détriment d'une intervention manuelle pour déterminer les champs qui nécessitent d’être chiffrés et où et comment autoriser le déchiffrage.

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

Ce n’est pas la première fois que j’utilise ce mot, il est temps de le mettre en lumière : le chiffrement. Nous allons parler du chiffrement au repos ou at-rest.

Le chiffrement au repos, c’est le chiffrement au niveau du disque. Il consiste à protéger ces données, une fois stockées, c’est à dire les rendre inexploitables en cas de vol puisque impossible à déchiffrer.

On utilise une clé de chiffrement pour chiffrer la donnée et une clé de déchiffrement pour la déchiffrer. Lorsque ces deux clés sont identiques, on parle de chiffrement symétrique. A contrario, lorsqu’elles sont différentes, on parle de chiffrement asymétrique : une paire composée d'une clé publique, servant au chiffrement, et d'une clé privée, servant à déchiffrer.

La protection de vos données peut également être assurée par un chiffrement d’enveloppe qui consiste à chiffrer des données à l'aide d'une clé DEK (Data Encryption Key), puis à chiffrer la clé DEK avec une clé racine que vous pouvez intégralement gérer.

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

La prévention des pertes de données (DLP) est une stratégie qui empêche les utilisateurs d'envoyer des informations sensibles ou critiques en dehors du réseau de l'entreprise. Le terme décrit également les logiciels qui aident les administrateurs de réseau à contrôler les données que les utilisateurs envoient.

Le but d’une telle solution est d’identifier les flux par lesquels peuvent transiter les données, et dans ces flux identifier les champs, données, formules, données personnelles en mouvement.

Network Security

Plus on descend dans ce framework, plus on s’éloigne de la donnée (enfin, pas totalement vu que tout est Data). Données ou pas, RGPD ou pas, la sécurité du réseau est une composante bien connue des systèmes sécurisés assurant l’intégrité de votre réseau.

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

Quand on parle de chiffrement au niveau réseau, on parle de chiffrement en transit ou in-transit. Ce chiffrement est nécessaire pour éviter les interceptions de messages qui transitent sur le réseau. On utilisera des protocoles cryptographiques qui chiffrent les données et authentifient une connexion lors du transfert comme TLS ou SSL.

Nous connaissons le protocole HTTPS (HyperText Transfer Protocol Secure) quand on est sur notre navigateur web, ce n’est rien de plus que le protocole HTTP appliquant un layer de chiffrement sécurisé SSL ou TLS.

Dans un objectif d’environnement sécurisé, ces protocoles doivent être appliqués à la fois lors des communications client-cluster mais également cluster-cluster, c'est-à-dire à l'intérieur de notre environnement, entre nos services.

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

Une security zone est une partie d’un réseau (un sous-réseau ou subnet) qui répond à des exigences de sécurités spécifiques définies. Chaque zone se compose d'une seule interface ou d'un groupe d'interfaces, auxquelles une politique de sécurité est appliquée (network policy).

Le trafic est autorisé ou refusé en fonction d'un ensemble prédéterminé de règles appelé liste de contrôle d'accès (ACL).

Par exemple, c’est le travail d’un firewall qui doit être résistant aux attaques et doit être en mesure de filtrer le trafic.

Ce découpage du réseau en sous-réseaux implémentant des politiques spécifiques est primordial. Il permet, par exemple, de n’autoriser les accès à internet qu’aux services nécessitant cette connexion et limiter l’exposition des autres services.

Si on prend un exemple dans nos environnement Data, dans un contexte Hadoop avec HDFS, un utilisateur final ne doit jamais avoir accès à nos DataNodes mais uniquement à nos NameNodes, il sera commun d’appliquer des ACL pour ne pas exposer nos DataNodes

Infrastructure Security & Integrity

Last but not least, la sécurité et l’intégrité de l’infrastructure. Par intégrité, j’entends une infrastructure capable de résister à la falsification, de fonctionner conformément aux normes ou politiques prescrites et d'avoir une mission claire. Pour ce faire, il vous faudra superviser activement votre environnement.

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

L’audit de sécurité est une évaluation complète de votre organisation, système et environnement.

Il comprend, entre autres :

  • les interviews des personnes impliquées sur votre environnement,
  • l’exécution d’analyses de vulnérabilité en matière de sécurité,
  • l’examen des accès aux services, applications,
  • le contrôle des accès physiques aux systèmes,
  • l’analyse de la traçabilité des actions menées sur vos environnements.

Côté technique, l’audit se base sur des audit logs. Ces logs tracent la totalité des actions faites sur votre système que ce soit les accès aux services ou les opérations menées sur ces derniers (ajout/suppression de nœuds) comme les actions faites sur vos données (ajout/suppression de données).

Les audits ne doivent pas être vus comme quelque chose de négatif mais contribuent à assurer la sécurité optimale de vos environnements.

Stocker les audit logs c’est bien, en tirer partie c’est mieux. Ils vous permettront d’obtenir, en temps réel, des informations précieuses sur la sécurité de votre environnement et de réagir rapidement en cas d’attaque si une supervision est active. On voit apparaître des outils basés sur du Machine Learning aidant à détecter les activités suspicieuses sur le réseau (Amazon Guard Duty,  Azure ATP par exemple), voire même les données sensibles d’une entreprise (AWS Macie par exemple).

Soldats, sécurisez la plateforme Data. 
Reçu fort et clair, terminé !

Cela correspond à tirer partie d’un autre type de logs, les logs d’activité. Mettre en place une supervision de l’activité de votre environnement, vos services, vos applications c’est s’assurer que votre système est opérationnel. Si la supervision est active, elle vous permettra d’anticiper les problèmes et d’éviter, par exemple, la perte de données.

Conclusion

Si vous avez tenu jusque là et que cet article vous a intéressé, mettez un pouce bleu (bon ici, sur ce blog, on ne peut pas… dommage !).

Plus sérieusement, parce que la sécurité est un sujet important, si vous avez mis en place toutes les briques de ce framework dans votre entreprise :

  • vous avez identifié et catégorisé vos données sensibles, vitales pour votre entreprise et cartographié vos flux ;
  • vous vous êtes assuré d’une part que les seules personnes autorisées ont bien un accès conforme à leur profil et, d’autre part que vos données sont correctement utilisées, partagées, de façon contrôlée, et ce, de bout en bout ;
  • vous avez protégé vos données par chiffrement et réduit le scope de votre environnement susceptible de manipuler des PII ;
  • vous avez sécurisé votre réseau, borné et sécurisé vos différents services par zones ;
  • vous êtes en mesure de réagir à toute faille de sécurité en quasi temps réel et êtes en capacité de suivre le mouvement de vos données.

Il y a la théorie et il y a la pratique.  Que ce soit on-premise ou dans le cloud, vous trouverez aisément chaussure à votre pied si vous vous lancez dans la sécurisation de votre environnement. Je ne vous ferai pas une liste détaillée des solutions qui existent sur le marché sinon j’y serais encore. Avec une longueur d’avance, les solutions cloud (de par la quantité de services qu’ils proposent) offrent plus de flexibilité et de facilité de mise en œuvre dans la configuration et l’exploitation du framework de sécurité que je viens de vous présenter.

Je ne vous souhaite pas une attaque de votre environnement… Mais, en tout cas, maintenant vous êtes prêts à relever n’importe quel challenge !

Je finirai par citer Guillaume Poupard, patron de l'agence française de cyberdéfense (Anssi) qui a dit qu’oublier la cybersécurité, c'est “rouler à 200 km/h à moto sans casque".


Être plus agile et rester positif

$
0
0
Être plus agile et rester positif

Le jeudi 29 octobre, j’ai assisté à l’Agile Tour Bordeaux portée par l’association Okiwi. L’événement a eu lieu en ligne sur l’outil Remo.

Avant cette journée, j’étais en quête d’outils, de techniques pour m’améliorer dans mon rôle de Scrum Master. Merci Agile Tour, j’ai trouvé mon bonheur avec deux conférences qui ont retenu particulièrement mon attention:

  • Keynote animée par Marilyn Kol: “Peut-on devenir Agile sans comprendre l’humain et ses limites?
  • Conférence autour du mindset avec Carine San Juan: “Comment hacker son mindset pour plus d’agilité”.

A travers ce post, je vais vous exposer les tips que j’ai retenu pour être plus agile en se concentrant sur le côté humain.

Comprendre l’humain et ses limites pour plus d’agilité

Être plus agile et rester positif

La première étape est de repérer quatre biais cognitifs:

  • Aversion à l’incertitude
  • Aversion à la perte
  • Surestimation de notre capacité à prédire
  • Besoin de sécurité.

Ensuite, nous devons chercher à comprendre les limites de l’humain pour des techniques agiles efficaces. Nous pouvons déjà commencer par nous rappeler du principe n°1 du Manifest Agile: “Notre plus haute priorité est de satisfaire le client en livrant rapidement et régulièrement des fonctionnalités à grande valeur ajoutée.“

Ensuite , il faut définir la valeur ajoutée. On privilégie le temps et les ressources humaines pour produire de la valeur et donner du rythme dans un cadre de développement soutenable.

Être plus agile et rester positif

Le troisième point est de faire du coaching incrémental en connaissant les limites de l’humain. Marilyn conseille de commencer par du coaching collaboratif (coaching demandeur et équipes). Cela permet d’instaurer une synergie de groupe efficace pour un premier objectif. Ensuite, elle entame un coaching adaptable.

Marylin nous invite aussi à challenger nos propres limites. Des questions qui méritent réflexion:

  • Qu’est-ce que l’agilité?
  • Nos valeurs agiles ?
  • Quels sont nos moteurs de motivation ?
  • Incarnons-nous ce que nous prônons ?

Pour cela on va:

  1. S’intéresser à avoir une équipe collaborative
  2. Prendre en considération les feedback clients
  3. Chercher l’excellence technique.

Comme dernier point, on se pose des questions sur nos idéaux. J’ai retenu deux idéaux dans lesquels je me retrouve:

  • Apprendre à se connaître pour mieux s’écouter
  • Être ok avec soi pour être ok avec les autres.

Hacker son mindset pour être plus agile

Être plus agile et rester positif

En ces temps difficiles, il est important de rester positif : renforcer son mindset.

Carine San Juan propose cinq façons pour développer le mindset de croissance pour plus d’agilité. Deux hacks m’ont marqué: un lié à la curiosité et l’autre à l’échec

Rester Curieux

Souvent, on dit que la curiosité est un vilain défaut. En modérant la curiosité, cela peut nous apporter du positif. On peut pratiquer le SHIFT:

  • Physique: changer de posture
  • Mental: se poser les 3 questions suivantes:
    • Est-ce que j’ai une intention positive ou suis-je sur la défensive ?
    • Est-ce que je vois une opportunité d’apprentissage ou une opportunité de démontrer que j’ai raison ?
    • Est-ce que j’écoute pour comprendre ou pour répondre ?

Ce type d’exercice peut être pratiqué avant une rétrospective surtout quand on sort d’un sprint difficile.

Recadrer l’échec

Carine pense que le fait de recadrer le discours interne va nous permettre de trouver les différents axes d’apprentissage. Par exemple, lors de la gestion d’un conflit, l’idée serait de trouver les mécanismes qui ont fait émerger le conflit pour mieux s’assurer qu’il est résolu.

Être plus agile et rester positif

Pour finir ma journée, j’ai pratiqué un exercice proposé par Carine San Juan: grandir et prendre sa place.
L’exercice consiste à respirer avec aisance debout ou assis, dos droit, et les pieds bien ancrés dans le sol. Cela m’a permis de me poser et me dire “agis et écris mon expérience Agile Tour Bordeaux 2020”.


Avoir un thème en tête rend la journée instructive et fluide. Ainsi, j’ai choisi les conférences en adéquation avec ma recherche. J’étais agréablement surprise de voir que je ne suis pas seule à chercher d’abord à comprendre l’humain, puis d’adapter les processus dans un second temps. Je sors ravie de cette journée et avec le sentiment de garder un état d'esprit toujours en éveil.

Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

$
0
0
Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

Dans cette série de deux articles, nous verrons comment construire une application simple d'apprentissage automatique serverless, basée sur Amazon API Gateway, AWS Lambda et un endpoint SageMaker pour la classification des images. Les articles sont orientés débutant et vous proposent une recette simple et très détaillée pour réaliser cette application.

J'utiliserai Amazon SageMaker Studio pour la préparation des données, l'entraînement du modèle, Chalice pour écrire des applications serverless ainsi que Boto3, le kit de développement AWS qui permet de créer, configurer et gérer des services AWS.

Dans cette partie, je vous donnerai un aperçu d'Amazon SageMaker Studio, et vous montrerai comment l'utiliser pour construire et déployer un modèle de Machine Learning.

Amazon SageMaker Studio : Qu’est-ce que c’est ?

Amazon SageMaker est un environnement de développement intégré (IDE), entièrement géré et managé par AWS, pour le Machine Learning, qui offre la possibilité de construire, entraîner, déployer et monitorer rapidement des modèles de Machine Learning (ML).

Pour l'instant, Amazon SageMaker Studio n'est disponible que dans les régions AWS suivantes :

  • US East (Ohio), us-east-2,
  • US East (N. Virginia), us-east-1,
  • US West (N. Oregon), us-west-2,
  • China (Beijing), cn-north-1,
  • China (Ningxia), cn-northwest-1,
  • EU (Ireland), eu-west-1.

SageMaker Studio fournit plusieurs outils dont :

SageMaker Studio Notebooks fournit des notebooks collaboratifs qui peuvent être lancés en moins de deux minutes (plus rapide que le lancement d'une instance de notebook). Vous pouvez partager des notebooks Jupyter avec d'autres et passer rapidement d'une configuration matérielle à une autre sans avoir à gérer une quelconque infrastructure.

SageMaker Experiments vous aide à suivre et à comparer les performances des modèles ML en capturant les paramètres d'entrée, les configurations et les résultats et en les stockant sous forme d'expériences 'experiments'. Vous pouvez ensuite explorer ces expériences et les comparer par une visualisation sur un tableau de bord.

SageMaker Debugger permet de monitorer, d'analyser et de visualiser les processus d’entraînement ML en temps réel, en détectant également les problèmes courants qui pourraient être rencontrés dans les jobs d’entraînement (e.g., un mauvais seed pour les paramètres d’optimisation, problèmes liés au gradient, une fonction de coût qui ne diminue pas, etc.), ainsi que la possibilité d'envoyer des alertes.

SageMaker Model Monitor monitore automatiquement les modèles ML en production et vous alerte lorsque des problèmes de qualité des données apparaissent.

SageMaker Autopilot vous aide à construire automatiquement des modèles ML (prétraitement des données, sélection des algorithmes et réglage des modèles), ainsi qu'à approvisionner l'infrastructure.

Mise en place de l’Amazon SageMaker Studio

Pour commencer à utiliser Amazon SageMaker Studio, vous devez suivre les étapes suivantes :

  1. Connectez-vous à la console de gestion AWS.

  2. À partir du sélecteur de région situé dans le coin supérieur droit de la console, changez votre région en eu-west-1 - Europe (Irland) ou toute région où Amazon SageMaker Studio est disponible.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

  3. Dans la console, cliquez sur Services > Amazon SageMaker.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

  4. Depuis la console SageMaker, dans la barre latérale gauche, cliquez sur Amazon SageMaker Studio.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

  5. Dans le tableau de bord Amazon SageMaker Studio, sous Get started, choisissez Quickstart.

  6. Pour le User name, gardez le nom par défaut ou créez-en un nouveau.

  7. Sélectionnez ensuite un Execution role avec la policy AmazonSageMakerFullAccess, ce rôle IAM donne à Amazon SageMaker la permission d'effectuer des actions dans d'autres services AWS en votre nom.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

  8. Si vous n'avez pas de rôle IAM, choisissez Create a new role dans la fenêtre contextuelle Créer un rôle IAM, choisissez Any S3 bucket > Create role.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

    SageMaker crée un nouveau rôle IAM avec la AmazonSageMakerFullAccess policy jointe.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

  9. Cliquez sur Submit.

  10. Après quelques secondes, SageMaker Studio sera prêt à être utilisé.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

  11. Une fois qu'il est prêt, lancez SageMaker Studio en cliquant sur le bouton Open Studio qui vous redirigera vers la console Amazon SageMaker Studio.

Vue d’ensemble de l’interface utilisateur Amazon SageMaker Studio

Amazon SageMaker Studio dispose d'une interface web où vous pouvez effectuer tous les processus de Machine Learning, de l'expérimentation à la production.

La capture d'écran suivante montre l'interface utilisateur de SageMaker Studio, il y a un onglet Bienvenue et un onglet Lanceur pour exécuter les tâches courantes, comme le lancement d'un nouveau notebook, l'ouverture d'une session de terminal et la création de fichiers texte ou de Markdown.

Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

Dans la barre latérale gauche, vous pouvez faire ce qui suit :

  • Charger des fichiers dans SageMaker Studio,
  • Se connecter aux dépôts Git,
  • Voir les terminaux, kernels et images qui tournent,
  • Rechercher et accéder aux commandes les plus courantes,
  • Afficher une liste d'expériences,
  • Accéder aux métadonnées d'un notebook via la section Advanced Tools,
  • Afficher une liste des endpoints,
  • Afficher une liste des onglets ouverts.

Utilisation des notebooks Amazon SageMaker Studio

Avec Amazon SageMaker Studio, vous pouvez utiliser un Jupyter notebook sans avoir besoin de lancer une instance et d'attendre qu'elle soit lancée. Vous pouvez créer un notebook dans SageMaker Studio depuis le File menu ou depuis le Lanceur Amazon SageMaker Studio, ici nous utiliserons un notebook existant pour déployer un modèle pré-entraîné de PyTorch qui a été entraîné sur un large ensemble de données pour classer des images de chats et de chiens.

Nous utiliserons donc le Launcher SageMaker comme ci-dessous :

  1. Dans l'onglet Launcher, choisissez System Terminal.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

  2. Clonez le notebook de classification des images, qui est récupéré dans amazon-sagemaker-examples avec la commande suivante :
    git clone https://github.com/a-shlash/dogs_vs_cats_image_classifier.git

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

  3. Dans la barre latérale gauche, choisissez l'icône du navigateur de fichiers et naviguez jusqu'à /dogsvscatsimageclassifier/ puis, double-cliquez sur pytorchtorchvisionneostudio.ipynb

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

Ce notebook contient tout le code et les instructions nécessaires pour importer le modèle ResNet18, créer un endpoint de prédiction puis envoyer des requêtes au endpoint.

SageMaker Studio Notebooks fournit une liste d'images que les utilisateurs peuvent choisir, incluant Data Science, PyTorch, MXNet, TensorFlow, etc.

  1. Pour ouvrir le notebook, double-cliquez dessus, puis une boîte de dialogue apparaît pour sélectionner le Kernel du notebook comme ci-dessous.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

Sélectionnez Python 3 (Data Science) > Select. Le kernel Data Science est une image Conda avec les packages et bibliothèques Python les plus utilisés, tels que NumPy et SciKit Learn.

  1. Pour choisir le type d'instance, cliquez sur "Unknown", une autre fenêtre pop-up apparaît qui contient une liste d'instances disponibles pour un lancement rapide avec leur vCPU, GPU et leur mémoire. Choisissez l’instance ml.t3.medium > Save and Continue, comme indiqué dans la capture d'écran suivante.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

Vous pouvez commencer par une petite instance puis la modifier à partir du notebook si vous souhaitez augmenter l'instance selon vos besoins.

  1. Vous recevrez une notification vous informant que le processus est en cours.
    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

  2. Quelques minutes plus tard, votre Python3 Jupyter notebook tournera sur l'image SageMaker de Data Science en utilisant une instance avec 2 vCPU et 4 GiB de mémoire.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

Votre instance Jupyter notebook est maintenant prête et nous pouvons suivre les instructions fournies pour déployer notre modèle de classification d'images comme endpoint.

Voir le endpoint SageMaker

En complétant l'exemple dans le notebook, vous obtiendrez un endpoint. Dans la barre latérale gauche, cliquez sur l'icône pour afficher le endpoint qui tourne.

Il doit ressembler à TorchVision-RestNet18-Neo-2020-07-15-17-35-33-456-Endpoint.

Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

Nettoyage

  1. Depuis le notebook, assurez-vous d'appeler sess.deleteendpoint(endpointname) pour supprimer le endpoint.

  2. Cliquez sur File > Shut down.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

Une fenêtre contextuelle de confirmation d'arrêt (Shutdown confirmation) apparaît.

Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

Cliquez sur "Shutdown All" pour mettre fin à toutes les images, noyaux, terminaux et au serveur Jupyter en cours d'exécution.

  1. Arrêtez SageMaker Studio :

Depuis le panneau de contrôle d'Amazon SageMaker Studio

  • Choisissez l'utilisateur.

  • Sur la page Détails de l'utilisateur, choisissez Delete user.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

  • Dans la fenêtre contextuelle Delete user, cliquez sur Yes, delete Studio, tapez delete pour confirmer puis choisissez Delete.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

  1. Lorsque l'utilisateur est supprimé, choisissez Delete Studio.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

  2. Dans la fenêtre contextuelle Delete Studio, cliquez sur Yes, delete Studio, tapez delete pour confirmer, puis choisissez Delete.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

  3. Dans le bucket s3, supprimez tous les fichiers téléchargés par SageMaker Studio.

    Modèle de Machine Learning du notebook à la production (Partie 1 - SageMaker Studio)

Conclusion :

À ce stade, nous savons comment créer une instance de notebook Jupyter avec des ressources personnalisées et déployer un modèle ML sur le endpoint SageMaker.

Découverte d’ArangoDB, une base multi-modèle

$
0
0
Découverte d’ArangoDB, une base multi-modèle

Au démarrage d'un projet, le choix de la base de données est une étape importante au bon déroulement de celui-ci. En fonction du type de données à stocker, on pourra choisir d’utiliser plutôt une base relationnelle, graphe, documents ou autre.

Certains projets peuvent nécessiter la mise en place de plusieurs bases de données, une base pour chaque type.

Le choix n’est pas toujours facile au début du projet car les données pourraient être amenées à changer.

ArangoDB répond en partie à cette problématique, car elle a la particularité d’être multi-modèle.

Multi-modèle

ArangoDB fait partie des bases de données dites NoSQL. Celles-ci regroupent quatre grands types de structure de données : clé-valeur, documents, colonnes et graphe.

ArangoDB prend en charge nativement les données de type document, graphe et clé/valeur.

Il n'y aura donc besoin d'installer et monitorer qu’une seule base et de n'apprendre qu’un langage de requête.

Langage unique

ArangoDB utilise son propre langage, l’AQL (Arango Query Language). L’AQL se rapproche du SQL dans la logique de construction des requêtes.

D’un point de vue technique, les 3 modèles sont stockés différemment, mais cela est transparent pour l’utilisateur final car les requêtes se font comme si l’on n'avait qu’une seule et même base. Ainsi, on peut récupérer des données des 3 modèles dans la même requête.

La syntaxe ressemble à du JavaScript. Des exemples de requêtes sont disponibles dans la deuxième partie de l’article.

Open source et version gratuite

Le projet commencé en 2011 est open source (Apache Licence 2.0) et a récemment dépassé les 10000 étoiles sur Github.

ArangoDB se décline en 2 versions, la version community et la version enterprise. La version enterprise apporte quelques fonctionnalités supplémentaires, en particulier sur le mode cluster et la sécurité.

Join natif

Contrairement à la plupart des bases de données NoSQL, ArangoDB permet de réaliser des requêtes JOIN tout en gardant la souplesse des bases de données documents. Il n’y a pas de contrainte sur les clés étrangères par exemple.

GeoJSON

GeoJSON est un format d’encodage pour les données géospatiales utilisant la norme JSON.

ArangoDB supporte ce format et permet de faire des requêtes pour calculer des distances, rechercher des points dans une zone géographique, etc.

Full-text search

Il est possible avec ArangoDB de définir un index full-text sur certains champs textuels et d’effectuer des requêtes complexes dédiées à la recherche dans du texte. Par exemple, on peut chercher dans un texte plusieurs mots clés et en exclure certains.

Facilité d'installation

ArangoDB propose un large choix d’installation, on pourra l’installer on-premise sur les différents OS (Windows, Linux, MacOs) ou virtualisée avec Docker.

Si l’on souhaite utiliser Kubernetes, un chart Helm permet de rapidement mettre en place un cluster.

Depuis un an, ArangoDB propose l'offre ArangoDB Oasis, entièrement managée dans le cloud avec le choix de l'hébergeur (AWS, GCP, ou Azure). Elle apporte en plus une meilleure gestion de la sécurité, une scalabilité facile et peut facilement être intégrée à des solutions d’Infrastructure-as-Code grâce à une API dédiée.

Nombreux drivers

ArangoDB propose des drivers officiels pour les langages Java, JavaScript, PHP, et Go, ainsi que des drivers communautaires pour PHP, .NET, Go, Python, Scala, Ruby.

Une intégration avec Spring Data et un connecteur spark Scala/Java sont aussi proposés.

L’utilisation des drivers nécessite tout de même d’écrire les requêtes avec le langage AQL.

Distribuée

L’installation proposée par défaut est le mode stand-alone mais ArangoDB propose d’autres modes d’installation :

  • Master/Slave : un Master et un ou plusieurs Slaves. Les requêtes en écriture doivent être envoyées au Master, le Slave réplique les données du Master de façon asynchrone.
  • Cluster : dans cette configuration, les requêtes peuvent être envoyées sur n’importe quel nœud. On parle de Master/Master.

ArangoDB possède un système de transactions qui respecte les propriétés ACID. En modestand-alone, les requêtes multi-documents et multi-collections sont supportées, en mode cluster, seules les requêtes single-document sont garanties ACID.

La documentation d’ArangoDB présente en détail le fonctionnement des transactions (Transactions) ainsi que les différents modes d’installation (Architecture).

Performante

ArangoDB a réalisé un benchmark en 2018 pour comparer son produit avec les bases MongoDB, PostgreSQL (tabular & JSONB), OrientDB et Neo4j. Les résultats sont accessibles sur leur site : Benchmark: MongoDB, PostgreSQL, OrientDB, Neo4j and ArangoDB.

Selon leur test, les performances d’ArangoDB étaient tout à fait à la hauteur de ses rivales pour les écritures / lectures. Les performances étaient très bonnes pour les agrégations et la recherche de voisins dans les graphes. La consommation mémoire était cependant un peu supérieure à la moyenne.

Elle était supérieure à la célèbre base Neo4j sur tous leurs critères de test.

ArangoDB est cependant moins performante qu’une base clé/valeur classique.

Un document clé/valeur dans ArangoDB est simplement un document avec un seul champ value. ArangoDB déconseille d’utiliser cette base dans le seul but de stocker des données clé/valeur.

Interface web

L’interface web n’est pas forcément la fonctionnalité la plus recherchée dans le choix d’une base de données, pourtant, elle peut apporter un vrai plus dans le management et le monitoring de la base.

L’interface web d’ArangoDB remplit parfaitement ce rôle grâce à un dashboard de monitoring, un éditeur de requêtes, et un outil de visualisation de graphes.

Documentations

ArangoDB propose une documentation complète avec de nombreux exemples : Documentation Overview

Une vingtaine d’entreprise ont partagé leur retour d’expérience sur ArangoDB et leur cas d’usage : ArangoDB Customers - NoSQL Multimodel Database

Il est aussi possible de rejoindre le Slack officiel ou un Google Groups dédiés à ArangoDB pour obtenir du support.

Hands-on

Ce tutoriel a pour but de prendre en main rapidement l’interface web d’ArangoDB et de comprendre les fonctionnalités principales, en particulier :

  • Key/Value,
  • Documents,
  • Join,
  • GeoJSON,
  • Graphe.

On prendra comme exemple un réseau social qui aurait besoin de stocker des posts (Posts), des utilisateurs (Users) avec leur localisation (Cities) et des commentaires (Comments) sur les posts.

Découverte d’ArangoDB, une base multi-modèle

Installation d’ArangoDB

La solution la plus rapide pour tester ArangoDB est d’utiliser l’image Docker. Il est toutefois possible d’installer la base sur une autre plateforme en suivant la documentation officielle (Download ArangoDB Database Community Edition).

Si Docker est installé sur votre poste, lancez la commande suivante :

docker run -p 8529:8529 -e ARANGO_ROOT_PASSWORD=openSesame arangodb/arangodb:3.7.2.1

L’interface web est disponible à l’adresse : http://localhost:8529/.

Une fois sur la page de connexion, connectez-vous en utilisant l’utilisateur root et le mot de passe openSesame.

Choisissez la base _system (la seule disponible pour l’instant).

La base _system propose un dashboard de monitoring, la gestion des utilisateurs et bases de données, la réplication, les logs, etc.

Découverte d’ArangoDB, une base multi-modèle

Création de la base

Créez une nouvelle base nommée social_network à partir de l’onglet DATABASES (1) et passez de la base _system à la nouvelle base (4).

Découverte d’ArangoDB, une base multi-modèle

Création des collections

Il existe 2 types de collections dans ArangoDB :

  • Document ;
  • Edge : dédiée aux graphes, les documents dans une collection Edge permettent de faire le lien entre deux documents classiques.

Pour le réseau social, on va créer 3 collections du type Document :

  • users ;
  • posts ;
  • cities ;
    et 1 collection du type Edge pour les commentaires qui fera le lien entre les users et les posts :
  • comments.

Découverte d’ArangoDB, une base multi-modèle

Dans l’interface web, on remarque l’utilisation de logos différents pour les collections Document et les collections Edge.

Découverte d’ArangoDB, une base multi-modèle

Insertion des données

Les documents ArangoDB possèdent des champs particuliers :

  • _key : la clé du document. Si elle n’est pas définie, celle-ci sera générée. La clé est indexée et unique ;
  • _id : l’id est la concaténation du nom de la collection et de la clé. L’id est indexé. On peut faire une requête pour obtenir un document directement grâce à son id.

Pour les documents de type Edge, qui représentent les relations :

  • _from : l’id du document d’origine,
  • _to : l’id du document d'arrivée.

Pour insérer les données, on va utiliser le langage AQL dans l'éditeur de requête.

Sélectionnez dans le menu QUERIES (1), écrivez la requête (2), et exécutez-là (3).

Découverte d’ArangoDB, une base multi-modèle

Cities

On insère 3 villes avec leurs coordonnées GPS.

LET cities_data = [
   { _key: "lyon", name: "Lyon",  coordinates: [45.735773, 4.815310] },
   { _key: "paris", name: "Paris",  coordinates: [48.875421, 2.288823] },
   { _key: "bordeaux", name: "Bordeaux",  coordinates: [44.844301, -0.576779] }
]

FOR city IN cities_data INSERT city IN cities

Users

On insère 4 utilisateurs possédant un nom d’utilisateur, un âge et l’id d’une ville précédemment insérée dans la collection cities.

LET users_data = [
    { _key: "user_1", username : "user 1", age: 25, city: "cities/lyon" },
    { _key: "user_2", username : "user 2", age: 35, city: "cities/lyon" },
    { _key: "user_3", username : "user 3", age: 55, city: "cities/paris" },
    { _key: "user_4", username : "user 4", age: 60, city: "cities/bordeaux" }
]

FOR user IN users_data INSERT user IN users

Posts

On insère 4 posts avec leur titre et leur contenu.

LET posts_data = [
    { _key: "post_1", title: "post 1", content: "content 1" },
    { _key: "post_2", title: "post 2", content: "content 2" },
    { _key: "post_3", title: "post 3", content: "content 3" },
    { _key: "post_4", title: "post 4", content: "content 4" }
]

FOR post IN posts_data INSERT post IN posts

Comments

On insère des commentaires qui font le lien entre des utilisateurs (_from) et des posts (_to).

LET comments_data = [
    { _from: "users/user_1", _to: "posts/post_1", text: "Comment of user 1 on post 1" },
    { _from: "users/user_1", _to: "posts/post_3", text: "Comment of user 1 on post 3" },
    { _from: "users/user_2", _to: "posts/post_1", text: "Comment of user 2 on post 1" },
    { _from: "users/user_2", _to: "posts/post_2", text: "Comment of user 2 on post 2" },
    { _from: "users/user_2", _to: "posts/post_3", text: "Comment of user 2 on post 3" },
    { _from: "users/user_3", _to: "posts/post_3", text: "Comment of user 3 on post 3" },
    { _from: "users/user_3", _to: "posts/post_4", text: "Comment of user 3 on post 4" }
]

FOR comment IN comments_data INSERT comment IN comments

Création du graphe

On va maintenant créer un graphe pour modéliser la relation entre les utilisateurs, les posts et les commentaires.

Pour cela, sélectionnez GRAPHS dans le menu (1), ajoutez un graphe (2), puis créez le graphe en définissant la collection edge à utiliser (comments) ainsi que la collection from (posts) et to (users) (3).

Découverte d’ArangoDB, une base multi-modèle

Une fois le graphe créé, on va pouvoir visualiser les données.

Toujours dans l’onglet GRAPHS sélectionnez le graphe créé (1).

Seulement quelques nœuds sont chargés par défaut, dans le graphe créé, on a très peu de données, on peut charger sans risque l'intégralité des données en cliquant sur le bouton (2).

Découverte d’ArangoDB, une base multi-modèle

Les relations entre les utilisateurs et les posts correspondent bien aux commentaires insérés. C’est un graphe orienté, le sens des flèches indique la direction de la relation. Dans ce cas, ce sont des utilisateurs qui commentent des posts.

Requêtes

Pour réaliser les requêtes, on va utiliser l’interface web.

Cliquez sur l’onglet QUERIES (1) pour accéder à l’éditeur, écrivez une requête (2) puis exécutez-la (3).

Le résultat apparaît en dessous, sous forme de tableau par défaut, mais cela peut être changé pour s’afficher au format json (4).

Découverte d’ArangoDB, une base multi-modèle

Key/Value

On peut requêter un document en connaissant son id (généré à partir de la collection et de la clé)

Si l’on souhaite récupérer le document associé à la ville de Lyon, on fera la requête suivante :

Requête :

RETURN DOCUMENT("cities/lyon")

Résultat :

Découverte d’ArangoDB, une base multi-modèle

Documents

On souhaite récupérer pour tous les utilisateurs, leur nom d’utilisateur et leur âge.

Requête :

FOR user IN users
    RETURN {
        username: user.username,
        age: user.age
    }

Résultat :

Découverte d’ArangoDB, une base multi-modèle

Join

On souhaite maintenant récupérer les utilisateurs de plus de 25 ans avec les informations détaillées sur les villes.

Requête :

FOR user IN users
    FOR city IN cities
        FILTER user.city == city._id
        FILTER user.age > 25
        RETURN MERGE(user, {city: city})

On utilise la fonction MERGE afin d’obtenir un seul document à partir de l’utilisateur et de la ville correspondante. ArangoDB propose de nombreuses fonctions prédéfinies documentées sur leur site : ArangoDB Query Language AQL Functions

Résultat :

Découverte d’ArangoDB, une base multi-modèle

On retrouve bien les 3 utilisateurs avec le document correspondant à leur ville inclus dans le champ city.

Geojson

Prérequis :

Pour réaliser des requêtes utilisant des coordonnées GPS, ArangoDB a besoin d’indexer le champ contenant les coordonnées.

Sélectionnez dans l’onglet COLLECTIONS la collection cities (1), puis allez dans l’onglet indexes (2).

Ajoutez un index de type Geo Index sur le champ coordinates et cochez la case Geo JSON (3).

Découverte d’ArangoDB, une base multi-modèle

On souhaite calculer la distance des utilisateurs par rapport à la tour Eiffel (48.858723, 2.295468).

Pour cela, on stocke le résultat de la requête précédente avec les 3 utilisateurs de plus de 25 ans dans une variable et on exécute une requête pour calculer la distance sur ces données.

Requête :

LET users_with_city = (
    FOR user IN users
        FOR city IN cities
            FILTER user.city == city._id
            FILTER user.age > 25
            RETURN MERGE(user, {city: city})
)
            
FOR user IN users_with_city
    LET distance = DISTANCE(user.city.coordinates[0], user.city.coordinates[1], 48.858723, 2.295468)
    RETURN {
        username: user.username,
        city: user.city.name,
        distance: distance / 1000
        }

Les distances sont en mètres, on divise par 1000 pour convertir en kilomètres.

Résultat :

Découverte d’ArangoDB, une base multi-modèle

Les résultats sont cohérents, l’utilisateur 3 de Paris est à 1,9 km de la tour Eiffel, les deux autres utilisateurs sont à plus de 350 km.

Graphe

Prérequis :

Les requêtes en AQL de type graphe utilisent le vocabulaire des graphes.

Découverte d’ArangoDB, une base multi-modèle

  • Vertex (Vertices au pluriel) : les sommets/nœuds du graphe (0, 1, 2, 3),
  • Edge : les arêtes qui relient deux sommets entre eux (l'arête 0 vers 2 , 2 vers 3 par exemple),
  • Path : les chemins parcourus entre deux nœuds,
  • Outbound / Inbound / Any : le sens de parcours,
    • Outbound : le même sens que la relation (2 vers 3),
    • Inbound : sens inverse de la relation (2 vers 0),
    • Any : parcours du graphe dans les deux sens,
  • Min..Max : la profondeur minimum et maximum du graphe,
    • 1..1 retournera les nœuds en bleu,
    • 2..2 retournera le nœud en vert,
    • 1..2 retournera les nœuds bleu et vert.

Cas 1 :

On souhaite utiliser le graphe pour obtenir les commentaires de l’utilisateur 2.

Requête :

FOR vertex, edge, path IN 1..1 OUTBOUND
  'users/user_2' GRAPH 'users_posts_comments'
  RETURN {
    post_title: vertex.title,
    comment: edge.text
    }

Résultat :

Découverte d’ArangoDB, une base multi-modèle

L’utilisateur 2 a commenté les posts 1, 2, et 3.

Cas 2 :

On souhaite obtenir les utilisateurs qui ont commenté le post 3. Pour cela, on doit parcourir notre graphe dans le sens inverse, on utilise le mot clé INBOUND.

Requête :

FOR vertex, edge, path IN 1..1 INBOUND
  'posts/post_3' GRAPH 'users_posts_comments'
  RETURN vertex

Résultat :

Découverte d’ArangoDB, une base multi-modèle

Les utilisateurs 1, 2 et 3 ont commenté le post 3.

Cas 3 :

Enfin, on souhaite obtenir les utilisateurs qui ont commenté les mêmes posts que l’utilisateur 1 ainsi que le nombre de posts qu’ils ont commentés en commun. Cela permet par exemple d’en déduire les utilisateurs qui ont des goûts similaires à l’utilisateur 1.

Cette requête est dite traversal, on va traverser le graphe sur deux niveaux (users -> posts -> users). On indique dans ArangoDB le degré de profondeur (2..2) et que l’on souhaite traverser dans les 2 sens (ANY).

On utilise la fonction COLLECT et COUNT pour compter le nombre d'occurrences des utilisateurs.

Requête :

FOR vertex, edge, path IN 2..2 ANY
  'users/user_1' GRAPH 'users_posts_comments'
  COLLECT user = vertex WITH COUNT INTO length
  RETURN {
        username: user.username,
        length
        }

Résultat :

Découverte d’ArangoDB, une base multi-modèle

L’utilisateur 1 et l’utilisateur 2 ont commenté 2 articles en commun.

Conclusion

ArangoDB est une bonne candidate pour les projets nécessitant le stockage de données sous forme de documents et de graphe.

Cette base est facile à installer et à utiliser grâce aux nombreux drivers disponibles.

Elle propose un mode cluster et différents systèmes de réplication.

Certes, le langage AQL pourra être perturbant au début, en particulier pour les requêtes graphe, cependant, la documentation officielle est très complète.

ArangoDB mérite d’être considérée dans le choix de la base de données, pour des besoins multi-modèle ou si le modèle de données n’est pas bien défini.

Azure Data Factory : réussissez vos pipelines ETL !

$
0
0
Azure Data Factory : réussissez vos pipelines ETL !

Cet article technique vise à présenter succinctement Azure Data Factory (ADF), comment le mettre en place dans un environnement DevOps sur Microsoft Azure, et d’autres conseils que j’ai récoltés au fur et à mesure pour réussir vos pipelines ETL ! Il vise un public technique avec une première expérience sur l’outil. Les techniques présentées fonctionnent à date, mais l’outil évoluant vite, comparez ce qui est présenté ici avec ce qui est préconisé avec la documentation (pas toujours à jour non plus !).

L’outil

Azure Data Factory est un ETL / ELT managé dans le cloud disponible sur Microsoft Azure, à la popularité grandissante. Il vise à remplacer progressivement SQL Server Integration Service (SSIS), l’ETL plus ancien et traditionnellement On-premises de Microsoft. Les deux ne sont cependant pas antagoniques : il est possible d’exécuter des packages SSIS dans ADF, peut être comme une première étape pour un lift-and-shift.

ADF garde une interface visuelle cohérente, qui sera familière à ceux qui ont l’habitude d’utiliser des outils ETL comme Talend ou Matillion. C’est cette interface que vous utiliserez pour la construction de vos pipelines.

Azure Data Factory : réussissez vos pipelines ETL !

Vision d’ensemble de l’interface ADF

Sous le capot, c’est soit un ensemble de machines gérées par Azure (pour les pipelines), soit un cluster Spark (pour les Data Flows, nous précisons la différence juste après) qui s’occupe de l’exécution. ADF n’est donc pas fondamentalement limité en matière de volumétrie, mais restera cependant, dans le cadre de ses composants logiques par défaut, adapté à des tâches ETL d’une complexité faible à moyenne. Je précise bien dans le cadre de ses composants logiques par défaut, car il est possible d’exécuter des notebooks Databricks, HDInsight, des Azure Functions et les Data Flows comme composants de votre pipeline pour ajouter la logique de transformation personnalisée qui vous est nécessaire.

Azure Data Factory : réussissez vos pipelines ETL !

Exemple de l’infrastructure managée mise en place pour une activité Copy (copier une donnée source vers une destination cible)

ADF a donc deux parties logiques principales :

  • Une partie dédiée à l’orchestration : les pipelines. C’est dans cette partie que l’on retrouve la logique pour extraire des données, les déplacer d’un endroit à l’autre. C’est aussi dans cette partie que vous pouvez orchestrer l’exécution des services de transformation mentionnés précédemment (Databricks…) : ADF jouera ici le rôle d’orchestrateur entre les différents services. De même, il est possible d’utiliser des composants de Machine Learning pour pouvoir intégrer vos modèles réalisés notamment avec AzureML, dans le pipeline. C’est sur cette partie que portera l’article.
  • Une partie dédiée uniquement aux préparations / transformations : les Data Flows. Cela concerne des tâches de data wrangling, ce qu’on ferait par exemple avec une librairie comme Pandas. Il faut utiliser le langage M de Power Query, dont les fonctions sont ensuite traduites en code Spark. Cette partie est encore jeune et pas très mature, je lui préfère actuellement l’exécution d’un notebook (qui peut être orchestré dans un pipeline) sur Databricks pour ces étapes de préparation.

Outre la partie “ETL / ELT managé dans le cloud”, l’un des avantages indéniables d’ADF, c’est la richesse de ses connecteurs (+90) pour divers endpoints et son intégration dans l’écosystème Azure. L’autre avantage, c’est la partie monitoring / industrialisation qui bénéficie de la maturité de Azure sur le sujet, notamment pour Azure DevOps. Vous pouvez aussi créer des pipelines hybrides, c’est-à-dire des pipelines qui vont se connecter directement à vos environnements On-premises pour en extraire / déposer des données, via les self-hosted integration runtime (un service à installer sur un de vos serveurs, servant de passerelle).

Gardons cependant en tête que ADF présente quelques limitations à l’heure actuelle, mais que ces dernières sont amenées à être rapidement comblées en fonction des retours utilisateurs sur le forum, où vous pouvez appuyer des demandes de fonctionnalités (les équipes sont vraiment à l’écoute). C’est un outil qui évolue vite. Il manque notamment, selon moi (et parmi les requêtes du forum) :

  • La possibilité d’arrêter un pipeline en fonction de la valeur d’une variable : lien.
  • La possibilité de désactiver des composants dans le pipeline pour qu’ils ne soient pas exécutés (et inversement de pouvoir exécuter des composants individuellement et pas tout le pipeline à chaque fois). Le compromis actuel est de mettre la connexion entre les composants en gris pour ne pas prendre en compte le reste du flow.
  • Un composant pour l’envoi de notifications (emails…). Fonctionnalité en cours de développement : lien
  • L’astuce actuelle consiste à utiliser une Logic App pour cette tâche et la déclencher via le composant Web (requête HTTP) de ADF.
  • Les tests (vérification de type des colonnes, nombre de lignes pour une table…) : lien.

Je vais maintenant vous donner quelques conseils et astuces pour la création de vos pipelines. Le but n’est pas de faire le tour entier de l’outil, la documentation (très bien faite) existe pour cela, mais plutôt de mettre en avant des points que l’on apprend par “expérience” (ou parce que c’était caché au fin fond d’une doc !).

Organisation des Pipelines

Dans un premier temps, c’est l’organisation de vos pipelines et surtout de vos datasets qui est importante. Dans un petit projet, cela se ressent moins, mais il faut penser à l’évolution potentielle. A l’instar de ce qu’on ferait avec un autre ETL, on va donc chercher à créer des niveaux d’abstraction dans nos pipelines (des hiérarchies, voir cet excellent article), utiliser les dossiers, et créer des datasets génériques. On peut aussi viser à respecter les conventions de nommage.

Datasets

Les datasets (source / cibles de données) représentent souvent la partie la moins bien organisée, même pour des petits projets. Quand on débute, on va créer des datasets rigides, qui référencent plusieurs fois la même source de données, et on finit par s’y perdre. Dans un second temps, on comprend l’intérêt des paramètres des datasets pour pouvoir variabiliser le chemin du dossier, ou le nom du fichier. Là encore, l’erreur commune est de commencer à rajouter de la logique par des expressions compliquées (par exemple, enlever l’extension du nom du fichier et créer un dossier avec ce nom etc.) au niveau de la définition du dataset.

Azure Data Factory : réussissez vos pipelines ETL !

Le problème ? Vos datasets deviennent trop spécialisés et ne sont pas réutilisables. Si un composant a besoin de la même source de donnée, mais qu’il n’a pas besoin de cette logique au niveau du nom du fichier ? Cela vous forcera à recréer un autre dataset, et ainsi de suite.

Ce qu’on souhaite donc garder en tête, c’est que la logique ne doit pas être du côté des datasets pour la variabilisation. Les expressions doivent être effectuées au niveau de l’activité dans la pipeline et le résultat final passé au dataset.

Azure Data Factory : réussissez vos pipelines ETL !

Implémentation de la logique au niveau de l’activité :

Azure Data Factory : réussissez vos pipelines ETL !

Cependant, certains cas nécessitent un nouveau dataset pour la même source, c’est notamment le cas pour passer de la volonté de copier un fichier compressé en brut, à la volonté de sa décompression (un en brut, l’autre en zipDeflate…). En effet, il faudra dans ce cas modifier le type de compression au niveau du dataset.

Azure Data Factory : réussissez vos pipelines ETL !

Avec ce réflexe de variabilisation de chaque champ d’un dataset, on peut rencontrer un problème : que faire si j’ai seulement besoin d’accéder au contenu d’un dossier dans cette source, et ne pas descendre jusqu’à au niveau du fichier ? J’aurai alors une erreur car le paramètre pour le nom du fichier restera vide. L’astuce, c’est d’assigner un string vide au paramètre qu’on ne souhaite pas remplir : @toLower(‘’)

On peut aussi utiliser les wildcards : en donnant la valeur * au filename, je sélectionne tous les fichiers dans le dossier spécifié.

Logique

La plupart des composants sont intuitifs d’utilisation. Cependant, certains besoins nécessitent une attention particulière.

Décompression de fichiers

Un cas qui peut paraître simple d’emblée (et qui devrait l’être), mais qui pose parfois souci au début, c’est la décompression de données sources vers la cible (par exemple, un livrable au format .zip ou .tar.gz sur un FTP, qu’on souhaite décompresser sur notre Data Lake). La configuration doit se faire à la fois au niveau des datasets et de l’activité Copy.

Pour décompresser un fichier, je dois spécifier, dans le dataset : le type de compression du dataset source en fonction du format de compression des données (en zipDeflate si c’est un zip, tarDeflate si c’est un .tar.gz…). Ensuite, je dois bien m’assurer que le filename (paramètre du dataset) n’est pas spécifié dans le dataset de Sink (cible) de l’activité Copy, il doit être vide. Seul le folder doit être spécifié.

Ensuite, deux cas :

  • Mon fichier compressé à un seul niveau (pas de sous-dossiers) : dans l’activité Copy, partie Sink, mettre le Copy Behavior à None.
  • Sinon, utiliser Preserve Hierarchy / Flatten Hierarchy.

Bon à savoir : il y a une option dans la partie Source de l’activité Copy, Preserve zip file name as folder (apparaît si le dataset de source à une compression zipDeflate) afin de spécifier si je souhaite que lors de la décompression, ADF créé un nouveau dossier en plus avec le nom du fichier, ou si je souhaite décompresser les fichiers directement à l’endroit spécifié.

Parallélisme et boucles imbriquées

Parfois, quand mon pipeline est trop imbriqué, je ne peux pas accéder aux variables de composants de niveau supérieur (ex : j’ai une itération ForEach, à l’intérieur une condition IF, et à l’intérieur du IF une activité Copy : je ne pourrai pas obtenir le nom de chaque fichier de l’itération). Il faudra utiliser le composant Set Variable pour avoir une variable globale dynamique.

Cependant, si j’ai un mode d’exécution en parallèle défini dans la boucle ForEach, il est possible que la variable utilisée par une pipeline  (lorsque je l’assigne avec SetVariable) soit modifiée par l’autre pipeline entre temps (c’est le problème des variables partagées), ce qui crée des incohérences dans la suite des traitements. Il faut donc garder ce point en tête.

Scheduling et Monitoring

Je peux configurer des notifications en cas de succès ou échec de mon pipeline via la catégorie Alert & Metrics dans la partie Monitoring.

Si après création des alertes, rien ne se passe lorsque vous lancez vos pipelines en mode Debug pour tester, cela ne vient pas de la configuration (j’ai perdu du temps sur ce point…). Simplement, que ce soit pour l’affichage des métriques au niveau du dashboard ou le déclenchement des alertes, seuls les runs de pipelines déclenchées par un trigger sont prises en compte. Pour vos debugs, vous ne pourrez que consulter l’onglet Debug des runs de pipeline. Cela fait sens après réflexion, afin de ne pas polluer les métriques d’exécution avec les tests.

Ainsi, pour lancer mon pipeline en “conditions réelles” (déclenchement des triggers…), je peux aller dans mon pipeline et faire Add Trigger -> Trigger now.

Azure Data Factory : réussissez vos pipelines ETL !

C’est aussi par cette manière (via le pipeline) qu’il est mieux de créer le trigger scheduler.

Précisons aussi que le dashboard, et globalement les métriques de run, ont un lag. Les résultats ne sont pas en temps réel, il faut souvent attendre quelques minutes entre l’exécution du pipeline et son affichage.

De même, attention pour l’activation des triggers : le déclencheur ne s’applique que lorsque vous avez publié (via Publish) dans Data Factory, et non lorsque vous enregistrez le déclencheur dans l’interface utilisateur. En effet, un trigger est aussi un fichier JSON (ARM) publié sous Artifact lors du Build.

Les alertes créées et déclenchées sont aussi disponibles au niveau du groupe de ressource : partie Monitoring -> Alerts. Ce sont les_ Action Groups_ qui sont utilisés par les alertes pour envoyer des notifications (email…). Pour les modifier : groupe de ressource -> partie Monitoring -> Alerts -> Manage actions.

DevOps

En suivant les bonnes pratiques DevOps, nous chercherons pour notre projet à :

  • avoir des environnements séparés : dev, recette, production ;
  • avoir le versioning de nos pipelines (ici sur git) ;
  • avoir un pipeline de release pour le déploiement de nos modifications réalisées en dev sur les autres environnements.

Mise en place des environnements

On déploie généralement, pour chaque projet, un groupe de ressources par environnement. Cet article n’ayant pas vocation à revenir sur ce point, ni expliquer les étapes, vous trouverez plus d’informations dans cet article.

Azure Data Factory : réussissez vos pipelines ETL !

Extrait de l’article mentionné précédemment

Services liés

Les services liés correspondent aux différents services avec lesquels vous interagissez dans votre pipeline : connexions aux sources de données, Key Vault pour le stockage des mots de passe, Databricks etc. Comme vous aurez aussi ces ressources répliquées dans les différents environnements, il vous faudra, pour le pipeline de release, variabiliser leur connexion (nous verrons juste après comment le faire).

Dans tous les cas, il vous faudra un Key Vault pour le stockage des identifiants nécessaires à l’accès des autres services. Vous ne devriez jamais les stocker sur une branche git.

Pour l’accès à Key Vault, attention de bien ajouter la Managed Identity de votre ADF (quand vous créez un Linked Service avec Key Vault, le panneau va afficher le nom de la Managed Identity) aux Access Policies du Key Vault.

Pensez aussi à donner un nom générique à vos services liés. Si vous êtes actuellement dans l’environnement de dev, n’appelez pas votre service “KeyVaultDev” mais simplement “KeyVault”. Cela facilite le déploiement entre environnements.

Versioning

ADF bénéficie d’une intégration simplifiée avec Azure DevOps. Notre premier réflexe devrait être de lier directement le projet ADF de l’environnement de dev à un un projet Git spécifique. ADF supporte les repos Github et Azure DevOps. Pour cela, rendez-vous dans Paramètres -> Source Control -> Git Configuration.

La spécification de tout ce qui est fait sur ADF (pipelines, datasets, services liés…) est au format JSON. Ce sont ces fichiers de définition qui seront sauvegardés sur Git. Le lien fait, chaque enregistrement des modifications effectuées sur ADF mettra à jour les fichiers correspondants sur Git.

Pensez à créer une branche de dev dans votre repo Git, et à la sélectionner dans ADF lorsque vous réalisez des modifications / développements. Une fois vos modifications terminées, vous ferez une pull request sur la branche de master.

Azure Data Factory : réussissez vos pipelines ETL !

Sur le repository qui sera lié avec ADF, vous trouverez donc vos branches standards (dev, master), et une branche supplémentaire, adf_publish, que ADF va se charger de créer pour vous. Le but de cette branche est d’héberger le code ARM (Azure Resource Manager, l’IaC de Azure) de votre projet qui sera généré une fois que vous cliquez sur “Publish” dans l’interface principale. C’est en quelque sorte l’étape de Build. ADF prend votre projet et transforme cela en config ARM.

Il est donc important de comprendre que seul l’ADF de dev sera lié avec votre repository Git. Les ADF dans les groupes de ressource de recette et production ne sont pas liés à un repository et seront “alimentés” par le pipeline de release.

Pour cliquer sur ce bouton “Publish”, vous devez vous trouver sur la branche master. D’où le cycle : développements sur la branche de dev -> pull request sur master -> publish.

Azure Data Factory : réussissez vos pipelines ETL !

Je peux aussi exporter en code ARM tout mon projet ADF (pipelines, datasets, integrations runtimes, triggers…) réalisé manuellement dans le mode visuel via le bouton ARM Template -> Export ARM Template dans l’interface principale.

L’avantage, c’est qu’une fois que vous avez votre code ARM dans la branche adf_publish, vous êtes prêt pour l’étape de déploiement dans les différents environnements (le pipeline de Release). L’inconvénient, c’est que cette étape de Build est manuelle, il faut cliquer sur le bouton Publish.

Variabilisation

Nous devons préparer notre projet pour qu’il soit facilement paramétrable. Vous réaliserez normalement ces étapes de variabilisation avant d’avoir cliqué sur Publish, de sorte que les templates ARM soient prêts à l’emploi. Ces variables pourront être modifiées dans le pipeline de Release, et ainsi permettre des valeurs changeantes selon les environnements, c’est là le but.

Paramètres du pipeline

Pour changer la valeur des paramètres du pipeline selon l’environnement, je dois créer un fichier arm-template-parameters-definition.json (et pas le arm_template_parameters.json par défaut qui ne contient pas les paramètres du pipeline). Il doit se situer à la racine de la branche sur le repository git du projet.

Avec facilité, je peux aussi aller dans Manage -> Parameterization template pour l’éditer directement, cela créera automatiquement le fichier si il n’est pas déjà présent.

Voici un exemple de configuration de ce fichier.

De mon point de vue, il n’est pas forcément intéressant de variabiliser les paramètres d'un pipeline, car vous devrez les spécifier de nouveau lors de la mise en place du trigger. Dans une approche multi-environnements, vous créerez généralement le trigger final dans l’environnement de production, et vous n’avez pas forcément envie de déployer un trigger actif d’un environnement à l’autre. C’est un choix personnel, à considérer.

Les paramètres globaux

Les paramètres globaux sont récents (été 2020) et permettent d’avoir des paramètres référencés par plusieurs pipelines. En effet, si plusieurs pipelines ont des paramètres en commun, les spécifier pour chaque pipeline entraînerait une duplication.

Un autre avantage, c’est qu’il est simple pour nous de les inclure dans les templates ARM : il suffit de cliquer sur Include in template ARM dans leur menu.

Variabiliser les paramètres de vos services liés

La variabilisation des paramètres des services liés n’est pas intuitive. Pour cela, il faut cliquer sur ce bouton au survol du service lié :

Azure Data Factory : réussissez vos pipelines ETL !

Ensuite, vous aurez un code JSON définissant le service lié, par exemple :

{
    "name": "AzureDataLakeStorage",
    "properties": {
        "annotations": [],
        "type": "AzureBlobFS",
        "typeProperties": {
            "url": "https://mondatalake.dfs.core.windows.net/",
            "accountKey": {
                "type": "AzureKeyVaultSecret",
                "store": {
                    "referenceName": "AzureKeyVault",
                    "type": "LinkedServiceReference"
                },
                "secretName": "adls-acess-key"
            }
        }
    }
}

Ce qui nous intéresse, c’est la clé typeProperties et ses enfants. Ce sont les propriétés que l’on va pouvoir variabiliser. Par exemple, secretName.

Rendez-vous ensuite dans la panneau de paramétrage du template :

Azure Data Factory : réussissez vos pipelines ETL !

Dans la partie surlignée en rouge, ajoutez la clé, ici secretName. Notez que si notre clé avait été imbriquée dans une autre clé, il aurait fallu ajouter le tout pour respecter l’arborescence.

Pipeline Build/Release

Comme vous le savez désormais, la phase de Build est réalisée manuellement par un clic sur le bouton Publish. Ce que nous souhaitons maintenant, c’est déployer nos ressources sur d’autres environnements. On considère que la partie de variabilisation précédente est faite, et que notre template ARM est ainsi prêt dans la branche adf_publish.

Azure Data Factory : réussissez vos pipelines ETL !

Workflow de déploiement ADF( doc )

Sur Azure DevOps, rendez-vous dans Pipelines -> Releases. Créez un nouveau pipeline. A terme, on cherche à accomplir le résultat suivant :

Azure Data Factory : réussissez vos pipelines ETL !

Votre Artifact doit être de type Azure Repository. Sélectionnez ensuite le projet, puis le repo dans lequel se situent les fichiers de l’ADF de dev. Sélectionnez ensuite la branche adf_publish. Il est possible de faire en sorte que votre pipeline de release se déclenche automatiquement à chaque nouveau commit sur la branche (donc à chaque Publish).

Nous allons ensuite ajouter deux stages, l’un pour la recette (staging), l’autre pour la production. Les deux seront identiques dans leur structure, seule la valeur des variables va changer. Créez donc d’abord celui de Staging, puis dupliquez le par la suite, en changeant simplement les variables utilisées (on y vient après).

A l’intérieur du stage, ajoutez une activité ARM Deployment. Sélectionnez les informations demandées puis le groupe de ressource dans lequel faire le déploiement (ici, celui de staging). Spécifiquement, c’est la partie Template et Override template parameters qui nous intéresse. On va remplacer les valeurs par défaut de nos paramètres de dev (d’où la partie sur la variabilisation pour qu’on puisse les surcharger). Vérifiez auparavant que le chemin pour template et template parameters sont corrects. En effet, ARM se compose toujours de deux fichiers : le fichier de templating et le fichier de paramètres.

Azure Data Factory : réussissez vos pipelines ETL !
Azure Data Factory : réussissez vos pipelines ETL !

Vous pourriez ici, si vous le souhaitiez, mettre directement les valeurs sans passer par des variables. Cependant, l’avantage des variables est de permettre de ne pas avoir à modifier ce fichier pour chaque environnement, seule la valeur de la variable changera. On utilise les groupes de variables à cette fin.

L’intérêt de créer un groupe de variables, plutôt que désigner directement des variables dans la pipeline de release, c’est qu’on peut créer un groupe de variables par environnement, contenant pour chacun les mêmes variables (même nom), et simplement les assigner chacun à leur stage respectif. Ainsi, nous n’avons quasiment rien à modifier lorsque nous dupliquons un stage.

Pour créer un groupe de variables, rendez-vous dans Pipelines -> Library.  Ici, nous avons créé un groupe par stage, et un autre groupe pour les variables statiques qui ne changent pas entre les environnements.

Azure Data Factory : réussissez vos pipelines ETL !

Ensuite, retournons dans notre pipeline de Release, partie Variable, puis Variable groups. Cliquez sur Link variable group, et ajoutez chaque groupe de variables créé précédemment, en l’assignant au bon stage (notion de scope).

Azure Data Factory : réussissez vos pipelines ETL !

Cela fait, nous pouvons retourner où nous en étions dans notre pipeline de Release (la partie Override template parameters de l’activité ARM Template Deployment) et référencer les variables avec cette syntaxe : $(variable).

ATTENTION : si je change des paramètres dans le fichier ARMTemplateParametersForFactory.json (suite au retrait de paramètres des services liés etc. et un nouveau Publish) et que je lance mon pipeline de release, j’aurai un message d’erreur du type : “Deployment template validation failed: The template parameters ‘BlaBla’ in the parameters file are not valid; they are not present in the original template and can therefore not be provided at deployment time...” Pourquoi ? Car dans la partie Override template parameters de la tâche ARM template deployment, les anciens paramètres y sont toujours présents, et il vous faudra donc les supprimer.

Il ne vous reste plus qu’à cliquer sur Create release et tester que le déploiement de vos ressources dans les différents environnements se passe comme prévu.

Au fur et à mesure de vos cycles de modifications et de déploiement, vous constaterez que les suppressions de composants (pipelines, services liés, datasets…) réalisées en dev ne sont pas répercutées dans les autres environnements. Cela est dû au mode de déploiement ARM de l’activité ARM template deployment de votre pipeline, qui est mis sur Incremental. Il existe diverses solutions (mettre le mode Complete à la place, ajouter l’activité Azure Data Factory Delete Items de cette extension, dans le pipeline de Release, utiliser un script Powershell), voir le sujet stackoverflow pour ce point.

Conclusion

Vous disposez désormais de bonnes techniques pour améliorer l’organisation, l’exécution et le déploiement de vos pipelines ! Pour aller plus loin, vous pouvez vous renseigner sur l’IaC avec Terraform et son intégration dans votre pipeline Azure DevOps, notamment à travers ce livre !

Utilisation de Logbook pour tracer les appels HTTP au sein d'une API REST Spring Boot

$
0
0
Utilisation de Logbook pour tracer les appels HTTP au sein d'une API REST Spring Boot

Dans le cadre de l’une de mes missions, nous avons mis en place une API de recherche de données sur des entreprises développée à l’aide de Spring Boot. Les données étaient collectées auprès de divers fournisseurs d’information dont une base de données ElasticSearch contenant les données propres au client.

Dans ce contexte, nous avions un besoin de traçabilité sur le comportement de notre API ainsi que nos interactions avec nos fournisseurs.
Afin de répondre à ce besoin, nous avons utilisé Logbook, une librairie open source de log Java développée par Zalando.

Voyons ensemble comment Logbook nous a permis de répondre à cette problématique.

Les mains dans le code

Créons notre application

Nous allons donc démontrer l’utilisation de Logbook à travers le développement d’une API REST qui va exposer un endpoint de recherche d’entreprises.
Pour cela, nous allons créer une application maven avec deux sous modules Spring Boot :

  • Une API REST exposant notre endpoint de recherche (module search-api)
  • Une API qui sera notre fournisseur d’information et sera appelée par notre API de recherche (module provider-api)

Commençons par créer notre projet multi-module maven. Pour cela, on crée un simple projet maven dans lequel nous allons supprimer le dossier src et ajouter les dépendances que l’on va utiliser dans nos sous modules.

Nous aurons deux sous module Spring Boot (version 2.3.1) écrits en Java 11

Nous utiliserons également les dépendances à lombok pour nous abstraire des boiler plates classiques et le client HTTP Feign (voir https://blog.ippon.fr/2016/09/21/feign-encore-un-client-http/ ).

Nous aurons ainsi le pom.xml suivant

...
<properties>
        <spring-boot.version>2.3.1.RELEASE</spring-boot.version>
        <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
        <lombok.version>1.18.16</lombok.version>
        <logbook.version>2.1.0</logbook.version>
        <squareup-okhttp.version>4.1.0</squareup-okhttp.version>
        <feign.version>10.11</feign.version>
    </properties>

...

<dependencyManagement>
       <dependencies>

           ...

           <!-- Dépendances qui va nous permettre via les annotations de nous abstraire de l'écriture de boiler plate
           type constructeurs, getters/setters, etc -->
           <dependency>
               <groupId>org.projectlombok</groupId>
               <artifactId>lombok</artifactId>
               <version>${lombok.version}</version>
               <scope>provided</scope>
           </dependency>

           <!-- Dépendance à la librairie que l'on va présenter dans ce blog pour logger les requetes HTTP -->
           <dependency>
               <groupId>org.zalando</groupId>
               <artifactId>logbook-bom</artifactId>
               <version>${logbook.version}</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>

           <!-- Dépendances au client HTTP que l'on va utiliser -->
           <dependency>
               <groupId>io.github.openfeign</groupId>
               <artifactId>feign-core</artifactId>
               <version>${feign.version}</version>
           </dependency>
           <dependency>
               <groupId>io.github.openfeign</groupId>
               <artifactId>feign-jackson</artifactId>
               <version>${feign.version}</version>
           </dependency>
           <dependency>
               <groupId>io.github.openfeign</groupId>
               <artifactId>feign-java11</artifactId>
               <version>${feign.version}</version>
           </dependency>
           <dependency>
               <groupId>io.github.openfeign</groupId>
               <artifactId>feign-okhttp</artifactId>
               <version>${feign.version}</version>
           </dependency>

           <!-- Dépendance au client okhttp3 utilisée par les libs feign-okhttp et par logbook-okhttp
           Cela va nous permettre de ne pas avoir de conflit entre les versions utilisées par ces libs -->
           <dependency>
               <groupId>com.squareup.okhttp3</groupId>
               <artifactId>okhttp</artifactId>
               <version>${squareup-okhttp.version}</version>
               <scope>compile</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>

   ...

</project>

Nous pouvons maintenant créer nos deux sous modules search-api et provider-api.

Search API

Pour construire notre première API REST, nous allons construire notre POM en spécifiant notamment les dépendances à logbook que nous allons utiliser

...

   <dependencies>
       ....

       <!-- Dépendances pour ajouter logbook à notre API spring boot avec la configuration par défaut  -->
       <dependency>
           <groupId>org.zalando</groupId>
           <artifactId>logbook-spring-boot-starter</artifactId>
       </dependency>
       <!-- Dépendance pour utiliser logstash dans nos log généré par logbook -->
       <dependency>
           <groupId>org.zalando</groupId>
           <artifactId>logbook-logstash</artifactId>
       </dependency>
       <!-- Dépendance pour utiliser logbook avec le client okhttp -->
       <dependency>
           <groupId>org.zalando</groupId>
           <artifactId>logbook-okhttp</artifactId>
       </dependency>

       ...
   </dependencies>

...

Au niveau du code fonctionnel, nous allons mettre en place une architecture simple composée de :

  • Un controller qui va exposer notre endpoint
  • Un service qui va être appelé par le controller et appliquer la logique métier
  • Des modèles d’objets qui vont être utilisés

On a donc les classes suivantes :

  • Le controller qui va exposer notre endpoint de recherche via une méthode GET. Il va se charger de récupérer les critères de recherches et les passer au service de recherche qui va nous renvoyer les sociétés trouvées :
package fr.ippon.search.functional.controller;

import fr.ippon.search.functional.model.Company;
import fr.ippon.search.functional.model.Criteria;
import fr.ippon.search.functional.service.SearchService;
import lombok.AllArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@AllArgsConstructor
public class SearchController {

   private final SearchService searchService;

   @GetMapping("/companies")
   public ResponseEntity<List<Company>> searchCompanies(Criteria criteria) {
       return ResponseEntity.ok(searchService.searchCompanies(criteria));
   }
}
  • Un service qui va utiliser notre client pour renvoyer la liste de Company. Ici on va juste se contenter de renvoyer la réponse de notre provider :
package fr.ippon.search.functional.service;

import fr.ippon.search.functional.client.ProviderClient;
import fr.ippon.search.functional.model.Company;
import fr.ippon.search.functional.model.Criteria;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@AllArgsConstructor
@Slf4j
public class SearchService {

   private final ProviderClient providerClient;

   public List<Company> searchCompanies(Criteria criteria) {
       try {
           return providerClient.searchCompanies(criteria);
       } catch (Exception exception) {
           log.warn("Call to provider api went wrong", exception);
           return List.of();
       }
   }

}
  • Une classe Company qui va contenir les informations d’une entreprise et une classe Criteria, qui comme son nom l’indique correspond à nos critères de recherche.
package fr.ippon.search.functional.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Company {
   private String name;
   private String streetName;
   private String country;
   private Status status;
}
package fr.ippon.search.functional.model;


public enum Status {
    A,
    C;

}
package fr.ippon.search.functional.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Criteria {
   private String name;
   private String country;
}
  • Un client HTTP qui va faire appel au provider grâce à la librairie Feign. Pour plus de détails sur l’utilisation de cette librairie, je vous invite à consulter l’article cité plus haut ou bien sur leur github :
package fr.ippon.search.functional.client;

import feign.Headers;
import feign.QueryMap;
import feign.RequestLine;
import fr.ippon.search.functional.model.Company;
import fr.ippon.search.functional.model.Criteria;

import java.util.List;

@Headers({"Content-Type: application/json"})
public interface ProviderClient {

    @RequestLine("GET /provider/companies")
    List<Company> searchCompanies(@QueryMap Criteria criteria);

}

Maintenant que la logique métier (plutôt simple ici) est implémentée, nous allons mettre en place la configuration nécessaire pour utiliser notre librairie afin d’avoir de jolis logs JSON de nos requêtes.

Ajoutons dans notre répertoire resources le fichier logback-spring.xml pour avoir la génération de logs JSON et utiliser le logger logbook.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
    </appender>

    <logger name="fr.ippon" level="INFO" />
    <logger name="org.springframework" level="ERROR" />
    <!-- Ajout de logbook dans les logs -->
    <logger name="org.zalando.logbook" level="TRACE" />

    <root level="info">
        <appender-ref ref="CONSOLE" />
    </root>

</configuration>

Logbook met en place une configuration assez flexible. En effet, il offre la possibilité de surcharger plusieurs composants que l’on peut retrouver dans sa classe d’AutoConfiguration.
Par défaut, nous avons les logs formatés de cette manière :

{
  "@timestamp": "2020-10-27T11:26:38.860+01:00",
  "@version": "1",
  "message": "{\"origin\":\"remote\",\"type\":\"request\",\"correlation\":\"b73a97811bd040f3\",\"protocol\":\"HTTP/1.1\",\"remote\":\"0:0:0:0:0:0:0:1\",\"method\":\"GET\",\"uri\":\"http://localhost:8080/companies?name=ippon\",\"headers\":{\"accept\":[\"*/*\"],\"accept-encoding\":[\"gzip, deflate\"],\"cache-control\":[\"no-cache\"],\"connection\":[\"keep-alive\"],\"content-type\":[\"application/json\"]}}",
  "logger_name": "org.zalando.logbook.Logbook",
  "thread_name": "http-nio-8080-exec-2",
  "level": "TRACE",
  "level_value": 5000
}

On voit que le JSON généré par Logbook se retrouve sérialisé dans l’attribut message... Pas forcément pratique à exploiter tel quel...

Nous allons donc surcharger la configuration de Logbook par défaut pour lui dire qu’on utilise ici Logstash, et ainsi avoir quelque chose de plus lisible :

package fr.ippon.search.configuration;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.zalando.logbook.HttpLogFormatter;
import org.zalando.logbook.Sink;
import org.zalando.logbook.json.JsonHttpLogFormatter;
import org.zalando.logbook.logstash.LogstashLogbackSink;

@Configuration
@AllArgsConstructor
public class LogbookConfiguration {

    private final ObjectMapper objectMapper;

    @Bean
    public Sink sink() {
        HttpLogFormatter formatter = new JsonHttpLogFormatter(objectMapper);
        return new LogstashLogbackSink(formatter);
    }

}

La librairie nous offre aussi la possibilité de configurer plusieurs types de clients HTTP pour qu’ils utilisent Logbook ( https://github.com/zalando/logbook/tree/2.1.0#okhttp-v3x ).

Dans notre cas, nous allons utiliser le client OkHttp v3.x qui est simple à configurer, à jour avec HTTP/2 et qui possède notamment une fonctionnalité de retry en cas d’erreur lorsque le service appelé utilise plusieurs adresses IP.

package fr.ippon.search.configuration;

import com.fasterxml.jackson.databind.ObjectMapper;
import feign.Client;
import feign.Feign;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import feign.okhttp.OkHttpClient;
import fr.ippon.search.functional.client.ProviderClient;
import lombok.AllArgsConstructor;
import okhttp3.ConnectionPool;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.zalando.logbook.Logbook;
import org.zalando.logbook.okhttp.GzipInterceptor;
import org.zalando.logbook.okhttp.LogbookInterceptor;

import java.util.concurrent.TimeUnit;

@AllArgsConstructor
@Configuration
public class ProviderClientConfiguration {

    private final Logbook logbook;

    private final ObjectMapper objectMapper;

    @Bean
    public ProviderClient providerClient() {
        return Feign.builder()
                .client(feignClient())
                .encoder(new JacksonEncoder(objectMapper))
                .decoder(new JacksonDecoder(objectMapper))
                .target(ProviderClient.class, "http://localhost:8081");
    }


    private Client feignClient() {
        return new OkHttpClient(
                new okhttp3.OkHttpClient.Builder()
                        .connectionPool(new ConnectionPool(100, 1, TimeUnit.HOURS))
                        .addNetworkInterceptor(new LogbookInterceptor(logbook))
                        .addNetworkInterceptor(new GzipInterceptor())
                        .build());
    }

}

On a maintenant notre Search API que vous pouvez démarrer et appeler en faisant un simple curl.

Par exemple :

curl -X GET 'http://localhost:8080/companies?name=ippon'

Vous l’aurez compris pour l’instant elle ne renverra qu’une liste vide mais ce qui nous intéresse ce sont les logs que nous a générés Logbook.

On va s'intéresser aux trois derniers logs pour voir qu’on a bien notre log d’erreur lors de l’appel au provider, suivi par le log de requête et celui de la réponse que nous a généré Logbook, le tout au format JSON.

En effet Logbook attend que la requête se finisse pour nous logger en même temps la requête et la réponse.

Utilisation de Logbook pour tracer les appels HTTP au sein d'une API REST Spring Boot

On a donc pour le curl ci-dessus les logs Logbook suivants :

Log de requête :

{
  "@timestamp": "2020-10-31T12:58:29.460+01:00",
  "@version": "1",
  "message": "GET http://localhost:8080/companies?name=ippon",
  "logger_name": "org.zalando.logbook.Logbook",
  "thread_name": "http-nio-8080-exec-2",
  "level": "TRACE",
  "level_value": 5000,
  "http": {
    "origin": "remote",
    "type": "request",
    "correlation": "9ea1f7b117218d79",
    "protocol": "HTTP/1.1",
    "remote": "0:0:0:0:0:0:0:1",
    "method": "GET",
    "uri": "http://localhost:8080/companies?name=ippon",
    "headers": {
      "accept": [
        "*/*"
      ],
      "accept-encoding": [
        "gzip, deflate"
      ],
      "cache-control": [
        "no-cache"
      ],
      "connection": [
        "keep-alive"
      ],
      "content-type": [
        "application/json"
      ],
      "host": [
        "localhost:8080"
      ]
    }
  }
}

Log de réponse :

{
  "@timestamp": "2020-10-31T12:58:29.462+01:00",
  "@version": "1",
  "message": "200 OK GET http://localhost:8080/companies?name=ippon",
  "logger_name": "org.zalando.logbook.Logbook",
  "thread_name": "http-nio-8080-exec-2",
  "level": "TRACE",
  "level_value": 5000,
  "http": {
    "origin": "local",
    "type": "response",
    "correlation": "9ea1f7b117218d79",
    "duration": 751,
    "protocol": "HTTP/1.1",
    "status": 200,
    "headers": {
      "Connection": [
        "keep-alive"
      ],
      "Content-Type": [
        "application/json"
      ],
      "Date": [
        "Sat, 31 Oct 2020 11:58:29 GMT"
      ],
      "Keep-Alive": [
        "timeout=60"
      ],
      "Transfer-Encoding": [
        "chunked"
      ]
    },
    "body": []
  }
}

On notera aussi l’attribut correlation dans l’objet http. Celui-ci est très utile car il permet tout simplement de faire le lien entre le log d’une requête et sa réponse associée.
Logbook nous offre aussi la possibilité de surcharger la génération par défaut de cet identifiant de corrélation (https://github.com/zalando/logbook#correlation)

Maintenant que notre Search API avec Logbook est prête, on va pouvoir ajouter notre Provider API pour voir comment Logbook fonctionne avec cette fois le client OkHttp.

Provider API

Pour la Provider API, on va implémenter une simple API qui renverra des Company en dur.

On va tout d’abord modifier le POM parent pour ajouter notre search-api en dépendance pour permettre à notre API de récupérer les modèles de données (Company et Criteria).

Dans ce genre de situation où on a des objets communs à plusieurs projets maven, il aurait été préférable de les regrouper au sein d’un projet maven à part mais pour rester focus sur notre sujet principal, on va aller au plus simple.

<?xml version="1.0" encoding="UTF-8"?>
...

    <dependencyManagement>
        <dependencies>

            <dependency>
                <groupId>fr.ippon</groupId>
                <artifactId>search-api</artifactId>
                <version>${project.version}</version>
            </dependency>
           ...

        </dependencies>
    </dependencyManagement>

   ...

</project>

Dans notre module provider-api, on va retrouver presque les mêmes dépendances que dans le search-api à l’exception des dépendances au client HTTP Feign notamment.

...

    <dependencies>
        ...

        <dependency>
            <groupId>fr.ippon</groupId>
            <artifactId>search-api</artifactId>
            <scope>provided</scope>
        </dependency>


        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

    </dependencies>
...

Nous allons avoir aussi moins de code à écrire que pour la Search API.

  • Le controller exposant notre endpoint qui va renvoyer en dur une liste de Company
package fr.ippon.provider.functional.controller;

import fr.ippon.search.functional.model.Company;
import fr.ippon.search.functional.model.Criteria;
import fr.ippon.search.functional.model.Status;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class ProviderController {

   @GetMapping("/provider/companies")
   public ResponseEntity<List<Company>> searchCompanies(Criteria criteria) {
       List<Company> companies = List.of(
               Company.builder()
                       .name("Ippon Technologie")
                       .status(Status.A)
                       .streetName("16 boulevard des tentacules")
                       .country("FR")
                       .build(),

               Company.builder()
                       .name(criteria.getName())
                       .status(Status.C)
                       .streetName("15 impasse du chat qui dort")
                       .country("BE")
                       .build()
       );

       return ResponseEntity.ok(companies);
   }

}

Pour le fichier de configuration application.yml, nous allons juste changer le port du server à mapper par notre application pour éviter un conflit avec le port par défaut qui sera utilisé par notre Search API.

server:
 port: 8081

Nos deux API sont désormais fin prêtes !!
Pour tester tout cela, il est toujours bon d’écrire des tests de composant (voir https://blog.ippon.fr/2020/08/31/cucumber-et-resttemplate/ ) plutôt que de tester “manuellement” en démarrant l’application. Pour vérifier le contenu de nos logs, on pourrait ainsi utiliser une classe de ce type (https://gitlab.com/cdamon/padbowl/-/blob/master/src/test/java/com/jhipster/padbowl/common/LogSpy.java ).

Mais pour ne pas rallonger l’article, nous prendrons un raccourci en démarrant nos deux API et en effectuant un simple curl.

Utilisation de Logbook pour tracer les appels HTTP au sein d'une API REST Spring Boot

Et voilà, notre API nous a bien renvoyé les entreprises que l’on avait renvoyées en dur côté provider.

Mais vous l’aurez compris, ce qui va nous intéresser est ailleurs.

Voyons donc ce qu’on a dans les logs de notre search-api

Utilisation de Logbook pour tracer les appels HTTP au sein d'une API REST Spring Boot

On voit donc ici en premier les logs de la requête sortante, effectuée par notre API de recherche vers l’API du provider au format Logbook, suivie par les logs de la requête que nous avons lancée.

Nous avons donc maintenant des logs propres nous permettant de tracer chaque requête effectuée par un client et la réponse associée avec un niveau de détail pertinent pour l’analyse et personnalisable.

Customisons notre Logbook

En fonction des besoins, on peut en effet influer sur la configuration par défaut via le fichier application.yml ou via la redéfinition des Beans d’autoconfiguration. Parmi les plus notables, on a la possibilité de :

  • Définir le statut HTTP pour lequel on veut afficher le body d’une réponse
  • Définir les endpoints pour lesquels utiliser Logbook ou non
  • Cacher certains headers et paramètres dans les logs
  • Définir des filtres sur différentes parties d’une requêtes
  • Adapter le format des logs (json, curl, splunk, ...)

Par exemple, nous pouvons simplement vouloir n’afficher le body d’une requête et de sa réponse qu’en cas d’erreur pour éviter d’avoir des logs trop verbeux.

Pour cela nous allons ajouter la configuration au fichier application.yml de search-api et provider-api :

logbook:
  minimum-status: 400
  strategy: "body-only-if-status-at-least"

Ici, nous disons à Logbook de nous logger les body seulement si le statut est supérieur ou égal à 400.
En redémarrant l’application, toutes les propriétés qu’on avait dans les logs sont là à l’exception du body.

Un des petits inconvénients (oui il y en a quand même), c’est l’absence de logs si une exception est levée au sein de l’application sans être gérée.

Pour mieux comprendre, enlevez le “try catch” dans la classe SearchService et démarrez uniquement le module search-api. Nous avons donc une exception qui est levée lors de l’appel au provider et pas de logbook dans les logs

Utilisation de Logbook pour tracer les appels HTTP au sein d'une API REST Spring Boot

N’étant jamais à l’abri d’une exception, nous avons au sein de ma mission, utilisé une autre librairie de Zalando permettant de gérer les exceptions au niveau des Controller pour renvoyer une réponse interprétable au client et garder des logs toujours lisibles et utiles au débuggage (voir https://github.com/zalando/problem-spring-web).

Conclusion

Souvent juste vus comme un moyen de déboguer son code, les logs peuvent souvent apporter bien plus d’informations fonctionnelles qu’on ne peut le penser. On a pu le voir ici en implémentant une utilisation de Logbook qui nous a permis d’afficher les détails du trafic HTTP au sein d’une API de manière structurée au format JSON.

On peut par exemple avoir via ces logs les temps de réponse de nos requêtes, les filtrer par critère de recherche, voir quelles requêtes sont tombées en erreur, etc. Autant de données essentielles aux stakeholders et aux développeurs pour avoir une aide à la supervision.

Au sein de ma mission, nous avons utilisé ces logs dans notre dashboard Splunk pour avoir par exemple des graphiques représentant ces informations et que nous utilisons au quotidien pour superviser notre environnement de production.

L’avantage d’une librairie comme celle qu’on vient d’utiliser, au-delà de son implémentation simplifiée par le module d’autoconfiguration, est sa capacité à être personnalisable pour l’adapter aux besoins.

Le code complet lié à cet article est disponible ici

Viewing all 927 articles
Browse latest View live