Comment créer de beaux diagrammes d’architecture avec Python ? (~7min)

Cet article traite du diagramme technique dans les projets de Data Engineering, examinant des solutions telles que Google Slides et draw.io. Il met en lumière les défis d’alignement et conclut par une réflexion sur la simplification de ce processus.

Nicolas Le Gall Profile Picture
Nicolas Le Gall Data Scientist

Introduction

Dans un projet de Data Engineering, pour avoir une représentation complète du projet un diagramme technique résumant l’intégralité du pipeline est nécessaire. Comment créer rapidement et facilement un diagramme technique qui résume le data pipeline d’un projet ? C’est une question qui se pose fréquemment quand vient le moment de représenter son pipeline. Il est possible de le faire directement sur Google Slides en insérant des formes et des flèches, mais cela peut vite devenir pénible quand il faut redimensionner les différentes parties, les faire bouger ensemble ou aligner le texte. Il existe aussi des outils, comme draw.io, qui facilitent la création de diagramme en liant les différentes parties, cependant les problèmes d’alignement persistent et le pipeline sera réalisé uniquement avec des formes. 

Pour éviter tous ces problèmes et limitations, il est possible d’utiliser le package Diagrams qui, en quelques lignes de code, va produire un diagramme technique facilement lisible. De plus, le fait de créer un diagramme technique avec du code permet de réutiliser ce qui a été fait et si plusieurs personnes travaillent en collaboration sur un même diagramme, cela permet de facilement utiliser un outil de version control.

 

Nous allons voir dans le détail que Diagram est un outil flexible qui permet de produire des diagrammes techniques facilement tout en gardant une certaine clarté pour les lecteurs. Nous allons voir étape par étape comment utiliser ce package et ses fonctionnalités.

 

Pré-requis

 

Pour pouvoir utiliser le package diagrams, il est nécessaire d’avoir Python 3.6 ou une version supérieure. Ensuite, il faudra installer GraphViz car c’est ce qui permet d’afficher les graphiques. Vous pourrez trouver GraphViz dans la section « Getting Started » du github du projet Diagram.

Ensuite vous pourrez installer la librairie diagrams avec votre gestionnaire de package, et alors vous serez prêt pour commencer à créer de magnifiques diagrammes.

Pour ma part j’ai installé le package avec pip

pip install diagrams

Les bases

Dans ce package il y a 4 éléments différents :

    • Les diagrammes (Diagram)
    • Les groupes (Cluster)
    • Les liens (Edge)
    • Les noeuds

Les 3 premiers éléments sont caractérisés respectivement par une classe. Concernant les nœuds il existe de nombreuses classes proposées par différents fournisseurs comme AWS, Azure ou GCP pour les clouds ou encore Kubernetes. Vous pouvez retrouver l’ensemble des classes dans la documentation officielle du package.

Enfin, ces 4 éléments sont liés : en effet un diagramme est constitué de nœuds qui peuvent être rassemblés entre eux dans des groupes et qui sont reliés entre eux par des liens. Il faudra donc importer les classes nécessaires afin de pouvoir représenter correctement votre diagramme d’architecture.

Maintenant, essayons de coder un premier diagramme pour comprendre les bases du package.

 

    from diagrams import Diagram
    from diagrams.aws.analytics import Glue, Quicksight
    from diagrams.aws.database import RDS
    from diagrams.aws.management import Cloudwatch
    from diagrams.aws.storage import S3
    
    with Diagram('Pipeline - Global Overview', filename='Diagramm/Pipeline_GO', show=True, direction="LR"):    
        db = RDS('RDS Database')        
        jobs = Glue('ETL')        
        log = Cloudwatch('Logging')        
        bucket = S3('S3 Buckets')        
        dashboard = Quicksight('Dashboard')       
        
    db >> jobs >> bucket >> dashboard        
    jobs >> log

Ce diagramme décrit un pipeline de data engineering, utilisant une table contenue dans une base de données RDS, traité via l’ETL AWS Glue. Les résultats du traitement sont stockés dans un bucket S3 et les logs dans Cloudwatch. Enfin un dashboard Quicksight est branché sur le bucket S3.

Regardons en détail ce premier morceau de code. Nous importons tout d’abord la classe Diagram, qui est nécessaire pour produire un diagramme. Ensuite, nous importons quelques classes de nœuds du fournisseur AWS, par exemple RDS, Glue, etc. 

