Retour aux projets
24 novembre 20255 min de lecture

Migration Architecture : De WordPress à une JAMstack conteneurisée

#Performance#Podman#Architecture#Nginx#Eco-conception

Retour d'expérience : pourquoi j'ai remplacé un CMS dynamique par un site pré-généré servi par Nginx, et ce que cette bascule a réellement coûté et rapporté.

Illustration de Migration Architecture : De WordPress à une JAMstack conteneurisée

🛑 Le constat : un CMS pour un site sans contenu dynamique

Mon ancien portfolio tournait sous WordPress. C’est un outil puissant, mais pour un site vitrine dont le contenu change une fois par mois, c’est un moteur de rendu dynamique mobilisé pour servir des pages qui ne varient jamais entre deux visiteurs.

Quatre dettes techniques s’accumulaient :

  1. Une stack disproportionnée : Apache, PHP et MySQL pour produire du HTML identique à chaque requête.
  2. Une surface d’attaque à entretenir : le cœur de WordPress et chaque extension doivent être maintenus à jour. Une extension abandonnée devient une porte d’entrée.
  3. Un contenu difficile à versionner : les articles vivent dans une base de données. Impossible de les relire dans une pull request, impossible de revenir proprement en arrière.
  4. Un TTFB dépendant de la charge : chaque visite déclenche des requêtes SQL et une exécution PHP.

Je voulais une plateforme qui reflète la manière dont je travaille : prévisible, versionnée, avec une surface d’exposition réduite au strict nécessaire.


🛠️ La réponse : tout pré-générer

J’ai opté pour une architecture JAMstack. Le principe : produire l’intégralité du HTML au moment de la compilation, et ne servir ensuite que des fichiers statiques.

1. Le moteur : Astro

Astro a une particularité utile ici : il n’envoie aucun JavaScript par défaut. Les composants sont rendus au build ; le navigateur reçoit du HTML et du CSS. Le peu de JS présent sur ce site (bascule de thème, filtres du journal) est explicitement demandé.

Le contenu vit dans des fichiers Markdown typés avec Zod. Chaque article est un fichier dans Git : je peux le relire dans un diff, le corriger, revenir dessus.

2. L’infrastructure : build multi-étapes, exécution Nginx

L’image de production est construite en deux temps. Node compile le site ; seul le résultat est copié dans une image Nginx.

# --- ETAPE 1 : BUILD ---
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci                 # Reproductible, contrairement a npm install
COPY . .
RUN npm run build          # Genere /app/dist

# --- ETAPE 2 : RUNTIME ---
FROM nginx:1.30-alpine
RUN apk update && apk upgrade --no-cache   # Correctifs de securite Alpine a jour
COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
USER nginx                 # Le serveur ne tourne pas en root
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]

Ce que cette structure apporte, précisément :

  • L’image finale ne contient ni Node, ni npm, ni le code source. Il n’y a plus d’interpréteur côté serveur à exploiter. Cela ne rend pas le site invulnérable — une faille dans Nginx, une injection via mon propre contenu Markdown ou une mauvaise configuration restent possibles — mais la surface d’attaque côté serveur est considérablement réduite.
  • L’image passe de plusieurs centaines de mégaoctets à quelques dizaines.
  • Le processus ne tourne pas en root, et n’écoute pas sur un port privilégié.

3. Podman plutôt que Docker

Le conteneur est décrit dans une unité systemd via Quadlet. Pas de démon central, un démarrage au boot géré par l’init du système, des logs dans le journal, et un redémarrage automatique en cas de crash.

Il ne publie aucun port sur l’hôte : il rejoint le réseau interne de mon reverse proxy, qui l’atteint par son nom d’hôte et assure seul la terminaison TLS.


📊 Ce que la bascule a changé

Indicateur Avant (WordPress) Après (Astro + Nginx)
Rendu des pages Dynamique, à chaque requête Pré-généré au build
Requêtes base de données Plusieurs par page Aucune
Composants à patcher Cœur CMS + extensions Nginx
Versionnement du contenu Base de données Git
Taille de l’image Lourde (PHP + Apache) Alpine, quelques dizaines de Mo

Les gains de temps de chargement sont réels et attendus — servir un fichier statique est structurellement plus rapide que de le générer. Je ne publie pas de chiffre précis ici : un score de performance dépend du réseau, du terminal et du moment de la mesure, et une capture d’écran d’un outil de benchmark ne prouve pas grand-chose. Mesurez sur votre propre connexion.


🔮 L’écosystème autour

Cette architecture ne vit pas seule :

  1. Intégration et déploiement continus : chaque poussée sur main déclenche la construction de l’image, un scan de vulnérabilités, la publication sur un registre, puis le redémarrage du service sur mon serveur. Aucun accès SSH n’est utilisé — c’est l’objet d’un article dédié.
  2. En-têtes de sécurité et politique de sécurité du contenu posés au niveau de Nginx, vérifiés automatiquement après chaque déploiement.

⚖️ Ce que j’ai perdu au passage

Par honnêteté, cette architecture n’est pas gratuite.

Publier demande un build. Corriger une faute de frappe implique un commit, un pipeline, une image. Sur WordPress, c’était trois clics. Pour un site personnel c’est un bon compromis ; pour une rédaction qui publie dix fois par jour, ce serait absurde.

Il n’y a plus d’interface d’administration. Écrire suppose un éditeur de texte et Git. C’est une barrière réelle pour un contributeur non technique.

La complexité s’est déplacée, elle n’a pas disparu. J’ai supprimé PHP et MySQL, et j’ai ajouté un pipeline, un registre d’images, une unité systemd et un reverse proxy. Le résultat est plus solide et plus reproductible, mais il demande de comprendre chacune de ces couches.

C’était le but. Ce site est aussi un terrain d’exercice.

← Revenir aux projets