Comment réussir une migration d’une architecture monolithique vers une architecture serverless ?

Architecture, Cloud & DevOps

Introduction

L’accessibilité aux ressources « on demand » qu’offre le Cloud Computing permet aux entreprises de réduire considérablement les coûts opérationnels. Toutefois, l’utilisation peu productive de ces services peut, au contraire, entraîner une hausse significative et inattendue des coûts. Le choix d’une une architecture correspondant au mieux à ses besoins est un impératif.

Dans le cadre d’un projet R&D développé au sein d’Accetal, nous avons conçu une application web pour estimer le salaire d’un candidat à l’embauche en analysant son CV. Cette application développée en Flask, sur Python et déployée sur une instance EC2 d’AWS a une architecture Client-Serveur traditionnelle. Est-ce un bon choix ?

L’application s’exécutait sur l’instance EC2 en continu (24/24h). Les applications web sont généralement déployées sur ce type d’instances puisqu’elles sont faciles à gérer. Cependant, et étant donné qu’il s’agissait d’un nouveau projet et que l’application Web était peu utilisée, nous gaspillions des ressources en la déployant ainsi. Nous avons donc décidé de migrer l’application vers une architecture serverless, en utilisant les fonctions lambda déployées sur AWS. 

Quels problèmes peut-on rencontrer dans une telle migration ?

En utilisant une architecture Client-Serveur, les fonctions métiers (Traitement et prédiction) sont stockées au même endroit que les fonctions périphériques (stockage, système de notifications (Emails), formatage…). En addition, l’application est relativement petite et écrite de manière monolithique. Le code est peu commenté et ne suit pas les standards de développement.

Pour une architecture serverless beaucoup moins coûteuse, il est nécessaire d’utiliser des fonctions Lambda : un service informatique qui vous permet d’exécuter du code sans devoir approvisionner ou gérer des serveurs.

L’application est étroitement couplée avec le système d’exploitation : 

  1. Les CVs étaient stockés sur le serveur. 
  2. Les blocs de codes faisaient référence à des chemins relatifs par rapport au système.

Le principal défi consiste à découper l’application en plusieurs fonctions indépendantes, capables d’être hébergées sur des fonctions Lambda séparées. 

Notre application est composée de 3 Endpoints HTTP

  • Bloc de prédiction: Télécharger les CVs sur le disque local, les traiter et renvoyer une prévision de salaire adaptée.
  • Bloc de contact: Envoyer un e-mail de contact depuis l’application Web. 
  • Bloc de rating : Envoyer l’évaluation de l’estimation.

Le bloc principal du système est celui de prédiction. Il comporte deux modèles d’IA, un premier pour extraire les données pertinentes du CV uploadé, et un deuxième pour calculer une estimation de salaire à partir de la data récupérée.

Il est donc composé de 3 sous-blocs : 

  • Dépôt de CV (Upload) : Téléchargement et stockage des CVs 
  • Algorithme de Traitement et d’extraction (Parse and extract) : Analyse et extraction des données pertinentes afin de les stocker en format JSON 
  • Algorithme de Prédiction (Prediction) : Prédiction de salaire.

Solution : P

Pour passer à une architecture serverless, nous avons dû complètement modifier l’architecture du système. Cette nouvelle architecture repose entièrement sur des services AWS.

Pour contourner le problème des stockages des CVs, nous avons choisi de les héberger sur un bucket S3. Quant au problème des chemins relatifs, un refactoring s’imposait au moment du découpage. 

Malgré le découpage de l’application, la répartition n’était toujours pas parfaite et le découplage n’était pas fait à 100%. 

Fonctions lambda: mode d’emploi

Chargement d’un CV:

L’étape de chargement d’un CV se fait via une fonction lambda, en recevant une requête pour charger un fichier PDF dans un bucket S3.

Cette fonction est exposée via une API Gateway, qui transmet à son tour les requêtes HTTP après avoir effectué une validation des types contenus dans la requête et sur les headers HTTP. 

Un des problèmes rencontrés en travaillant sur cette partie était l’encodage des fichiers.  

Pour une raison que nous ignorons, des erreurs se produisaient lors de l’intégration des fonctions lambda, de l’API Gateway et de données sous format « multipart/form-data ». Pour contourner les potentielles sources de cette erreur, nous avons chargé les fichiers sous forme de données binaires à partir de jQuery. Nous avons ensuite envoyer les paramètres supplémentaires en tant qu’en-têtes HTTP personnalisées. Nous n’avions alors plus qu’à autoriser le transfert de ces en-têtes dans l’API Gateway. 

L’API Gateway de AWS dispose d’un moyen d’autoriser les requêtes CORS, mais, par défaut, elle ne prend en charge que les en-têtes application/json. En conséquence, pour un type de contenu spécifique, nous avions besoin de le configurer manuellement. 

Traitement/analyse des CVs et prédiction de salaire 

Ce bloc a été le plus dur à migrer vers une fonction lambda.  

En effet, l’application contenait des dépendances obsolètes. Nous avons alors tenté de la déployer avec ses dépendances sous format zip. Il est apparu que nous n’étions pas en mesure de transférer ces dépendances en archive à cause de certaines limitations de taille sur AWS.  

Pour pallier à ce problème, nous avons packagé l’application dans un conteneur Docker. Cela nous a permis d’obtenir une image de 3,9 Go (en sachant que la limite dans AWS est de 10 Go). Nous avons également modifié le code de récupération pour récupérer les CVs depuis le Bucket S3 au lieu du disque local du serveur. 

Envoie des e-mails de contact et d’évaluation 

Pour cette troisième et dernière fonction, nous avons conteneurisé le système de notification dans un conteneur Docker déployé dans une nouvelle fonction lambda, qui utilise le Simple Messaging System d’AWS. 

Étapes opérationnelles 

Les étapes suivantes ne sont pas spécifiques à notre application, elles sont nécessaire pour toute application suivant une architecture lambda :  

  • Dans un premier temps, afin de réduire nos frais, nous avons décidé d’héberger notre interface statique dans un bucket S3, accessible au public. 
  • Puis, nous avons configuré un service CloudFront pour exposer le bucket S3 et rediriger l’ensemble du trafic HTTP vers HTTPS. Nous avons utilisé Amazon Certificate Manager pour stocker les certificats SSL.  
  • Enfin, nous avons configuré le service Route 53 pour rediriger le trafic vers l’instance CloudFront. 

Conclusion 

Lorsque vous hébergez votre application dans un environnement cloud, vous pouvez réduire considérablement l’effort requis pour maintenir l’infrastructure et mettre, ainsi, l’accent sur la logique métier. Dans notre cas, nous avons exploité les services mis à disposition par le Cloud AWS pour gérer le stockage ainsi que les mails et les notifications.  Cela nous a permis de se consacrer sur le cœur de notre métier et de sa valeur ajoutée, c’est-à-dire l’analyse de documents et le traitement de l’Information. 

Dans le cadre d’une migration depuis une architecture web traditionnelle vers une architecture serverless, le concept clé consiste à comprendre votre logique métier et à l’isoler des fonctions périphériques. L’adhésion à des modèles de conception tels que le modèle de responsabilité unique (serverless) peut faciliter la tâche, en simplifiant la segmentation des applications en composantes plus petites. 

Keywords: Cloud, DevOps, AWS, AWS S3, AWS CloudFront, AWS Serverless, AWS Lambda, AWS EC2

Publié le 23/03/22

 


Partagez cet article :