On crée alors un nouveau Diagram avec le nom ‘Pipeline – Global Overview’. Comme nous avons renseigné le paramètre filename, le diagramme va être enregistré à l’emplacement indiqué, attention le chemin indiqué est un chemin relatif (la racine sera la même que celle de l’emplacement où le code est exécuté, par exemple si le code est lancé depuis le bureau, le diagramme sera enregistré sur le bureau) et non un chemin absolu. Le paramètre show étant égal à True, Python va ouvrir immédiatement le diagramme après l’exécution du code. Le paramètre direction indique dans quel sens va être construit le graphique, ici il sera de gauche à droite (from Left to Right), qui est le paramètre par défaut. Les autres options sont de droite à gauche (RL), de haut en bas (TB) et de bas en haut (BT). À l’intérieur du diagramme, on crée plusieurs nœuds, avec les classes que nous avons importées. Pour créer un lien entre deux nœuds, il faut ajouter ‘>>’ entre les deux nœuds si on souhaite que la flèche aille de gauche à droite ou ‘<<‘ le cas échéant.

 

Afin de terminer avec toutes les classes du package, essayons un diagramme un peu plus complexe intégrant des clusters.

    from diagrams import Diagram, Cluster
    from diagrams.aws.analytics import Glue, Quicksight
    from diagrams.aws.database import RDS
    from diagrams.aws.management import Cloudwatch
    from diagrams.aws.storage import S3
    
    with Diagram('Pipeline - Global Overview', filename='Diagramm/Pipeline_GO', show=True, direction="LR"):    
        db = RDS('RDS Database')      
        
        with Cluster('AWS Glue (ETL) \n Data Engineering\n (Filter, join, rename...)'):        
            jobs = [Glue('Job1'), Glue('Job2')]    
            
        log = Cloudwatch('Logging')        
        bucket = S3('S3 Buckets')        
        dashboard = Quicksight('Dashboard')       
        
        db >> jobs >> bucket >> dashboard        
        jobs >> log

Ce diagramme décrit le même pipeline que le précédent, la seule différence est qu’ici il y a deux Jobs qui sont représentés dans AWS Glue.

L’utilité première des Cluster est de regrouper dans un même sous-ensemble des éléments semblables.

La deuxième utilité que je trouve aux clusters est de pouvoir délimiter de façon encore plus claire les différentes parties du pipeline, comme le montre l’exemple suivant :

    from diagrams import Diagram, Cluster
    from diagrams.aws.analytics import Glue, Quicksight
    from diagrams.aws.database import RDS
    from diagrams.aws.management import Cloudwatch
    from diagrams.aws.storage import S3
    
    with Diagram('Pipeline - Global Overview', filename='Diagramm/Pipeline_GO', show=True, direction="LR"):    
        with Cluster('RDS'):        
            db = RDS('PostgreSQL BDD\n stored in RDS')    
        
        with Cluster('AWS Glue (ETL)'):        
            jobs = Glue('Data Engineering\n (Filter,\n join,\n rename..)')    
            
        with Cluster('Cloudwatch'):        
            log = Cloudwatch('Monitoring Scripts')    
            
        with Cluster('S3'):        
            bucket = S3('S3 Buckets\n to store\n AWS Glue outputs')    
            
        with Cluster('Quicksight'):        
            dashboard = Quicksight('Dashboard\n for monitoring')    
            
        db >> jobs >> bucket >> dashboard    
        jobs >> log

Le code va produire encore une fois le même diagramme, mais l’apparence sera différente, en effet chaque partie d’AWS qui a été utilisée sera encore plus clairement identifiée.

Paramètres avancés

 

Maintenant que l’on sait utiliser les diagrammes, les clusters, les edges et les nodes, intéressons-nous à la personnalisation. Il y a deux objets personnalisables : les nodes et les edges.

 

Personnalisation des Edges

 

Regardons en premier lieu comment personnaliser les edges. Il y a 3 paramètres de personnalisation : la couleur, le style et le label. La couleur par défaut est le gris, mais si vous souhaitez paramétrer une autre couleur, les couleurs sont celles utilisées par le package matplotlib que vous pouvez retrouver ici</a >.

Ensuite, il est possible de jouer sur le style et il y en a 4 de disponibles :

    • Le style par défaut qui est une ligne continue
    • Bold, une ligne continue en gras
    • Dashed, la ligne est faite de tirets
    • Dotted, la ligne est faite de points

Il n’est pas possible de combiner des styles différents, i.e. avoir une ligne faite de tirets en gras.

Enfin, il est possible de rajouter un label sur un Edge si on souhaite expliciter ce que représente cet Edge.

