Déployer des versions atomiques avec rsync

Une commande sur ton portable construit le site, envoie seulement ce qui a changé dans un nouveau répertoire de version, bascule le site en ligne dessus en une seule étape atomique, garde les dernières versions et vérifie le résultat. Le retour arrière tient aussi en une commande.

intermédiaire~30 min de manipulation
#deploy#rsync#ssh#symlink#rollback

Pas encore validée de bout en bout — sois le premier.Signaler un problème

Brouillon — pas encore exécuté de bout en bout. Cette page est écrite mais son auteur ne l'a pas encore déroulée sur une vraie machine. Des commandes peuvent être fausses : lis avant de lancer, et dis-nous ce qui casse.

En un coup d'œilConstruire, envoyer, basculer un lien
Ton VPS
npm run buildrsync over SSHatomic swapservesHTTPS
Ton portable./scripts/deploy.sh
out/npm run build
Nouvelle versionreleases/<UTC timestamp>
currentsymlink
Caddy/current
Visiteurshttps://

Chaque déploiement arrive dans son propre répertoire sous releases/. Le site en ligne est ce vers quoi pointe current, et déplacer ce lien est un seul renommage atomique : les visiteurs ne voient jamais un site à moitié envoyé.

Le site est servi, mais ce n'est qu'une page d'attente. Cette page te donne une commande, ./scripts/deploy.sh, qui construit le site sur ton portable, envoie seulement ce qui a changé dans un nouveau répertoire de version sur le serveur, bascule le site en ligne dessus en une étape atomique, supprime les anciennes versions au-delà des dernières, et vérifie que https:// répond. Une seconde commande revient en arrière. Les envois passent par un compte dédié, , avec sa propre clé, sans sudo, et dont la racine web est la seule possession.

Avant de commencer

Il te faut les pages 1 à 5 terminées : le dossier du projet sur ton portable (page 1), ssh vps qui te connecte en , et Caddy qui sert la page d'attente sur https://.

Vérification
$curl -s -o /dev/null -w '%{http_code}' https:///
Retour attendu
200

