Deployer un serveur Wordpress sur des EC2 avec database RDS, géré via un Auto Scaling Group (ASG) avec un Application Load Balancer (ALB) dans un subnet privée via Virtual Private Cloud (VPC), Internet et NAT Gateway sous Terraform¶
Je te propose de deployer une application sur des machines sur AWS avec de la haute disponibilité et haute résilience, dans un cloud privé et sécurisé 😎
Je te montre ici un Wordpress, mais c'est adaptable pour n'importe quelle application, serveur, Docker, etc.
Introduction¶
On va se réaliser une petite installation très basique, pour toute personne qui souhaite découvrir l'environnement AWS.
On va se monter un serveur Wordpress, mais libre à toi de servir ce que tu souhaites comme service. On va rendre ce serveur hautement disponible sous plusieurs avaibility zone (AZ) et aussi hautement disponible avec un application load balancer qui va distribuer à un auto scaling group, avec une RDS comme database SQL.
Un bastion sera disponible afin d'avoir un point d'entrée au serveur Wordpress via SSH.
Et comme on fait du clean code ici, on va se faire tout ça as code avec Terraform.
Presentation des ressources nécessaire¶
Dans ce post je vais parler avec des abbréviations. C'est pour cela que je te les définis toutes ici avant d'aller plus loin. Pour ce chapitre on aura besoin de
- VPC / Virtual Private Cloud : c'est un espace dans lequel on va pouvoir défini l'ensemble de notre installations. Sa particularité est qu'il est isolé.
- IGW / Internet Gateway : Element qui va permettre d'amener le réseau dans notre VPC
- NGW / Nat Gateway : Element qui permet d'amener du network dans un subnet privée
- ALB / Application Load Balancer : Comme son nom l'indique, il va permettre de distribuer la charge à des applications selon des métriques afin de répartir de façon équilibré les accès entre toute nos machines à notre disposition
- EC2 / Elastic Compute Service : C'est un service qui va nous permettre de faire pop des machines virtuelles (VM)
- ASG / Auto Scaling Group : Cet élément ci va nous permettre de faire scale-up et scale-down nos EC2 afin de pouvoir encaisser des pics de traffic
On aura besoin d'avoir 2 AZ différentes dans la même région, afin d'utiliser la RDS.
Pré-requis¶
Je ne perdrais pas trop de temps ici, mais il est nécéssaire d'avoir setté son Terraform, la configuration de son state qu'il soit en local ou en remote (bien mieux).
Initialisation du network¶
Création du VPC¶
C'est cet élément qui va contenir l'ensemble de mon network. On commence donc par le définir :
Création du subnet public¶
On va créer 2 subnets public qui hebergeront les bastions, ainsi que les internet gateway.
Création du subnet privée¶
Ici on va créer 2 subnets privées qui vont heberher la RDS et les EC2
Création de l'internet gateway¶
L'internet gateway et la ressource qui va pouvoir relier nos public subnet à internet.
Création des NAT gateway¶
Le NAT gateaway va te permettre de connecter tes subnets privés à internet, via le subnet public par cet gateway.
On lui affecte une ip publique pour cela.
Chaque NAT gateway doit avoir une adresse IP public, rendu possible via AWS Elastic IP. On ajoute un NAT gateway par public subnet.
Création des tables de routage - subnet public¶
Cela va permettre de faire transiter le traffic de l'extérieur du VPC, vers le subnet public.
On commence par créer une table de routage
On fait une association par subnet
On associe une nouvelle route pour distribuer le trafic du réseau public au réseau privé.
Création des tables de routage - subnet privé¶
Cela va permettre de faire transiter le traffic du subnet public au subnet privé
On défini ici une table de routage pour chacun de nos 2 subnets privés
On fait l'association d'une table à un subnet.
Dans chacun des tables de routage, on ajoute une nouvelle entrée. L'ajout de la route permet de faire passer les paquets.
Locals des subnets¶
Je te met ici les locals que j'ai utilisé pour définir mes 4 subnets :
Ici je défini des range d'IPs différents histoire qu'elle ne se surperpose pas.
Partie ALB - Application Load Balancer¶
L'ALB va être responsable de distribuer le traffic.
Ici on va lui spécifier de balancer notre traffic vers une certaine ressource, un target group.
On créer ici le target group. On spécifie le traffic ici en http. On fait ici simple, mais à toi de gérer plutôt la redirection de l'http vers https afin d'être secure. Il te faudra donc te fournir un certificat SSL pour cela.
On défini un healthcheck. Celui-ci permettra de tester un endpoint vers nos machines EC2, vers localhost:80/ afin de vérifier que le code de retour est bien compris entre 200 et 499. Si tel est le cas, la machine sera défini comme pouvant recevoir du traffic.
Ici on défini une association entre l'ASG et le target group défini de l'ALB.
Partie ASG - Auto Scaling Group pour Wordpress¶
On va réaliser tout le setup nécéssaire à nos machines EC2 pour monter une application Wordpress dessus.
On commence par définir ici l'ASG. C'est lui qui va être responsable de scale-up et scale-down nos EC2 en fonction des peaks de traffic afin de s'adapter à la demande.
Pour la suite, on va défini un script bash, qui embarque tout le nécéssaire afin de monter de façon automatique l'app Wordpress et ses dépendances nécéssaire à son bon fonctionnement.
Grosso modo, on va avoir besoin des dependances suivantes :
- httpd : serveur web pour servir notre wordpress
- php : le language dont est codé wordpress afin d'être lancé
- Quelques autres deps de php
On souhaite ensuite télécharger la dernière version de wordpress. On la dézippe, et on la place au bon endroit afin d'être servi par notre serveur web.
On appeller à l'outil sed afin de faire quelques changes dans notre fichier de configuration du serveur wordpress pour le bon lancement. En effet, il doit se connecter à notre database RDS SQL.
On lui injectera donc les bonnes valeurs pour :
- nom de la database
- un username et password qui servira de connexion string à la database
- une adresse, qui est l'endpoint de notre AWS RDS
Une fois la configuration setté, on lance notre serveur web.
Ce script sera automatiquement éxécuté à chaque nouvelle instance qui démarrera.
Et pour binder ce script précédent qui setup correctement nos EC2 à notre ASG qui les fait spawn, on utilise un launch template
La props user_data est resonsable du lancement du script au démarrage. On lui injecte notre script bash, et on le surcharge avec plusieurs variable.
Je te montre ici les quelques variables que j'ai du utiliser pour la définition des ressources précédent.
Partie RDS - Database SQL¶
L'application Wordpress à besoin d'une database de type SQL pour fonctionner.
Amazon propose le service managé RDS qui peut nous fournir ce que l'on a besoin. Elle scale up et down d'un point de vue stockage de donnée. On peut lui ajouter des read-replicas pour plus de performances, et aussi du multi-AZ pour avoir une plus grande réplication géographique des données.
On lui défini dans quel subnet on souhaite qu'elle fonctionne. Il lui faut de base au moins 2 subnets dans 2 AZ différents.
Danger
Tu pourrais te dire qu'installer une MariaDB directement sur les EC2 serait une meilleure opération. Etant donnée que les EC2 peuvent scale-up et scale-down, tu supprimerais automatiquement toute database par ces même opérations. C'est pour cela que l'on crée une RDS qui soit dissocié des EC2, et hautement disponible et scalable à tout moment. Ou alors avoir une MariaDB qu'a des fins de tests seulement
Partie debug avec l'ajout d'un EC2 Bastion¶
On appelle ici Bastion, une simple machine EC2. Celle-ci aura pour rôle de nous avoir un accès en SSH, afin de réaliser d'éventuelles maintenances ou tout autre service de débuggage sur nos serveurs hebergant Wordpress.
On utilise le port 22 pour ce protocol.
Ajout de l'EC2¶
On créer une nouvelle machine comme suit :
Ajout de la clé SSH¶
Tu as pu voir une option key_name dans la partie précedente.
Ici je créer une clée SSH, sur mon poste en local avec mon ssh-agent. Je vais garder sur mon poste la clée privée pour m'authentifier et créer un fichier dans mon workspace projet-perso-aws.pub.
Celle-ci va contenir la clée publique ce coup-ci. C'est elle qui sera disponible à la fois sur le Bastion, mais aussi sur mes diverses EC2 lancé par mon auto scaling group.
Je déclare le path qui mène vers ma clée publique :
Et je créer la ressource associé contenant ma clée publique, avec mon fichier local précédent :
Partie sécurité avec l'ajout de security group¶
SG pour l'ALB¶
Ici tu peux réaliser plusieurs opérations.
Si tu souhaites autoriser le protocol http, ouvre le port 80
Si tu souhaites autoriser le protocol https, ouvre le port 443
On autorise ici tout le traffic sortant.
SG pour l'ASG¶
Ici tu peux réaliser plusieurs opérations.
Si tu souhaites autoriser le protocol http, ouvre le port 80
Si tu souhaites autoriser le protocol https, ouvre le port 443
Ouvre le port 22 si tu souhaites autoriser le SSH, notemment pour faire fonctionner un bastion.
SG pour le bastion¶
Ici on souhaite autoriser pour l'ingress, le SSH vers nos EC2 qui hebergent notre application Wordpress. Il faut donc autoriser le port 22. Ajoute le port 80 si tu as besoin du net, pour réaliser d'autre type de débugage.
On laisse sortir tout traffic, concernant l'egress.
SG pour la RDS¶
Concernant la RDS, on doit lui ouvrir les ports 3306, qui est le port par default des databases MySQL pour l'ingress.
On laisse sortir tout traffic concernant l'egress.
Optimisations possible¶
Récuperer les AMI les plus récentes via un Data¶
Si tu souhaites avoir quelque chose de plus dynamique pour le choix de ton AMI pour tes différentes EC2, tu peux utiliser un data de terraform afin d'avoir par exemple la dernière version de cette image. Pratique pour notre use case !
Une fois que l'on défini une regex pour notre image souhaité, on peut ainsi l'appeller dans nos EC2 avec :
Sécuriser le mot de passe de la RDS & EC2 via AWS Secret Manager¶
On a fait une première version de notre application, de la façon la plus simple et intuitif possible pour débuter sur AWS.
Mais si vous devions utiliser ce code pour de la production, nous ferions une grave bêtise, d'un point de vue sécurité. Nous avons commis la faute d'inscrire le mot de passe de la RDS en dur, dans le code. Quiquonque accéde à notre répository Github, peut accéder à notre RDS, dump les data et absolument tout péter de notre application si il la souhaite.
Réalisons dans le prochain point, quelque chose de plus secure.
On commence par créer une nouvelle ressource qui va nous permettre de créer un mot de passe aléatoire
On créer un nouveau secret
On update ce précédent secret, et on lui ajoute notre mot de passe randomisé en value
On peut maintenant mettre à jour les templates de nos EC2 lancé via notre ASG. De même pour notre bastion
Ne pas oublier biensur de mettre à jour ce même password, lors de la création de notre RDS
Note
On aurait très bien pu utiliser un mot de passe que l'on souhaite et non randomisé. Pour cela, il foudra qu'il soit dans le répository, de façon crypté avec SOPS par exemple, lié à un Keyvault. Ainsi, plus de soucis qu'il soit dans le code Github.
Security groups & sources id¶
Pour améliorer l'aspect sécurité, on peut ajouter quelques optimisations au niveau des divers security groups.
En effet, quand on défini des sg de type ingress par exemple, on définit seulement un port entrant et sortant.
On peut très bien ajouter depuis quelle source, quelle origine provient le traffic. Et donc ajouter un source_security_group_id définissant quel security group on laisse passer. Exemple ici du security group appliqué à la RDS. On lui affecte en entrée le security group de l'auto scaling group, qui fait spawn nos EC2 qui contiennent wordpress.
Redondance & optisation des subnets¶
Ici j'ai fais un projet des plus simplistes en utilisant seulement 2 subnets publics et 2 subnets privés. Mais tu te doute bien qu'un projet d'une vrai entreprise en production est bien plus complexe.
A toi de diviser le projet en plusieurs sous modules que tu appelleras individuellement afin de découpler les diverses ressources entre elles.
N'oublis pas d'utiliser les fonctions built-in de terraform (for_each, etc) afin de définir tes subnets et leurs appels de façon plus propre et factorisé.