Voyons voir ce que cela donne en code.

    from diagrams import Diagram, Cluster, Edge
    from diagrams.aws.analytics import Glue, Quicksight
    from diagrams.aws.database import RDS
    from diagrams.aws.management import Cloudwatch
    from diagrams.aws.storage import S3
    
    def arrow(color='black', style='line', label=None):    
        """    
            Function to define the edge between the part of the diagram    
            :param color: the color of the edge, could be any color    
            :type color: str    
            :param style: the style of the edge, could be dashed, dotted, bold or line (default)    
            :type style: str    
            :param label: the text you want to show on the edge    
            :type label: str    
            :return: Edge object with the different parameters we set up    
            :type: Edge()    
        """    
        return Edge(color=color, style=style, label=label)
        
    with Diagram('Pipeline - Global Overview', filename='Diagramm/Pipeline_GO', show=True, direction="LR"):    
        with Cluster('RDS'):        
            db = RDS('PostgreSQL BDD\n stored in RDS')    
            
        with Cluster('AWS Glue (ETL)'):        
            jobs = Glue('Data Engineering\n (Filter,\n join,\n rename..)')    
            
        with Cluster('Cloudwatch'):        
            log = Cloudwatch('Monitoring Scripts')    
            
        with Cluster('S3'):        
            bucket = S3('S3 Buckets\n to store\n AWS Glue outputs')    
            
        with Cluster('Quicksight'):        
            dashboard = Quicksight('Dashboard\n for monitoring')    
            
        db_mycharlotte >> arrow(color='red', style='bold') >> jobs >> arrow(color='red', style='bold') >> \    
        bucket >> arrow(color='red', style='bold') >> dashboard    
        
        jobs >> arrow(color='hotpink', style='dashed') >> log

Ici, j’ai créé une fonction, arrow(), qui produit par défaut une flèche noire en gras, que je préfère à la flèche par défaut du package. J’utilise ensuite cette fonction pour définir les différentes flèches que je souhaite avoir dans mon graphique. Quand on souhaite personnaliser un Edge, il faut explicitement le marquer dans le schéma du diagramme entre les deux nœuds concernés. Ici j’ai souhaité que les Edges du pipeline soient en rouge et en gras, sauf la flèche pour les logs qui est en rose et en pointillés. On peut voir cela dans les deux dernières lignes du code.

Personnalisation des Nodes

 

Abordons le deuxième point, la personnalisation des Nodes. Qu’est-ce que cela signifie ? La personnalisation des nodes, c’est afficher un Node avec une image qui n’est pas déjà pré-enregistrée dans la banque d’image du package et donc créer un Node qui n’existe pas.

Vous souhaitez représenter l’envoi d’un mail en cas d’erreur ? Cela n’est pas dans les options disponibles sur le package, mais il vous suffit de télécharger une image représentant un e-mail et en utilisant le Node Custom(), vous pourrez intégrer ce nouveau Node à votre diagramme. Il existe donc de nombreuses possibilités de Nodes et la seule limite est votre imagination.

    from diagrams import Diagram, Cluster, Edge
    from diagrams.aws.analytics import Glue, Quicksight
    from diagrams.aws.database import RDS
    from diagrams.aws.management import Cloudwatch
    from diagrams.aws.storage import S3
    
    with Diagram('Pipeline - Global Overview', filename='Diagramm/Pipeline_GO', show=True, direction="LR"):    
        with Cluster('RDS'):        
            db = RDS('PostgreSQL BDD\n stored in RDS')    
            
        with Cluster('AWS Glue (ETL)'):        
            jobs = Glue('Data Engineering\n (Filter,\n join,\n rename..)')    
            
        with Cluster('Cloudwatch'):        
            log = Cloudwatch('Monitoring Scripts')    
            
        with Cluster('S3'):        
            bucket = S3('S3 Buckets\n to store\n AWS Glue outputs')    
            
        with Cluster('Quicksight'):        
            dashboard = Quicksight('Dashboard\n for monitoring')    
            
        with Cluster('Devs'):        
            houcem = Custom('Houcem\n Lead DS', '.../Custom/houcem.png')        
            nico = Custom('Nico\n DS', '.../Custom/nico.png')        
            dev = [nico, houcem]    
            
        db >> arrow(color='red', style='bold') >> jobs >> arrow(color='red', style='bold') >> bucket >> \    
        arrow(color='red', style='bold') >> dashboard    jobs >> arrow(color='hotpink', style='dashed') >> log    
        
        houcem >> arrow(color='sandybrown', style='dotted') >> jobs    
        houcem >> arrow(color='sandybrown', style='dotted') >> log    
        nico >> arrow(color='blue', style='dotted') >> jobs    
        nico >> arrow(color='blue', style='dotted') >> log    
        nico >> arrow(color='blue', style='dotted') >> bucket    
        nico >> arrow(color='blue', style='dotted') >> dashboard   

