Blog

Monter une architecture pour mettre en place un modèle SaaS

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.

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.

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.com: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.com: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 Node.js 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 :
1. 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
2. Appel au script principal, vérification des arguments qui ont été passés
3. Quelques traitements (comme la lecture du port disponible) et appel à un premier script pour déployer un AquilaCMS et un MongoDB
4. Le script de déploiement a terminé son exécution, il renvoie la main au script principal
5. Appel au script qui va créer la configuration Nginx
6. Le script de déploiement a terminé son exécution, il renvoie la main au script principal
7. Le script principal renvoie la main à l’API
8. L’API retourne tout ce qui a pu être écrit sur la sortie standard des différents scripts
9. La boutique peut faire divers autres appels à cette même API pour récupérer des informations
10. L’API renvoie ces informations


schema architecture saas

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).

RESTEZ CONNECTÉS

Soyez informés de toutes nos nouveautés et mises à jour

AquilaCMS informations