aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Outils & DevOps

Zero Downtime Deploy : guide symlink et health check

Un zero downtime deploy, c'est mettre en ligne une nouvelle version sans qu'un seul utilisateur ne voie jamais une page d'erreur. Dans l'approche classique, vous vous connectez au serveur, copiez les fichiers par-dessus le répertoire en production, installez les dépendances puis redémarrez le service ; mais pendant ces quelques secondes, le site sert soit des fichiers à moitié écrits, soit tombe complètement. Dans ce guide, je vous montre pas à pas comment mettre en place une bascule sans interruption pour des projets petits et moyens, en n'utilisant rien d'autre qu'un symlink (lien symbolique) et un health check, sans recourir à un lourd outil d'orchestration.

Le problème : pourquoi « copier et redémarrer » provoque une coupure

Écrire les fichiers directement par-dessus le répertoire en production comporte deux dangers. D'abord, si la copie est interrompue, le serveur web finit par servir un mélange d'ancien et de nouveau code — ce qui, en PHP, signifie des erreurs fatales, des incohérences d'assets ou des templates à moitié rendus. Ensuite, des étapes comme composer install ou l'exécution des migrations prennent plusieurs secondes, et pendant toute cette fenêtre l'application reste dans un état incohérent.

Le cœur de la solution est simple : vous installez la nouvelle version dans un dossier séparé, et une fois que tout est prêt, vous faites basculer le lien qui pointe vers le répertoire en production vers le nouveau dossier en une seule opération. Sous Linux, remplacer un symlink est une opération atomique : le serveur voit soit l'ancien dossier en entier, soit le nouveau en entier, jamais un mélange des deux.

Structure des répertoires : releases, shared et current

Il existe une structure éprouvée que des outils comme Capistrano et Deployer utilisent depuis des années. Mettez en place cette disposition sur le serveur :

/var/www/app/
├── releases/
│   ├── 2026-06-28-101500/
│   └── 2026-06-28-094200/
├── shared/
│   ├── .env
│   └── storage/
└── current -> releases/2026-06-28-101500/

La logique est la suivante : chaque déploiement arrive comme un nouveau dossier horodaté sous releases/. Les éléments qui ne changent pas entre versions et qu'il faut préserver (le fichier d'environnement, les images uploadées, les logs) résident sous shared/ et sont reliés à chaque release par des symlinks. Le document root du serveur web pointe toujours vers le lien current. La bascule consiste simplement à déplacer ce lien current.

Le flux de déploiement, étape par étape

La séquence ci-dessous est le squelette d'une bascule propre. N'exécutez chaque étape que si la précédente a réussi :

  • Créer le nouveau dossier de release et y extraire le code (git clone, tar pipe ou un artefact de CI).
  • Relier les ressources partagées : créer un symlink des éléments comme .env et storage depuis shared/ vers la nouvelle release.
  • Installer les dépendances : composer install --no-dev -o, suivi d'un build frontend si nécessaire.
  • Migrations et cache : si le schéma a changé, lancez php artisan migrate --force, puis php artisan optimize.
  • Lancer le health check : vérifier que la nouvelle release démarre réellement.
  • Basculer le symlink : seulement si le contrôle passe, faites pointer current vers le nouveau dossier.

Le point critique : gardez le symlink pour la fin. Comme les opérations lentes — build, installation des dépendances, migrations — se déroulent toutes dans un dossier qui n'est pas encore en production, les utilisateurs n'en sont jamais affectés.

Le cœur de la bascule : remplacer le symlink

La bonne façon de mettre à jour un symlink sur place est ln -sfn. Le drapeau -f écrase le lien existant, tandis que -n empêche le lien d'être créé à l'intérieur de la cible lorsque celle-ci est un répertoire :

ln -sfn /var/www/app/releases/2026-06-28-101500 /var/www/app/current