Ici, j’ai choisi de représenter les développeurs qui ont travaillé sur ce projet en spécifiant sur quelles parties du pipeline ils sont intervenus. Pour que ça soit plus visuel, j’ai créé deux nouveaux Nodes avec les photos des développeurs et ainsi, on identifie clairement qui contacter en cas de soucis sur le pipeline.

Enfin une fois que l’on maîtrise les différentes fonctionnalités du package, on peut produire des diagrammes très fournis. En voici un exemple :

    from diagrams import Diagram, Cluster, Edge
    from diagrams.aws.analytics import Glue, GlueCrawlers, GlueDataCatalog, Quicksight
    from diagrams.aws.database import RDS
    from diagrams.aws.management import Cloudwatch
    from diagrams.aws.storage import S3, SimpleStorageServiceS3Object, SimpleStorageServiceS3BucketWithObjects
    from diagrams.custom import Custom
    
    with Diagram('Pipeline - Global Overview', filename='Diagramm/Pipeline_GO', show=True, direction="LR"):    
        with Cluster('RDS'):        
            db_mycharlotte = RDS('PostgreSQL BDD\n stored in RDS')    
        
        with Cluster('AWS Glue'):        
            crawler = GlueCrawlers('Glue\n Crawler')        
            data_catalog = GlueDataCatalog('Glue\n DataCatalog')        
            jobs = Glue('Glue Jobs')        
        
        with Cluster('Jobs'):            
            job = [Glue('Job for\n Activity\n transformation'),                    
                    Glue('Job for\n Appointment\n transformation')]    
        
        with Cluster('Cloudwatch'):        
            log = Cloudwatch('\n\n\nMonitoring Scripts')    
            
        with Cluster('S3'):        
            bucket = SimpleStorageServiceS3BucketWithObjects('S3 Buckets\n to store\n AWS Glue outputs')        
            
            with Cluster('Objects within S3 bucket'):            
                obj = [SimpleStorageServiceS3Object('Output from \nActivity\n Transformation Job'),                   
                        SimpleStorageServiceS3Object('Output from \nAppointment\n Transformation Job')]    
                
        with Cluster('Quicksight'):        
            dashboard = Quicksight('Dashboard\n for monitoring')    
            
        with Cluster('Devs'):        
            houcem = Custom('Houcem\n Lead DS', '.../Custom/houcem.png')        
            nico = Custom('Nico\n DS', '.../Custom/nico2.png')        
            dev = [nico, houcem]    
        
        db >> arrow(color='red') >> data_catalog    
        
        db << arrow(color='purple', style='dotted', label='Connect DB ') << crawler >> \    
        arrow(color='purple', style='dotted', label='to AWS Glue') >> data_catalog >> arrow(color='red') >> \    
        jobs >> arrow(color='purple') >> job    
        
        job >> arrow(color='hotpink', style='dashed') >> log    
        job >> arrow(color='red') >> bucket >> arrow(color='darkgreen') >> \    
        obj >> arrow(color='red') >> dashboard    
        
        houcem >> arrow(color='sandybrown', style='dotted') >> jobs    
        houcem >> arrow(color='sandybrown', style='dotted') >> log    
        nico >> arrow(color='blue', style='dotted') >> jobs    
        nico >> arrow(color='blue', style='dotted') >> log    
        nico >> arrow(color='blue', style='dotted') >> bucket    
        nico >> arrow(color='blue', style='dotted') >> dashboard 

On remarque tout d’abord que le code est quand même plus complexe que sur les différents diagrammes précédents. Ensuite, concernant le diagramme en lui-même, il donne un vrai aperçu détaillé du pipeline de traitement de la donnée depuis la base de données jusqu’au dashboard le tout dans l’environnement AWS.

Conclusion

 

Diagrams est un package qui permet de représenter des pipelines à travers des diagrammes, et cela, avec facilité et flexibilité. Si vous souhaitez avoir plus d’informations et utiliser des commandes plus avancées, je vous conseille de regarder le github du projet Diagrams et particulièrement dans la section Issues.

NB : Cet article a été librement inspiré par l’article Create Beautiful Architecture Diagrams with Python écrit par Dylan Roy et disponible ici

A voir absolument

Les articles les plus appréciés

Vous avez un projet de transfomation ? Parlons-en !