Lors de la conception d’une application web, opter pour une architecture Cloud Native est une manière simple pour obtenir une application scalable, hautement disponible et réduire les coûts de son infrastructure.
Imaginez avoir une application de liste de courses hébergée sur un service cloud, avec une API, une base de donnée, mais dont l’infrastructure ne coûte rien !
Dans cet article, nous allons suivre les étapes permettant de créer de bout en bout l’application Cloud Native suivante, basée sur le cloud AWS :
Nous allons voir les fondamentaux pour créer une architecture Cloud Native et comment automatiser la création des ressources de notre infrastructure avec Terraform.
Dans chaque section, des extraits de code Terraform seront fournit afin de vous guider dans la création de l’application.
Voici l’architecture cible que nous allons construire progressivement :
Tous les fichiers Terraform utilisés pour créer notre webapp, incluant les fichiers HTML et JS, sont disponible sur repository GitHub suivant : https://github.com/mdesc/cloud-native-webapp
N’oubliez pas d’exécuter la commande suivante dans le directory après avoir clone le projet : terraform init
Le Cloud-Native est une approche du développement et du déploiement d'applications qui se basent sur le cloud, faisant le plus souvent usage de microservices, de conteneurisation, d'automatisation et de serverless. Ces pratiques permettent aux applications de bénéficier d'une meilleure résilience, d’une mise à l’échelle simplifiée et de réduire les coûts liés à la maintenance des machines faisant tourner l’infrastructure.
💡 Avantages d’une architecture Cloud-Native :
- Scalabilité : Avec la capacité à ajuster dynamiquement les ressources en fonction de la demande, les applications cloud-native peuvent gérer efficacement les fluctuations du nombre d’utilisateurs et les pics de charges.
- Cost Management : Les architectures cloud-native permettent de ne payer que pour les ressources consommées, minimisant le gaspillage et optimisant les coûts.
Concrètement, dans le cadre de cet article, notre application de liste de courses utilise une architecture serverless, ce qui signifie que l'infrastructure sous-jacente est entièrement gérée par le fournisseur de services cloud, ici AWS.
Cela comprend la gestion des serveurs, des ressources de stockage et de la mise en réseau.
Nous n’avons donc pas à payer pour des ressources inutilisées ou à gérer la mise à l'échelle : les ressources nécessaires seront automatiquement allouées lorsque l'application sera sollicitée.
Terraform est un outil d'Infrastructure as Code (IaC) qui vous permet de définir et de provisionner une infrastructure en utilisant un langage déclaratif.
Dans une approche déclarative, vous définissez l'état désiré de votre infrastructure, et Terraform gère l'orchestration pour atteindre cet état. Cela contraste avec les outils IaC impératifs, comme Ansible, où vous devez spécifier les étapes pour atteindre un état désiré en configurant les ressources.
La nature déclarative de Terraform permet de se concentrer sur le résultat final plutôt que sur les détails procéduraux. Cela simplifie la gestion de l'infrastructure : il est plus simple de gérer les différentes versions de son infrastructure, faire des rollbacks etc.
💡 Configurer votre environnement local :
Voici un lien détaillant la démarche à suivre pour installer Terraform et le configurer avec la CLI AWS : https://developer.hashicorp.com/terraform/tutorials/aws-get-started/aws-build
Le but de cet article n’étant pas d’expliquer le fonctionnement de Terraform, voici quelques explications rudimentaires : https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code
Pour obtenir rapidement un premier exemple de notre future webaapp, nous allons commencer par créer les ressources nous permettant d’avoir un simple site web statique.
Nous allons dans un premier temps héberger notre application statique via un Bucket S3, qui sera exposé par un CloudFront (CDN), et donc accessible via une url cloudfront.net.
Concrètement, voici un schéma représentant l’architecture de notre webapp à la fin de cette première section :
Commençons par créer un Bucket S3 afin d’y stocker les fichiers statiques de notre site.
S3 peut être comparé à un Google Drive, mais pour application : nous allons rendre disponible des fichiers qui seront utilisé par les futures ressources de notre architecture.
Voici le code à mettre dans votre fichier main.tf
:
Vous pouvez ensuite exécuter la commande suivante pour créer les ressources décrites par le fichier main.tf
:
terraform apply --auto-approve
Nous nous attendions bien à avoir 2 ressources : un nom aléatoire pour notre bucket, (random_pet
), et un bucket S3 (aws_s3_bucket
).
⚠️ N’oubliez pas d’exécuter un terraform destroy --auto-approve
à chaque fois que vous avez finit de travailler sur votre infra, pour ne pas laisser des ressources tourner inutilement dans AWS, bien que l’ensemble des ressources de cet article soient dans le free tier : https://aws.amazon.com/free.*
Maintenant, nous pouvons créer des ressources permettant de gérer la visibilité du bucket, et y upload notre fichier HTML :
Intégrer CloudFront à notre architecture permet d’assurer que notre application est accessible rapidement partout dans le monde, et également nous évitons de communiquer le lien direct vers notre bucket, exposé à tout internet…
Pour cela, créons une distribution CloudFront avec les permissions nécessaires pour accéder au fichier index.html
afin de l’exposer via une URL “cloudfront.net” :
Pour passer d’un site web statique à dynamique, nous allons ajouter une base de donnée (DynamoDB), du code JS (exécuté via une Lambda function) et une API Gateway.
DynamoDB est la base de donnée NoSQL scalable de AWS. Contrairement aux bases de données relationnelles, DynamoDB e requiert pas de schéma fixe.
Cela rend DynamoDB idéal pour les applications web où les besoins en données peuvent évoluer rapidement.
Nous allons utiliser une fonction Lambda pour interagir avec la table DynamoDB, voici une introduction au service Lambda de AWS : https://docs.aws.amazon.com/lambda/latest/dg/welcome.html
Dans notre cas, nous devons upload un fichier lambda.js
sur un bucket S3 pour ensuite créer la ressource Lambda, qui exécutera le code JS :
Le code contenu dans lambda.js
va écrire ou lire dans la DB en fonction de l’évènement que la lambda reçoit en input. Libre à vous d’y ajouter des fonctionnalités ou de les séparer dans plusieurs lambdas pour améliorer l’architecture !
De plus, nous devons accorder à notre lambda certaines permissions pour qu’elle puisse interagir avec la table DynamoDB. Il suffit les définir via les fichiers policy.json
et asssume_role_policy.json
:
⚠️ N’oubliez pas de remplacer <your-account-id>
par l’id de votre compte AWS.
Nous utilisons ces 2 fichiers JSON pour créer les ressources aws_iam_role
et aws_iam_role_policy
de notre lambda :
Nous pouvons maintenant créer un fichier ZIP contenant le code source JS de notre lambda puis l’uploader sur le bucket S3 afin de créer la ressource Lambda dans AWS :
Si vous êtes attentif, vous avez remarqué la présence de permissions liée à des “logs” dans le fichier de policy de la Lambda.
Cela octroie à la lambda les autorisations requises pour créer des fichiers de logs dans une ressource CloudWatch, très utiles pour débugger notre code ou monitorer la lambda.
À cette étape, notre lambda peut lire et écrire dans la table myDB
de DynamoDB.
N’hésitez pas à tester cette interaction depuis la CLI AWS ou directement sur le dashboard AWS pour mieux comprendre son fonctionnement.
Commande pour invoke une lambda depuis la CLI : aws lambda invoke --function-name <nom_de_la_fonction>
Évidemment, nous ne voulons pas appeler notre lambda directement depuis notre future webapp. Il manque une couche essentielle à notre architecture : une API Gateway.
L'API Gateway est le point d'entrée pour notre webapp et agit comme une passerelle pour toutes les requêtes entrantes.
Le but est d’arriver à cette architecture :
Cette ressource fonctionne en associant des routes HTTP à des actions spécifiques, telles que l'appel de fonctions AWS Lambda (ça nous intéresses !).
Lorsqu'une requête est reçue sur une route spécifique, l'API Gateway la transmet à la fonction Lambda appropriée, qui peut ensuite traiter la demande et renvoyer une réponse.
Cette intégration permet une exécution de code serverless, basée sur des événements déclencheurs, depuis notre HTML :
Une fois cette ressource créée, nous ajoutons l'URL de l'API Gateway, pour créer le index.html
, via un script local (sed "s|BACKENDURL|$( cat ./site.env )|" ./site/template.html > ./site/index.html
) qui lit l’URL écrite dynamiquement dans un fichier site.env
en local suite à la création de l’API Gateway :
Il faut également ajouter cette ressource en dépendance à celle qui upload le fichier index.html
, pour ne pas risquer de l’upload avant que l’URL de l’API Gateway n’y soit écrite :
dans la ressource null_resource.upload_to_s3
, ajoutez null_resource.build
dans la liste depends_on
.
Nous pouvons maintenant tester l'intégration entre l'API Gateway et la Lambda. Pour ce faire, lancez Chrome sans vérifier les CORS dans un premier temps (avec le flag --disable-web-security
), ce qui nous permettra de voir si les requêtes sont traitées correctement sans restriction de politique CORS.
Vous avez maintenant une webapp pour gérer votre liste de courses avec des fonctionnalités simples, déployable sur le cloud AWS à tout moment !
En plus d’être hébergé par un cloud provider, notre infrastructure se basera sur plusieurs couches qui seront résilientes aux pannes, et qui seront mises à l’échelle automatiquement : nous n’avons rien à gérer, tout est délégué.
L’infrastructure mise en place durant cet article n’est évidemment pas parfaite et est simplifiée par soucis de simplicité, néanmoins voici quelques next-steps pour améliorer l’infrastructure :