Préambule : NextSourcia est une société d’hébergement et de développement de solutions Web dont la solution phare est AquilaCMS, un CMS e-commerce open source développé sur une ME*N stack.
Un des projets que nous avons dû mettre en place ces derniers mois est la mise à disposition d’une offre d’hébergement sur notre boutique, permettant l’installation de notre solution AquilaCMS en un clique.
Une version anglaise de cet article est disponible sur le site d’AquilaCMS et sur Medium.
Pourquoi utiliser le modèle SaaS
Pour réaliser ce projet nous nous sommes appuyés sur le modèle SaaS, qui veut dire Software as a Service, cela désigne un modèle où l’installation d’un logiciel ne se fait pas sur la machine de l’utilisateur mais sur une machine distante. Dans notre cas, cela se traduit par l’installation d’un AquilaCMS sur nos serveurs, à la demande de l’utilisateur, ce qui correspond parfaitement à l’offre d’hébergement que nous voulions mettre en place.
Si vous voulez adapter ce projet, il vous suffit de remplacer AquilaCMS et les différentes technologies utilisées telles que MongoDB par celles qui vous intéressent.
Ce projet a été pensé pour un faible traffic et une latence minimale, pour un projet avec un traffic plus élevé il est recommandé d’utiliser Traefik à la place d’Nginx et Kubernetes avec Docker (notamment pour des besoins de montée en charge). Ce même projet avec Traefik et Kubernetes fera peut-être l’objet d’un article.
La mise en place avec Docker
Nous avons opté pour une architecture basée sur des images Docker et une installation sur nos propres serveurs, sans appel à un service extérieur. Cela a deux avantages : réduire un maximum la latence réseau en ayant la main sur tous les serveurs utilisés et avoir le contrôle sur l’entièreté de l’architecture pour l’optimiser un maximum selon nos besoins. Dans ce projet nous utilisons Docker de façon “vanilla”, sans extensions et sans orchestration comme avec Kubernetes. Cette façon de faire peut également être pratique dans des cas où l’utilisation d’un orchestrateur n’est pas possible.
Déployer dynamiquement une application et son MongoDB
Le premier besoin de cette architecture est de pouvoir déployer notre solution et une base MongoDB à chaque demande d’hébergement. Pour cela nous avons développé des scripts Bash qui lancent des conteneurs Docker à partir d’un fichier docker-compose.yml
et de variables d’environnements.
version: '3.4'
services:
mongo:
container_name: mongo-${ID}
image: mongo
volumes:
- "db-name-volume:/data/db"
networks:
- aquila
ports:
- "${PORT_DB}:27017"
aquila:
depends_on:
- mongo
container_name: aquila-${ID}
build: .
environment:
- NODE_ENV=production
- AQUILA_ENV=aquila-${ID}
ports:
- "${PORT_AQL}:3010"
networks:
- aquila
volumes:
db-name-volume:
name: "mongo-${ID}"
networks:
aquila:
external:
name: aquila-${ID}
Les variables d’environnements sont placées dans un fichier .env
dans le même dossier que le docker-compose.yml
. Ici nous avons la variable ID
qui correspond au nom de la boutique renseignée par l’utilisateur, PORT_AQL
qui est le port du conteneur AquilaCMS et PORT_DB
qui est le port du conteneur MongoDB. Nous pouvons noter la déclaration d’un volume à l’extérieur du service mongo
car c’est la seule façon de pouvoir utiliser une variable dans le nom d’un volume. Enfin, nous créons un réseau extérieur qui est propre à chaque paire de conteneurs AquilaCMS et MongoDB.
Gestion des ports du serveur d’hébergement
Pour connaître les ports sur lesquels les conteneurs vont être lancés, nous avons mis en place la gestion d’un fichier CSV dans nos scripts : à chaque demande d’hébergement, le fichier est parcouru et le premier port disponible est récupéré, puis une ligne est ajoutée dans le CSV pour le conteneur AquilaCMS et une autre pour le conteneur MongoDB.
Par exemple, nous avons dans notre fichier :
8001,test1.aquila-cms.cloud,aquila
8002,test1.aquila-cms.cloud,mongo
8003,test2.aquila-cms.cloud,aquila
8004,test2.aquila-cms.cloud,mongo
8007,test4.aquila-cms.cloud,aquila
8008,test4.aquila-cms.cloud,mongo
Le port 8005 sera récupéré pour lancer le conteneur AquilaCMS et le port 8006 pour le conteneur MongoDB.
Gestion des redirections
Une problématique majeure du modèle SaaS est la redirection d’une URL accessible à l’utilisateur vers l’adresse de son site sur nos serveurs. Nous voulons par exemple que test1.aquila-cms.cloud
renvoie vers serveurinterne:8005
. Premièrement, nous avons mis en place un conteneur Nginx avec un volume partagé avec la machine host dans lequel nous allons créer un fichier de configuration pour chaque nouvel AquilaCMS.
Deuxièmement, nous avons pris un nom de domaine dont la totalité du sous-domaine pointe vers ce conteneur Nginx, cela veut dire que *.aquila-cms.cloud
pointe vers l’adresse du conteneur Nginx (par exemple serveurinterne:8000
).
Par exemple, si l’hébergement test1.aquila-cms.cloud
est demandé, on crée un fichier de configuration test1.aquila-cms.cloud.conf
dans le volume du conteneur Nginx et on envoie une commande à ce conteneur pour redémarrer le processus Nginx (/usr/sbin/nginx -s reload
).
Communication entre la boutique et les scripts
Pour communiquer entre la boutique et les scripts du serveur d’hébergement, nous passons par une API REST, cela permet d’appeler les scripts même si la boutique et les hébergements ne sont pas sur le même serveur, le tout de façon sécurisée. Pour cette API, nous utilisons un autre de nos projets qui est sorti en open source : Aquila Probe, mais n’importe quelle API qui fait l’interface entre une requête HTTP et l’exécution d’un script (via le module NodeJS child_process
par exemple) est suffisante.
L’architecture finale
Voici notre architecture de ce projet, sobrement intitulé Aquila Saas. Les différentes étapes de la création d’un hébergement :
- Un bouton sur notre boutique shop.aquila-cms.com fait appel à une route de notre API en passant comme argument le nom du site du client
- Appel au script principal, vérification des arguments qui ont été passés
- Quelques traitements (comme la lecture du port disponible) et appel à un premier script pour déployer un AquilaCMS et un MongoDB
- Le script de déploiement a terminé son exécution, il renvoie la main au script principal
- Appel au script qui va créer la configuration Nginx
- Le script de déploiement a terminé son exécution, il renvoie la main au script principal
- Le script principal renvoie la main à l’API
- L’API retourne tout ce qui a pu être écrit sur la sortie standard des différents scripts
- La boutique peut faire divers autres appels à cette même API pour récupérer des informations
- L’API renvoie ces informations
Supprimer un hébergement
Pour supprimer un hébergement il suffit de revenir sur chacun des points énoncés précédemment : la création des conteneurs AquilaCMS et MongoDB, la création du fichier de configuration Nginx et l’insertion de l’information du port dans le fichier CSV. Dans un script de suppression, il faut alors stopper et supprimer les conteneurs qui correspondent au bon utilisateur (docker stop et docker rm) et supprimer le réseau sur lequel étaient les deux conteneurs (si aucun autre projet n’est sur le même serveur, un docker system prune — all peut être adapté pour être sûr de supprimer toute trace de cet hébergement). Il suffit ensuite de supprimer le fichier de configuration Nginx et de supprimer les lignes de l’utilisateur du fichier CSV (peut se faire avec la commande sed par exemple).
AquilaCMS est une solution e-commerce open source et “all in one”, auto-hébergée, basée sur Node.js. Le projet présenté ici intervient dans un contexte de proposition d’une offre d’hébergement de cette solution pour tous ceux souhaitant la tester. Si vous voulez adapter ce projet, il vous suffit de remplacer AquilaCMS et les différentes technologies utilisées telles que MongoDB par celles qui vous intéressent.