11 KiB
RUNBOOK — Déploiement Blue/Green (NAS DS220+)
Objectif : déployer une release sans casser, avec rollback immédiat.
0) Portée
Ce runbook décrit le déploiement de l’édition web Archicratie sur NAS (Synology), en mode blue/green :
web_blue: upstream staging →127.0.0.1:8081web_green: upstream live →127.0.0.1:8082- Edge Traefik publie :
staging.archicratie.trans-hands.synology.me→ 8081archicratie.trans-hands.synology.me→ 8082
1) Pré-requis
- Accès shell NAS (user
archicratia) +sudo - Docker Compose Synology nécessite souvent :
sudo env DOCKER_API_VERSION=1.43 docker compose ...
- Les fichiers edge Traefik sont dans :
/volume2/docker/edge/config/dynamic/
2) Répertoires canon (NAS)
On considère ces chemins (adapter si besoin, mais rester cohérent) :
- Base :
/volume2/docker/archicratie-web - Releases :
/volume2/docker/archicratie-web/releases/YYYYMMDD-HHMMSS/app - Symlink actif :
/volume2/docker/archicratie-web/current→ pointe vers le.../appactif
3) Garde-fous (AVANT toute action)
3.1 Snapshot de l’état actuel
en bash :
cd /volume2/docker/archicratie-web ls -la current || true readlink current || true
3.2 Vérifier l’état live/staging upstream direct
curl -sSI http://127.0.0.1:8081/ | head -n 12 curl -sSI http://127.0.0.1:8082/ | head -n 12
3.3 Vérifier l’état edge (host routing)
curl -sSI -H 'Host: staging.archicratie.trans-hands.synology.me' http://127.0.0.1:18080/
| grep -iE 'HTTP/|location:|x-archi-router' | head -n 30
curl -sSI -H 'Host: archicratie.trans-hands.synology.me' http://127.0.0.1:18080/
| grep -iE 'HTTP/|location:|x-archi-router' | head -n 30
Si tu n’es pas authentifié, tu verras un 302 vers auth... : c’est normal.
4) Procédure de déploiement (release pack → nouvelle release)
4.1 Déposer le pack
Hypothèse : tu as un .tgz “release pack” (issu de release-pack.sh) dans incoming/ :
cd /volume2/docker/archicratie-web ls -la incoming | tail -n 20
4.2 Créer un répertoire release
TS="$(date +%Y%m%d-%H%M%S)" REL="/volume2/docker/archicratie-web/releases/$TS" APP="$REL/app" sudo mkdir -p "$APP"
4.3 Extraire le pack
PKG="/volume2/docker/archicratie-web/incoming/archicratie-web.tar.gz" # adapter au nom réel sudo tar -xzf "$PKG" -C "$APP"
4.4 Sanity check (fichiers attendus)
sudo test -f "$APP/Dockerfile" && echo "OK Dockerfile" sudo test -f "$APP/docker-compose.yml" && echo "OK compose" sudo test -f "$APP/astro.config.mjs" && echo "OK astro config" sudo test -f "$APP/src/layouts/EditionLayout.astro" && echo "OK layout" sudo test -f "$APP/src/pages/archicrat-ia/index.astro" && echo "OK archicrat-ia index" sudo test -f "$APP/docs/diagrams/archicratie-web-edition-global-verbatim-v2.svg" && echo "OK diagrams"
4.5 Permissions (crucial sur Synology)
But : archicratia:users doit pouvoir traverser le parent + lire le contenu.
sudo chown -R archicratia:users "$REL" sudo chmod -R u+rwX,g+rX,o-rwx "$REL" sudo chmod 750 "$REL" "$APP"
Vérifier :
ls -ld "$REL" "$APP" ls -la "$APP" | head
5) Activation : basculer current vers la nouvelle release
5.1 Backup du current existant
cd /volume2/docker/archicratie-web TS2="$(date +%F-%H%M%S)"
on backup "current" (symlink ou dossier)
if [ -e current ] || [ -L current ]; then sudo mv -f current "current.BAK.$TS2" echo "✅ backup: current.BAK.$TS2" fi
5.2 Recréer current (symlink propre)
sudo ln -s "$APP" current
ls -la current readlink current sudo test -f current/docker-compose.yml && echo "✅ OK: current/docker-compose.yml"
Si cd current échoue, c’est que current n’est pas un symlink correct OU que le parent n’est pas traversable (permissions).
6) Build & run : (re)construire web_blue/web_green
6.1 Vérifier la config compose
cd /volume2/docker/archicratie-web/current
sudo env DOCKER_API_VERSION=1.43 docker compose -f docker-compose.yml config
| grep -nE 'services:|web_blue:|web_green:|context:|dockerfile:|PUBLIC_SITE|REQUIRE_PUBLIC_SITE'
| sed -n '1,220p'
6.2 Build propre (recommandé si changement de code/config)
sudo env DOCKER_API_VERSION=1.43 docker compose build --no-cache web_blue web_green
6.3 Up (force recreate)
sudo env DOCKER_API_VERSION=1.43 docker compose up -d --force-recreate web_blue web_green
6.4 Vérifier upstream direct (8081/8082)
curl -sSI http://127.0.0.1:8081/ | head -n 12 curl -sSI http://127.0.0.1:8082/ | head -n 12
7) Tests de non-régression (MINIMAL CHECKLIST)
À exécuter systématiquement après up.
7.1 Upstreams directs
curl -sSI http://127.0.0.1:8081/ | head -n 12 curl -sSI http://127.0.0.1:8082/ | head -n 12
7.2 Canonical (anti “localhost en prod”)
curl -sS http://127.0.0.1:8081/ | grep -oE 'rel="canonical" href="[^"]+"' | head -n 1 curl -sS http://127.0.0.1:8082/ | grep -oE 'rel="canonical" href="[^"]+"' | head -n 1
Attendu :
blue (8081) → https://staging.archicratie.../
green (8082) → https://archicratie.../
7.3 Edge routing (Host header + diag)
curl -sSI -H 'Host: staging.archicratie.trans-hands.synology.me' http://127.0.0.1:18080/
| grep -iE 'HTTP/|location:|x-archi-router' | head -n 30
curl -sSI -H 'Host: staging.archicratie.trans-hands.synology.me' http://127.0.0.1:18080/_auth/whoami
| grep -iE 'HTTP/|location:|x-archi-router' | head -n 30
7.4 Smoke UI (manuel)
Home : lien “Essai-thèse — ArchiCraT-IA” → /archicrat-ia/
TOC global : liens /archicrat-ia/* (pas de préfixe /archicratie/archicrat-ia/*)
Reading-follow/TOC local : scroll ok
8) Rollback (si un seul test est mauvais)
Objectif : revenir immédiatement à l’état précédent.
8.1 Repointer current sur l’ancien backup
cd /volume2/docker/archicratie-web ls -la current.BAK.* | tail -n 5
choisir le plus récent
OLD="current.BAK.YYYY-MM-DD-HHMMSS" sudo rm -f current sudo ln -s "$(readlink -f "$OLD")" current 2>/dev/null || sudo ln -s "$(readlink "$OLD")" current
ls -la current readlink current
8.2 Rebuild + recreate
cd /volume2/docker/archicratie-web/current sudo env DOCKER_API_VERSION=1.43 docker compose build --no-cache web_blue web_green sudo env DOCKER_API_VERSION=1.43 docker compose up -d --force-recreate web_blue web_green
8.3 Re-tester la checklist (section 7)
Si rollback OK : investiguer en environnement isolé (staging upstream uniquement, ou release dans un autre current).
9) Notes opérationnelles
Ne jamais modifier dist/ “à la main” sur NAS.
Si un hotfix prod est indispensable : documenter et backporter via PR Gitea.
Le canonical dépend du build : PUBLIC_SITE doit être injecté (voir runbook ENV-PUBLIC_SITE).
10) CI Deploy (Gitea Actions) — Gate SKIP / HOTPATCH / FULL (merge-proof) + preuves
Cette section documente le comportement canonique du workflow :
.gitea/workflows/deploy-staging-live.yml
Objectif : zéro surprise. On ne veut plus “penser à force=1”. Le gate doit décider automatiquement, y compris sur des merge commits.
10.1 — Principe (ce que fait réellement le gate)
Le job deploy calcule les fichiers modifiés entre :
BEFORE= commit précédent (avant le push sur main)AFTER= commit actuel (après le push / merge sur main)
Puis il classe le déploiement dans un mode :
-
MODE=full
- rebuild image + restart
archicratie-web-blue(8081) +archicratie-web-green(8082) - warmup endpoints (para-index, annotations-index, pagefind.js)
- vérification canonical staging + live
- rebuild image + restart
-
MODE=hotpatch
- rebuild d’un
annotations-index.jsonconsolidé depuissrc/annotations/** - patch direct dans les conteneurs en cours d’exécution (blue+green)
- copie des médias modifiés
public/media/**vers/usr/share/nginx/html/media/** - smoke sur
/annotations-index.jsondes deux ports
- rebuild d’un
-
MODE=skip
- pas de déploiement (on évite le bruit)
⚠️ Important : le mode “hotpatch” ne rebuild pas Astro. Donc toute modification de contenu, routes, scripts, anchors, etc. doit déclencher full.
10.2 — Matrice de décision (règles officielles)
Le gate définit deux flags :
HAS_FULL=1si changement “build-impacting”HAS_HOTPATCH=1si changement “annotations/media only”
Règle de priorité :
- Si
HAS_FULL=1→ MODE=full - Sinon si
HAS_HOTPATCH=1→ MODE=hotpatch - Sinon → MODE=skip
10.2.1 — Changements qui déclenchent FULL (build-impacting)
Exemples typiques (non exhaustif, mais on couvre le cœur) :
src/content/**(contenu MD/MDX)src/pages/**(routes Astro)src/anchors/**(aliases d’ancres)scripts/**(tooling postbuild : injection, index, tests)src/layouts/**,src/components/**,src/styles/**(rendu et scripts inline)astro.config.mjs,package.json,package-lock.jsonDockerfile,docker-compose.yml,nginx.conf.gitea/workflows/**(changement infra CI/CD)
=> On veut full pour garantir cohérence et éviter “site partiellement mis à jour”.
10.2.2 — Changements qui déclenchent HOTPATCH (sans rebuild)
Uniquement :
src/annotations/**(shards YAML)public/media/**(assets média)
=> On veut hotpatch pour vitesse et éviter rebuild NAS.
10.3 — “Merge-proof” : pourquoi on ne lit PAS seulement git show $SHA
Sur un merge commit, git show --name-only $SHA peut être trompeur selon le contexte.
La méthode robuste est :
- utiliser
event.json(Gitea Actions) pour récupérerbeforeetafter - calculer
git diff --name-only BEFORE AFTER
C’est ce qui rend le gate merge-proof.
10.4 — Tests de preuve A/B (reproductibles)
Ces tests valident le gate sans ambiguïté. But : vérifier que le mode choisi est EXACTEMENT celui attendu.
Test A — toucher src/content/... (FULL auto)
- Créer une branche test
- Modifier 1 fichier dans
src/content/(ex : ajouter une ligne de commentaire non destructive) - PR → merge dans
main - Vérifier dans
deploy-staging-live.yml:
Attendus :
Gate flags: HAS_FULL=1 HAS_HOTPATCH=0✅ build-impacting change -> MODE=full (rebuild+restart)- Les étapes FULL (blue puis green) s’exécutent réellement
Test B — toucher src/annotations/... uniquement (HOTPATCH auto)
- Créer une branche test
- Modifier 1 fichier sous
src/annotations/**(ex: un champ comment, ts, etc.) - PR → merge dans
main - Vérifier dans
deploy-staging-live.yml:
Attendus :
Gate flags: HAS_FULL=0 HAS_HOTPATCH=1✅ annotations/media change -> MODE=hotpatch- Les étapes FULL sont “skip” (durée 0s)
- L’étape HOTPATCH s’exécute réellement
10.5 — Preuve opérationnelle côté NAS (2 URLs + 2 commandes)
But : prouver que staging+live servent bien les endpoints essentiels (et que le déploiement n’a pas “fait semblant”).
10.5.1 — Deux URLs à vérifier (staging et live)
- Staging (blue) :
http://127.0.0.1:8081/ - Live (green) :
http://127.0.0.1:8082/
10.5.2 — Deux commandes minimales (zéro débat)
curl -fsSI http://127.0.0.1:8081/ | head -n 1
curl -fsSI http://127.0.0.1:8082/ | head -n 1