L'architecture pragmatique de mes projets en production

Lorsqu’on lit certaines discussions entre développeurs ou architectes logiciels, on pourrait croire que la moindre application web nécessite aujourd’hui une infrastructure distribuée, un cluster Kubernetes et plusieurs services cloud spécialisés.

Pourtant, de nombreux services web (y compris ceux qui accueillent plusieurs milliers de visiteurs par jour) peuvent fonctionner parfaitement avec une architecture beaucoup plus simple.

Voici un retour d’expérience sur l’infrastructure que j’utilise pour mes projets en production, dont certains dépassent les 5000 visiteurs quotidiens, et que j’ai également appliqué pendant des années lors d’une compétition de sécurité informatique avec plus de 250 participants en présentiel.

Contexte

Pendant longtemps, j’ai hébergé l’ensemble de mes projets directement chez moi. Mes sites web, mes services, ma forge Git et même mon infrastructure d’intégration continue tournaient sur de petites machines ARM (principalement des Pine64).

Contrairement à ce que j’entends partout, cela fonctionnait très bien.

La principale raison pour laquelle j’ai arrêté n’est pas un problème de puissance de calcul ou de fiabilité, mais simplement mon passage à un mode de vie plus nomade. Une connexion résidentielle stable et permanente devient difficile à garantir lorsque l’on se déplace souvent.

J’ai donc déplacé la partie publique de mes services chez OVH, en essayant de conserver la même philosophie : simplicité et pragmatisme.

Une observation simple

Dans beaucoup de projets web modernes, l’architecture ressemble à quelque chose comme ceci :

utilisateur → CDN → frontend → API → base de données

Chaque requête déclenche du calcul côté serveur, même lorsque le contenu change très rarement.

Dans mon cas, plusieurs de mes sites ont une caractéristique importante : le contenu principal change une fois par jour.

Prenons par exemple le jeu quotidien Yakazu : pour la majorité des visiteurs, la seule chose qui compte est la grille du jour, qui ne change qu’une fois par jour.

Dans ces conditions, générer la page dynamiquement à chaque requête n’a pas beaucoup de sens.

Architecture retenue

La solution est donc très simple : le site est entièrement statique.

Chaque jour, un événement déclenche la CI qui :

  1. génère la nouvelle grille ;
  2. reconstruit le site ;
  3. publie les fichiers statiques.

Ces fichiers sont ensuite servis directement par l’hébergement statique fourni avec le nom de domaine chez OVH.

Oui, l’offre gratuite de 100 MB.

Cela donne donc une architecture très simple :

CI → génération du site → fichiers statiques → OVH

Tout le trafic est absorbé par l’infrastructure de l’hébergeur, ce qui est exactement ce pour quoi elle est conçue.

Frontend

Le site est construit avec SvelteKit.

Ce choix peut surprendre puisque je n’utilise pas du tout les capacités serveur de ce framework. Mais il permet :

  • de générer facilement un site statique (@sveltejs/adapter-static) ;
  • d’avoir un routage propre ;
  • de structurer le code proprement ;
  • de construire des Progressive Web Apps.

Autrement dit : un framework moderne, mais sans dépendre d’un backend permanent.

Et quand il faut un backend ?

Certaines fonctionnalités nécessitent malgré tout un peu de logique serveur.

Par exemple, les championnats organisés régulièrement doivent vérifier les participations et enregistrer les résultats et les chronos.

Dans ce cas, je démarre un petit serveur backend séparé, qui ne reçoit qu’un nombre très limité de requêtes.

L’architecture devient alors :

site statique → CORS → petit backend

Ce backend est volontairement minimal et ne traite que les actions qui ne peuvent pas être réalisées côté client.

Le point important est que la majorité du trafic ne le touche jamais.

Résultat

Avec cette architecture :

  • la quasi-totalité du trafic est servie sous forme de fichiers ;
  • il n’y a quasiment pas de calcul côté serveur ;
  • la surface de panne est très réduite.

Les problèmes que je rencontre sont presque toujours liés au pipeline de génération :

  • une image Docker qui n’est plus à jour ;
  • un déclenchement CI qui échoue ;
  • un oubli dans la génération des données (l’absence de grille de jeu par exemple).

Mais ces problèmes sont faciles à détecter et se corrigent généralement en quelques minutes.

Pourquoi faire simple ?

Avec un peu de recul, je me rends compte que cette approche suit quelques principes simples :

  1. pré-calculer ce qui peut l’être ;
  2. servir des fichiers plutôt que du calcul ;
  3. réserver le backend aux cas réellement nécessaires.

Ces principes permettent souvent d’absorber une charge importante avec des ressources très modestes.

Pendant plus de dix ans, j’ai par exemple organisé un challenge de sécurité informatique qui a fini par accueillir environ 300 participants simultanés, avec des machines très simples.

Le secret n’était pas la puissance du matériel, mais la conception du système. Je vous laisse visionner ma présentation de cette infrastructure, avec les tâtonnements, les erreurs et les contraintes qui l’ont façonné.

Et les comptes utilisateurs, les paiements ?

Une “connexion” à un compte utilisateur n’a pas nécessairement besoin d’une validation systématique par un backend (d’ailleurs la notion de compte utilisateur n’a pas toujours de sens).

Une seule requête d’API peut faire l’authentification (que ce soit la validation d’un paiement ou d’une adresse email, …) puis on stocke cette réponse dans le navigateur pour en faire usage dans l’interface : maintenant … ou même plus tard. Comme ça, inutile de refaire une requête à l’API pour une information que l’on a déjà.

Non seulement cela réduit la charge du serveur, mais cela accroit aussi la vitesse de chargement pour l’utilisateur qui voit ses données sans appel réseau, et donc même hors ligne. Tout le monde est gagnant.

Conclusion

L’infrastructure moderne propose de nombreux outils très puissants. Mais il est parfois utile de se rappeler que beaucoup de problèmes peuvent être résolus de manière beaucoup plus simple.

Avant d’ajouter une nouvelle couche technologique, il vaut souvent la peine de se poser une question très simple :

est-ce que ce contenu doit vraiment être généré à chaque requête ?

Dans bien des cas, la réponse est non.