Cette unique commande est le moment où toute la bascule se produit. Sur la plupart des systèmes, ln crée d'abord la cible sous un nom temporaire puis la met en place avec un appel rename(), et rename() est atomique sur le même système de fichiers. Après la bascule, des processus comme PHP-FPM peuvent encore avoir l'ancien chemin en cache dans OPcache ; rechargez donc PHP-FPM en douceur ensuite (par exemple systemctl reload php8.3-fpm) ou réinitialisez OPcache.

Health check : ne jamais promouvoir une version cassée

La dernière barrière devant la bascule est le health check. L'objectif est de confirmer — avant de basculer le symlink — que la nouvelle release répond réellement. Ajoutez un endpoint simple à votre application (par exemple /health) et interrogez-le dans le script de déploiement :

URL="http://127.0.0.1/health"
for i in $(seq 1 10); do
  code=$(curl -s -o /dev/null -w "%{http_code}" "$URL")
  if [ "$code" = "200" ]; then
    echo "Health OK"; exit 0
  fi
  sleep 2
done
echo "Health check echoue, deploiement annule"; exit 1

Si le script obtient autre chose qu'un 200, il s'arrête avec exit 1 et le lien current continue de pointer vers l'ancienne version, qui fonctionne. L'endpoint de santé ne doit pas être superficiel : s'il vérifie des dépendances — une connexion à la base de données, l'accès à un cache critique — au lieu de dire simplement « PHP tourne », il élimine le risque de promouvoir une version à moitié cassée.

Rollback et nettoyage des anciennes releases

Le meilleur atout de cette disposition, c'est que le rollback est aussi rapide que le déploiement. Si vous repérez un problème, il suffit de faire repointer current vers la release précédente :

ln -sfn /var/www/app/releases/2026-06-28-094200 /var/www/app/current
systemctl reload php8.3-fpm

Pour que les anciennes releases ne s'accumulent pas sur le disque, ajoutez une petite étape de nettoyage qui garde les 3 à 5 plus récentes et supprime le reste. Ainsi vous conservez quelques sauvegardes pour le rollback sans jamais remplir le disque.

Une note sur les migrations : des changements rétrocompatibles

Le symlink est peut-être atomique, mais le schéma de base de données ne l'est pas. L'ancien et le nouveau code partagent brièvement la même table pendant la bascule. Découpez donc les changements de schéma destructeurs (supprimer ou renommer une colonne) en deux phases : d'abord ajoutez la nouvelle structure et déployez de manière à ce que les deux versions continuent de fonctionner ; puis, une fois l'ancien code totalement retiré, nettoyez les anciennes colonnes lors d'un déploiement ultérieur. Cette approche « expand and contract » est l'autre moitié cachée d'une vraie absence de coupure.

Questions fréquentes

Ai-je besoin de Docker ou Kubernetes pour cela ?

Non. Les déploiements basés sur symlink fonctionnent parfaitement sur un seul VPS ou un hébergement mutualisé, sans aucun conteneur. Docker et Kubernetes apportent de la valeur lorsque vous montez en charge et devez gérer plusieurs serveurs ou réplicas, mais pour des projets petits à moyens, l'approche symlink est bien plus simple et tout à fait suffisante.

Ai-je vraiment besoin d'un endpoint de health check dédié ?

Interroger la page d'accueil fonctionne aussi, mais un endpoint /health dédié est préférable. Il peut vérifier délibérément les dépendances (BDD, cache), rester léger et être surveillé sans créer de bruit dans vos logs.

Pourquoi recharger PHP-FPM après avoir basculé le symlink ?

OPcache peut mettre en cache les fichiers par leurs vrais chemins résolus. Même après la bascule du lien, les processus peuvent continuer à servir l'ancien code. Un reload (et non un restart) rafraîchit ce cache, sans provoquer de coupure.

Vous voulez un pipeline de déploiement sans coupure ? Que ce soit Laravel ou une autre stack, je peux vous aider à mettre en place sur votre serveur existant un flux de déploiement basé sur symlink, avec health check et rollback en une seule commande. Contactez-moi et parlons de votre projet.

Bu kategorideki tüm yazılar →

Devamı için