Introduction
Ce projet est né dans le cadre de mon mastère MAALSI au CESI. Le sujet imposait le développement d'une application de vente d'objets d'occasion, mais l'enjeu principal était ailleurs : démontrer la maîtrise d'une architecture microservices déployée sur une infrastructure Kubernetes complète.
J'ai choisi d'aller très au-delà des exigences du sujet en montant une infrastructure de production réaliste sur mon Mac mini personnel, avec deux clusters Kubernetes (recette et production), un reverse proxy, une VM de backup, et une chaîne CI/CD complète. Le jury a salué ce travail par ses félicitations et l'une des meilleures notes de la promotion.
Architecture applicative
L'application repose sur quatre microservices Laravel, chacun responsable d'un domaine fonctionnel précis :
- UserService : gestion des utilisateurs et de leurs données
- AuthService : authentification centralisée avec génération de tokens JWT
- MessageService : messagerie entre utilisateurs
- WebSocketService : messagerie instantanée en temps réel via Laravel Echo
Laravel n'étant pas conçu pour fonctionner en microservices, j'ai dû adapter le framework en profondeur. J'ai créé des dépôts "starter packs" servant de templates réutilisables pour chaque microservice, avec notamment un middleware custom ValidateJWT contournant le système natif auth:api de Laravel — celui-ci exige un Model User local, or le User n'existe que dans le UserService.
Les communications entre services passent en HTTP/REST via des ClusterIP Services Kubernetes, sans transiter par le Nginx Ingress Controller. Ce choix délibéré évite de faire de l'Ingress un point de défaillance unique et limite la charge réseau sur ce composant.
Authentification décentralisée
L'authentification repose sur un système de JWT asymétrique (RS256) :
- Le client envoie ses identifiants à l'AuthService
- L'AuthService interroge le UserService pour valider les credentials
- Un JWT est signé avec la clé privée RS256 (détenue uniquement par l'AuthService)
- Le token est renvoyé dans un secure cookie
- Chaque microservice vérifie les requêtes entrantes avec la clé publique
- Une vérification complémentaire dans Redis s'assure que le token n'a pas été révoqué (blacklist centralisée)
J'avais initialement prévu un chiffrement edDSA, mais une incompatibilité avec Laravel m'a contraint à basculer sur RS256.
Infrastructure
L'ensemble de l'infrastructure tourne sur des VMs Multipass hébergées sur un Mac mini, pour un total d'environ dix machines virtuelles :
- 1 VM reverse proxy : Nginx, fail2ban, gestion des certificats TLS
- 1 cluster Kubernetes de recette : control plane + workers
- 1 cluster Kubernetes de production : control plane + workers
- 1 VM de backup : réception des snapshots ETCD et des backups des autres VMs
Chaque VM dispose de sa propre adresse IP sur le réseau. La base de données MySQL tourne dans un pod du cluster avec un schéma dédié par microservice, garantissant l'isolation des données. Redis est également déployé dans le cluster pour la gestion de la blacklist JWT.
Infrastructure as Code
J'ai développé des playbooks Ansible permettant de gérer l'infrastructure de manière automatisée :
- Création de cluster : un seul playbook prenant en paramètres le nom du cluster, le nombre de workers et les ressources (RAM, CPU) crée automatiquement toutes les VMs Multipass, installe Kubernetes et configure le cluster complet
- Scaling up : ajout d'un worker au cluster existant (création de la VM, configuration, join automatique)
- Scaling down : suppression propre d'un worker (drain des pods, retrait du cluster, suppression de la VM)
Le scaling automatique des pods est quant à lui géré par le HPA (Horizontal Pod Autoscaler) natif de Kubernetes, configuré sur la métrique RAM.
CI/CD
La chaîne CI/CD repose sur GitHub Actions et GitHub Container Registry :
- CI : déclenchée sur chaque pull request vers les branches de recette et de pré-production (tests, vérifications)
- CD : sur merge, build de l'image Docker, tag versionné, push sur le GHCR, puis appel d'un playbook Ansible qui se connecte au control plane du cluster cible pour déployer la nouvelle image
Les tags versionnés sur le registry permettent un rollback rapide en cas de problème.
Observabilité et tests de charge
Un dashboard Grafana sur mesure permet de suivre en temps réel l'état de l'infrastructure : nombre de nodes, charge CPU et RAM par node, état du MySQL, et surtout les métriques du MessageService (charge par pod, nombre de pods actifs).
Les tests de montée en charge avec K6, ciblant le MessageService et son WebSocket, ont démontré un scaling automatique jusqu'à 10 pods et 5000 utilisateurs simultanés avant le crash du cluster — limité uniquement par les ressources matérielles du Mac mini. Après diminution de la charge, les services se sont rétablis automatiquement.
Une analyse DAST avec ZAP a également été réalisée manuellement pour évaluer la sécurité de l'infrastructure.
Difficultés rencontrées
Ce projet a été de loin le plus complexe de mon parcours. Parmi les obstacles majeurs :
- L'adaptation de Laravel pour le microservice, en particulier le contournement du système d'authentification natif
- La découverte de l'incompatibilité edDSA avec Laravel, nécessitant une migration vers RS256
- Un problème de StorageClass absent qui bloquait les PersistentVolumeClaims en pending
- Une corruption de clusters suite à un plantage de Multipass ayant rendu le kernel de certaines VMs irrécupérable
- Des blocages des GitHub Runners par le Keychain de macOS