Le rsync fourni avec le Mac n'est pas celui qu'il te faut. Installe rsync 3 avec Homebrew (installe d'abord Homebrew depuis brew.sh si la commande brew manque) :

Mac
$brew install rsync

Puis ferme ce terminal et ouvres-en un nouveau (Cmd+N dans Terminal). Le shell déjà ouvert peut continuer à lancer /usr/bin/rsync : il retient où il a trouvé une commande, et si Homebrew vient d'y être installé, son PATH ne contient pas encore /opt/homebrew/bin. Un nouveau shell relit ton PATH et trouve d'abord le rsync de Homebrew. Dans le nouveau terminal :

Mac
$rsync --version | head -1
Vérification
$rsync --version | head -1 | grep -o 'version 3'
Retour attendu
version 3

Créer l'utilisateur de déploiement

Ton compte admin a sudo ; le compte qui envoie le site ne doit pas l'avoir, et il reçoit sa propre clé. Sur le portable, crée la clé de déploiement si elle n'existe pas encore et copie sa moitié publique sur le serveur :

Mac
$[ -f ~/.ssh/id_ed25519_deploy ] || ssh-keygen -t ed25519 -N "" -C "deploy@laptop" -f ~/.ssh/id_ed25519_deploy
$scp ~/.ssh/id_ed25519_deploy.pub vps:deploy.pub

Puis connecte-toi au serveur comme d'habitude :

Mac
$ssh vps

Sur le serveur, installe rsync (le côté qui reçoit), crée l'utilisateur, laisse-le passer SSH, donne-lui la racine web, et installe la clé de déploiement pour lui, précédée de restrict. La dernière ligne, exit, te ramène sur le portable :

Serveur·
$sudo apt install -y rsync
$sudo adduser --disabled-password --gecos ""
$sudo usermod -aG sshusers
$sudo chown -R :
$sudo install -d -m 700 -o -g /home//.ssh
$sed 's/^/restrict /' deploy.pub | sudo tee /home//.ssh/authorized_keys
$sudo chown : /home//.ssh/authorized_keys
$sudo chmod 600 /home//.ssh/authorized_keys
$rm deploy.pub
$exit

Ajouter l'alias sur le portable

Ajoute un second hôte dans ~/.ssh/config sur ton portable, à côté de vps :

Mac
$cat >> ~/.ssh/config <<'EOF'
$
$Host vps-deploy
$ HostName
$ Port
$ User
$ IdentityFile ~/.ssh/id_ed25519_deploy
$ IdentitiesOnly yes
$EOF
Vérification
$ssh vps-deploy 'ls '
Retour attendu
current
releases
Si tu obtiens Permission denied (publickey)

Par ordre de probabilité : l'utilisateur n'est pas dans le groupe sshusers, les droits sur /home//.ssh sont trop ouverts, la ligne de clé a perdu son préfixe ssh-ed25519, ou le chemin IdentityFile ne correspond pas à la clé que tu as créée. Sur le serveur, sudo journalctl -u ssh -n 20 donne la raison.

Écrire le script de déploiement

Crée le dossier scripts dans le projet :

Mac
$cd && mkdir -p scripts

Puis écris scripts/deploy.sh : copie la commande et colle-la dans le même terminal. Le chemin est relatif, donc le fichier atterrit dans le dossier du projet où tu viens d'entrer.

Macécrit un fichierscripts/deploy.sh
Commande sensible — suppression récursive ou forcée (rm). Vérifie avant d'exécuter.
#!/usr/bin/env bash
# Build the site and publish it as a new release. Usage: ./scripts/deploy.sh
set -euo pipefail
cd "$(dirname "$0")/.."
HOST=vps-deploy
ROOT=
KEEP=
[ "$KEEP" -ge 1 ] || { echo "KEEP_RELEASES must be at least 1" >&2; exit 1; }
npm ci
npm run build
test -f out/index.html
REL=$(date -u +%Y%m%dT%H%M%SZ)
echo "Release $REL"
rsync -rlpz --checksum --delete --chmod=D755,F644 \
--link-dest="$ROOT/current/" \
out/ "$HOST:$ROOT/releases/$REL/"
ssh "$HOST" "cd $ROOT && ln -sfn releases/$REL current.tmp && mv -T current.tmp current"
ssh "$HOST" "cd $ROOT/releases && ls -1d 20* | sort | head -n -$KEEP | xargs -r rm -rf --"
curl -fsS -o /dev/null -w '%{http_code}\n' https:///

Rends-le exécutable, pour qu'il se lance en ./scripts/deploy.sh :

Mac
$chmod +x scripts/deploy.sh

Déployer pour la première fois

Lance le script depuis le dossier du projet. La dernière ligne affichée doit être 200 ; ouvre https:// et ton site remplace la page d'attente. Le premier envoi transfère tout le site (environ 220 Mo avec les médias de public/) ; les suivants seulement ce qui a changé.

Mac
$cd
$./scripts/deploy.sh
Vérification
$ssh vps-deploy 'readlink /current | cut -c1-11'
Retour attendu
releases/20
Mac
Commande sensible — suppression récursive ou forcée (rm). Vérifie avant d'exécuter.
$ssh vps-deploy 'rm -rf /releases/placeholder'

Revenir en arrière

Revenir en arrière, c'est refaire la bascule vers une version plus ancienne. Ce script pointe current vers la version juste avant celle en ligne, ou vers celle que tu nommes. Copie la commande et colle-la dans le terminal, dans le dossier du projet :

Macécrit un fichierscripts/rollback.sh
#!/usr/bin/env bash
# Usage: ./scripts/rollback.sh [release-name]
set -euo pipefail
HOST=vps-deploy
ROOT=
CUR=$(ssh "$HOST" "readlink $ROOT/current")
CUR=$(basename "$CUR")
LIST=$(ssh "$HOST" "cd $ROOT/releases && ls -1d 20* | sort")
if [ $# -gt 0 ]; then
TARGET=$1
else
TARGET=$(printf '%s\n' "$LIST" | awk -v c="$CUR" '$0 == c { print p; exit } { p = $0 }')
fi
[ -n "$TARGET" ] || { echo "No release older than $CUR." >&2; exit 1; }
printf '%s\n' "$LIST" | grep -qx "$TARGET" || { echo "Unknown release: $TARGET" >&2; exit 1; }
ssh "$HOST" "cd $ROOT && ln -sfn releases/$TARGET current.tmp && mv -T current.tmp current"
echo "current: $CUR -> $TARGET"
curl -fsS -o /dev/null -w '%{http_code}\n' https:///

Rends-le exécutable, déploie une fois de plus pour que le serveur ait deux versions, liste-les, et reviens à la première :

Mac
$chmod +x scripts/rollback.sh
$./scripts/deploy.sh
$ssh vps-deploy 'ls -1 /releases'
$./scripts/rollback.sh

Terminé

Un déploiement tient maintenant en une commande, ./scripts/deploy.sh, et un retour arrière en une autre, ./scripts/rollback.sh. Chaque version est un répertoire nommé par l'heure UTC de son déploiement, celle en ligne est celle vers laquelle pointe current, et le passage de l'une à l'autre est un seul renommage. Le serveur garde les dernières.

La page suivante, Sauvegardes, surveillance et la routine mensuelle, fait en sorte que tu sois prévenu quand le site tombe, et que le serveur puisse être reconstruit si le VPS est perdu.

Tout a fonctionné ?

Si tu as suivi cette page jusqu'au bout sur une vraie machine, dis-le. Ta validation est datée et enregistre ta stack : le prochain lecteur sur le même chemin sait que ça marche toujours.

Cette copie est en lecture seule. Pour dire que ça marche, ou que ça ne marche pas, ouvre une issue

Seuls tes choix de stack sont enregistrés, jamais tes valeurs. Le pseudo reste sur ce navigateur.