Rework article thanks to Frederic review
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

This commit is contained in:
nemunaire 2026-03-13 12:21:41 +07:00
commit 1288d4d88f

View file

@ -2,197 +2,224 @@
title: L'architecture pragmatique de mes projets en production
date: !!timestamp '2026-03-05 15:00:00'
tags:
- hosting
- continuous integration
- architecture
- web
- infrastructure
- security
---
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
aujourd'hui une infrastructure distribuée, un environnement 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.
Pourtant, de nombreux services web, y compris ceux qui accueillent
plusieurs milliers de visiteurs par jour, peuvent fonctionner
aussi bien avec une architecture beaucoup plus simple.
Voici un retour d'expérience sur l'infrastructure que j'utilise pour
Voici un retour d'expérience sur l'infrastructure de
mes projets en production, dont certains dépassent les 5000 visiteurs
quotidiens, et que j'ai également appliqué pendant des années lors
quotidiens. Je l'ai également appliquée pendant des années au profit
d'une compétition de sécurité informatique avec plus de 250
participants en présentiel.
participants en temps réel.
<!-- more -->
# Contexte
## 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
J'ai hébergé durant des années l'ensemble de mes projets directement
[à mon domicile]({{< relref "/fr/post/self-hosting/index.md" >}}). 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).
machines ARM : principalement des Pine64, [mais pas que]({{< relref "/en/post/kernel_configs/index.md" >}}).
Contrairement à ce que j'entends partout, cela fonctionnait très bien.
Contrairement à ce que je lis souvent, l'ensemble 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
un [mode de vie plus nomade]({{< relref "/fr/post/china-for-digital-nomads/index.md" >}}). 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.
Ma solution a consisté à déplacer la partie publique de mes services chez OVH,
en conservant la même philosophie : simplicité et pragmatisme.
# Une observation simple
## Une observation simple
Dans beaucoup de projets web modernes, l'architecture ressemble à
quelque chose comme ceci :
Beaucoup de projets web modernes adoptent une architecture semblable à
celle-ci :
```
utilisateur → CDN → frontend → API → base de données
navigateur → 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.
Chaque requête déclenche du calcul côté serveur, alors même que le
contenu principal change très rarement. Cela réclamme de nombreuses
machines, implique parfois des prestataires, avec pour effet que si une brique
tombe en panne, le contenu ne peut plus être servi à l'utilisateur.
Dans mon cas, plusieurs de mes sites ont une caractéristique
importante : **le contenu principal change une fois par jour.**
De nombreux blogs, dont celui-ci, sont servis directement par un serveur web,
sans phase de génération ou mécanisme complexe de cache. La génération des
pages a lieu avant la publication : 1 fois pour toute.
Prenons par exemple le [jeu quotidien
Yakazu](https://yakazu-gratuit.fr/) : pour la majorité des visiteurs,
la seule chose qui compte est **la grille du jour**, qui ne change
qu'une fois par jour.
La très grande majorité des site web qui présentent principalement du contenu
pourraient être simplifié de la même manière : journaux en ligne, vitrines
d'entreprise, boutiques en ligne, ...
J'édite par exemple un [jeu quotidien de Yakazu](https://yakazu-gratuit.fr/).
Pour la majorité des visiteurs, la seule chose qui compte est **la grille du
jour**. Or, ce contenu ne change qu'une fois par jour. Ainsi, générer la page
dynamiquement à chaque requête n'a pas beaucoup de sens.
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.
La solution est donc très simple : rendre tout le site principalement statique.
Chaque jour, un événement déclenche la CI qui :
Chaque jour, un événement déclenche la reconstruction du site puis pousse les
nouveaux fichiers sur l'espace mis à disposition par l'hébergeur.
1. génère la nouvelle grille ;
2. reconstruit le site ;
3. publie les fichiers statiques.
Ces fichiers sont donc servis aux utilisateur directement, avec pour seul
intermédiaire l'hébergement statique inclus avec le nom de domaine chez OVH.
Ces fichiers sont ensuite servis directement par l'hébergement
statique fourni avec le nom de domaine chez OVH.
Oui cette offre gratuite de 100 MB qui vous semble microscopique et inutilisable
peut en réalité servir à héberger une boutique où les utilisateurs sont heureux
de revenir chaque jour.
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.
Tout le trafic est absorbé par l'infrastructure de l'hébergeur, exactement ce
pour quoi elle est conçue.
# Frontend
## Génération des pages
Le site est construit avec SvelteKit.
Le site est construit avec [SvelteKit](https://kit.svelte.dev/).
Ce choix peut surprendre puisque je n'utilise pas du tout les
capacités serveur de ce framework. Mais il permet :
Ce choix peut surprendre puisque je n'utilise pas les capacités serveur de cet
outil. Mais il permet de :
- 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.
- générer facilement un site statique (`@sveltejs/adapter-static`) ;
- structurer le code ;
- avoir un routage propre ;
- construire des Progressive Web Apps.
Autrement dit : un framework moderne, mais sans dépendre d'un backend
permanent.
# Et quand il faut un backend ?
## Quand il faut une partie dynamique ?
Certaines fonctionnalités nécessitent malgré tout un peu de logique
serveur.
côté 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.
Ici, je démarre un petit serveur séparé, qui reçoit un nombre très
limité de requêtes.
L'architecture devient alors :
```
site statique → CORS → petit backend
navigateur → site statique
(CORS) → backend
```
Ce backend est volontairement minimal et ne traite que les actions
qui ne peuvent pas être réalisées côté client.
Ce backend est volontairement minimal et traite seulement les actions
réellement indispensables.
Le point important est que **la majorité du trafic ne le touche
jamais**.
Bénéfice principal : **la majorité du trafic ne le touche jamais**.
# Résultat
## Quid des comptes utilisateurs, des paiements ?
Pourquoi une "connexion" à un compte utilisateur aurait obligatoirement besoin
d'une validation **systématique** par le serveur ?
{{% card color="success" title="Arrêtons de mettre des bâtons dans les roues" %}}
L'étape de création d'un compte utilisateur n'a bien souvent aucune justification :
lorsqu'un joueur veut acheter plus de grilles de Yakazu, il veut jouer, il ne
veut pas imaginer un nouveau mot de passe.
Il n'y a aucune barrière technique qui empêche de donner accès à du contenu contre
un simple paiement.
Si on profite du paiement pour receuillir une donnée personnelle identifiante lors
du paiement (email, numéro de téléphone, compte Apple ou Google pour Apple Pay ou
Google Pay, ...), cette même donnée personnelle pourra servir pour donner à nouveau
accès au contenu, cette fois après authentification (lien magique par exemple).
{{% /card%}}
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 l'utiliser dans
l'interface aussitôt ... ou plus tard ! Inutile d'interroger à nouveau
l'API pour une information déjà connue.
D'une part, nous réduisons la charge du serveur. D'autre part nous accélérons
le chargement pour l'utilisateur qui voit ses données sans appel réseau, même
hors ligne. Tout le monde y gagne !
## 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 puissance de calcul nécessaire côté serveur est ridicule ;
- la surface de panne est très réduite.
Les problèmes que je rencontre sont presque toujours liés au pipeline
Les problèmes que je rencontre sont presque toujours liés au processus
de génération :
- une image Docker qui n'est plus à jour ;
- un déclenchement CI qui échoue ;
- un déclenchement qui échoue ;
- une image Docker obsolète ;
- 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 :
## Pourquoi faire simple ?
Après plusieurs années d'expérience, je constate que cette approche suit
plusieurs règles apparemment 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**.
3. **réserver les calculs aux cas réellement nécessaires**.
Ces principes permettent souvent d'absorber une charge importante
avec des ressources très modestes.
Ces principes permettent d'absorber une charge importante avec des
ressources humaines ou logistiques 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
## Application à un service principalement dynamique
Certains services reposent obligatoirement sur une phase de calculs ou de
génération côté serveur. Pas pour de mauvaises raisons, mais parce que le
contenu présenté dépend de facteurs qui dépassent le seul utilisateur.
J'ai organisé pendant plus de dix ans un challenge de sécurité informatique
avec des machines très simples. Avec près de 300 participants simultanés les
dernières années, la conception d'une architecture qui tient la charge et
résiste aux attaques logicielles probables est un défi qui dépasse de loin ce
que la majorité des sites web pourront observer.
Le secret n'était pas la puissance du matériel, mais la conception du système.
Regardez ma [présentation de cette
infrastructure](https://youtube.com/watch?v=naFxBW5yoUA), avec les
tâtonnements, les erreurs et les contraintes qui l'ont façonné.
contraintes, mes tâtonnements, mes erreurs et les succès qui l'ont façonnée.
# 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).
## Conclusion
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.
L'infrastructure moderne propose des outils puissants.
Mais il est parfois utile de se rappeler que beaucoup de problèmes
peuvent être résolus de manière beaucoup plus simple.
peuvent être résolus de manière simple.
Avant d'ajouter une nouvelle couche technologique, il vaut souvent la
peine de se poser une question très simple :
Avant d'ajouter une nouvelle couche technologique, je vous encourage à vous poser la question :
**est-ce que ce contenu doit vraiment être généré à chaque requête ?**
**Ce contenu doit vraiment être généré à chaque requête ?**
Dans bien des cas, la réponse est non.
Prêtons attention à ne pas être quelquun qui construit une cuisine de restaurant 3 étoiles pour faire des pâtes.