feat: add NAS backup, verification, and DR scripts
Some checks failed
Deploy to Production / Build and Deploy (push) Has been cancelled
Some checks failed
Deploy to Production / Build and Deploy (push) Has been cancelled
This commit is contained in:
@@ -1,102 +1,319 @@
|
||||
# Playbook de Sauvegarde Complète & Reprise d'Activité (Disaster Recovery)
|
||||
> Gestion des pannes matérielles, sauvegarde de Nginx Proxy Manager (NPM) et transfert distant (sans NAS).
|
||||
# Disaster Recovery — Wordly.art
|
||||
## Guide opérationnel complet
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Objectif
|
||||
Ce document explique comment automatiser la sauvegarde et restaurer l'intégralité de la plateforme SaaS **Wordly.art** (Base de données, fichier de configuration `.env` contenant vos secrets, et configurations de routage SSL/Proxy de **Nginx Proxy Manager**) sur un nouveau serveur en cas de crash du serveur principal.
|
||||
## Architecture
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 1. Variables de configuration dans le `.env`
|
||||
|
||||
Pour activer les options de reprise d'activité, ajoutez ces variables dans votre fichier `.env` de production :
|
||||
|
||||
```ini
|
||||
# ============== Configuration Disaster Recovery (DR) ==============
|
||||
# Choix de la destination : LOCAL, NAS, ou SCP
|
||||
BACKUP_DEST_TYPE=LOCAL
|
||||
# Chemin local ou point de montage (ex: /mnt/nas-backups/wordly)
|
||||
BACKUP_DEST_PATH=/var/backups/wordly
|
||||
|
||||
# Configuration SSH/SCP (requis uniquement si BACKUP_DEST_TYPE=SCP)
|
||||
SCP_HOST=192.168.1.200
|
||||
SCP_USER=backup_user
|
||||
SCP_KEY_PATH=/root/.ssh/id_rsa
|
||||
SCP_PORT=22
|
||||
SCP_DEST_PATH=/var/backups/wordly_saas
|
||||
|
||||
# Configurations des dossiers de Nginx Proxy Manager (NPM)
|
||||
# Laissez vide si NPM tourne sur une autre machine et n'est pas géré ici.
|
||||
NPM_DATA_DIR=/opt/npm/data
|
||||
NPM_LETSENCRYPT_DIR=/opt/npm/letsencrypt
|
||||
```
|
||||
[ Internet ]
|
||||
│
|
||||
▼ (80/443)
|
||||
┌────────────────────────────────────┐
|
||||
│ NPM dédié : 192.168.1.184 │ ← STABLE (ne tombe pas)
|
||||
│ Interface admin : :81 │
|
||||
└────────────┬───────────────────────┘
|
||||
│ Forward Hostname → IP du serveur actif
|
||||
▼
|
||||
┌────────────────────────────────────┐
|
||||
│ Serveur APP : 192.168.1.151 │ ← PEUT CRASHER
|
||||
│ Docker: postgres, redis, │
|
||||
│ backend:8001, frontend:3000│
|
||||
└────────────┬───────────────────────┘
|
||||
│ rsync SSH toutes les 6h (cron)
|
||||
▼
|
||||
┌────────────────────────────────────────────────┐
|
||||
│ NAS Synology : 192.168.1.146 │ ← SOURCE DE VÉRITÉ
|
||||
│ Chemin réel : /volume1/backups/wordly │
|
||||
│ Accès : SSH key (wordly-backup@nas) │
|
||||
│ Pas de montage CIFS — rsync direct │
|
||||
└────────────┬───────────────────────────────────┘
|
||||
│ (en cas de crash de .151)
|
||||
▼
|
||||
┌────────────────────────────────────┐
|
||||
│ Serveur SECOURS : 192.168.1.98 │ ← Docker déjà installé
|
||||
│ Restaure via rsync SSH depuis NAS│
|
||||
│ → NPM redirigé automatiquement │
|
||||
└────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Pourquoi rsync SSH et pas CIFS/SMB ?**
|
||||
- Pas de montage à gérer, pas de `/etc/fstab` à configurer
|
||||
- Fonctionne même si le NAS redémarre (pas de montage stale)
|
||||
- Chemin exact `/volume1/backups/wordly` utilisable directement
|
||||
- SSH chiffré, clé sans mot de passe pour l'automatisation
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 2. Comment configurer la sauvegarde à distance (Mode SCP)
|
||||
## RPO / RTO
|
||||
|
||||
Si vous n'avez pas de NAS, le mode **SCP** permet d'envoyer chaque nuit l'archive complète vers une autre machine ou ordinateur de votre réseau local (ex: `192.168.1.200`).
|
||||
| Scénario | Données perdues max | Temps de remise en route | Procédure |
|
||||
|----------|--------------------|--------------------------|-|
|
||||
| Container crashe | 0 | ~30s | Autorestart Docker |
|
||||
| Process PostgreSQL crashe | 0–5s | ~1 min | Autorestart + WAL |
|
||||
| Corruption DB partielle | 0–6h | ~5 min | Restore depuis NAS |
|
||||
| Serveur .151 mort | 0–6h | **~25 min** | Restore NAS sur .98 + NPM auto |
|
||||
| Erreur humaine (DROP) | 0–6h | ~5 min | Restore snapshot précédent |
|
||||
|
||||
---
|
||||
|
||||
## Ce qui est sauvegardé
|
||||
|
||||
| Composant | Sauvegardé | Fréquence |
|
||||
|-----------|-----------|-----------|
|
||||
| PostgreSQL `pg_dump` | ✅ | Toutes les 6h |
|
||||
| `.env` (secrets, clés API, Stripe...) | ✅ | Dans chaque archive DR |
|
||||
| `docker-compose.yml` | ✅ | Dans chaque archive DR |
|
||||
| Dossier `docker/` (configs) | ✅ | Dans chaque archive DR |
|
||||
| Redis | ❌ | Cache — sessions perdues à la restore (reconnexion users) |
|
||||
| Config NPM | ❌ | NPM sur .184 (stable). Seul Forward Host change via API. |
|
||||
| Métriques Prometheus | ❌ | Non critique, repart de zéro |
|
||||
|
||||
---
|
||||
|
||||
## 1. SETUP INITIAL (une seule fois sur .151)
|
||||
|
||||
### Étape 1 : Créer le compte sur le NAS Synology
|
||||
|
||||
**Connectez-vous à l'interface DSM : `http://192.168.1.146:5000`**
|
||||
|
||||
#### 1a. Créer l'utilisateur dédié
|
||||
|
||||
```
|
||||
DSM → Panneau de configuration → Utilisateurs et groupes → Créer
|
||||
Nom d'utilisateur : wordly-backup
|
||||
Mot de passe : [choisissez un mot de passe fort]
|
||||
☑ L'utilisateur ne peut pas changer son mot de passe
|
||||
→ Suivant
|
||||
|
||||
Permissions sur les dossiers partagés :
|
||||
backups → ☑ Lecture/Écriture
|
||||
→ Suivant → Terminer
|
||||
```
|
||||
|
||||
#### 1b. Activer SSH sur le NAS
|
||||
|
||||
```
|
||||
DSM → Panneau de configuration → Terminal et SNMP
|
||||
☑ Activer le service SSH
|
||||
Port : 22 (ou autre si vous avez changé)
|
||||
→ Appliquer
|
||||
```
|
||||
|
||||
#### 1c. Créer le dossier wordly sur le NAS
|
||||
|
||||
### Étape A : Générer une clé SSH sur le serveur principal
|
||||
Sur le serveur applicatif (`192.168.1.151`), si vous n'avez pas de clé SSH :
|
||||
```bash
|
||||
sudo ssh-keygen -t rsa -b 4096 -N "" -f /root/.ssh/id_rsa
|
||||
```
|
||||
|
||||
### Étape B : Autoriser la connexion sur la machine de backup
|
||||
Copiez la clé publique sur votre machine de sauvegarde (`192.168.1.200`) :
|
||||
```bash
|
||||
sudo ssh-copy-id -i /root/.ssh/id_rsa.pub backup_user@192.168.1.200
|
||||
```
|
||||
*Vérification* : Exécutez `sudo ssh -i /root/.ssh/id_rsa backup_user@192.168.1.200` depuis le serveur principal. Vous devez vous connecter **sans saisir de mot de passe**.
|
||||
|
||||
---
|
||||
|
||||
## 📅 3. Automatisation quotidienne
|
||||
|
||||
Ajoutez le script à votre crontab pour qu'il s'exécute automatiquement chaque nuit à 03h30 :
|
||||
```bash
|
||||
sudo crontab -e
|
||||
```
|
||||
Ajoutez cette ligne tout à la fin :
|
||||
```cron
|
||||
30 3 * * * /opt/wordly/scripts/disaster-recovery.sh --backup >> /var/log/wordly-dr-backup.log 2>&1
|
||||
# Depuis votre poste (ou n'importe quelle machine sur le réseau) :
|
||||
ssh admin@192.168.1.146
|
||||
mkdir -p /volume1/backups/wordly/snapshots
|
||||
mkdir -p /volume1/backups/wordly/scripts
|
||||
chown -R wordly-backup:users /volume1/backups/wordly
|
||||
exit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚨 4. Procédure de restauration sur un nouveau serveur (Failover)
|
||||
### Étape 2 : Configurer les variables dans `.env` sur `.151`
|
||||
|
||||
Si le serveur principal crashe complètement et que vous devez remonter l'infrastructure sur un serveur de secours (ex: `192.168.1.152`) :
|
||||
|
||||
### Étape 4.1 : Récupérer l'archive de sauvegarde
|
||||
Récupérez le dernier fichier `wordly_dr_TIMESTAMP.tar.gz` depuis votre stockage de backup (NAS, machine de backup distante via SCP, ou clé USB).
|
||||
|
||||
### Étape 4.2 : Installer Docker sur le nouveau serveur
|
||||
```bash
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
sudo usermod -aG docker $USER && newgrp docker
|
||||
# ── NAS SSH ───────────────────────────────────
|
||||
NAS_HOST=192.168.1.146
|
||||
NAS_USER=wordly-backup
|
||||
NAS_PATH=/volume1/backups/wordly
|
||||
NAS_SSH_PORT=22
|
||||
NAS_SSH_KEY=/root/.ssh/wordly_nas_key
|
||||
|
||||
# ── Alertes Telegram ──────────────────────────
|
||||
TELEGRAM_BOT_TOKEN= # Voir section "Créer un bot Telegram" ci-dessous
|
||||
TELEGRAM_CHAT_ID= # Votre chat ID personnel
|
||||
|
||||
# ── NPM Failover API ──────────────────────────
|
||||
NPM_API_URL=http://192.168.1.184:81/api
|
||||
NPM_ADMIN_EMAIL=admin@wordly.art
|
||||
NPM_ADMIN_PASSWORD=VotreMotDePasseNPM
|
||||
NPM_PROXY_HOST_DOMAIN=wordly.art
|
||||
|
||||
# ── Rétention ────────────────────────────────
|
||||
DAILY_RETENTION=7
|
||||
WEEKLY_RETENTION=4
|
||||
MONTHLY_RETENTION=6
|
||||
DR_RETENTION_DAYS=30
|
||||
```
|
||||
|
||||
### Étape 4.3 : Lancer la restauration automatique
|
||||
1. Créez le dossier de destination et placez-vous dedans :
|
||||
```bash
|
||||
sudo mkdir -p /opt/wordly
|
||||
cd /opt/wordly
|
||||
```
|
||||
2. Lancez le script de restauration à partir de l'archive (le script va extraire le `.env`, copier le `docker-compose.yml`, restaurer les configurations et certificats SSL de NPM, démarrer Docker et réinjecter les données de la base de données) :
|
||||
```bash
|
||||
# Remplacez par le nom ou le chemin exact de votre archive
|
||||
bash /chemin/vers/votre/archive/scripts/disaster-recovery.sh --restore /chemin/vers/votre/archive/wordly_dr_20260607_033000.tar.gz
|
||||
```
|
||||
3. Validez l'action en saisissant `RESTORE-ALL` lorsque le script vous le demande.
|
||||
---
|
||||
|
||||
### Étape 4.4 : Redirection du trafic réseau
|
||||
Puisque le serveur a changé d'adresse IP (de `192.168.1.151` à `192.168.1.152`) :
|
||||
### Étape 3 : Créer un bot Telegram (5 minutes)
|
||||
|
||||
#### Cas A : Si NPM tournait sur le serveur qui a crashé
|
||||
Le script a restauré NPM sur la nouvelle machine. Vous devez simplement aller sur le routeur de votre box internet et modifier la redirection des ports **80** et **443** (Port Forwarding) pour qu'ils pointent vers la nouvelle IP `192.168.1.152` au lieu de `192.168.1.151`.
|
||||
1. Ouvrir Telegram → chercher **@BotFather**
|
||||
2. Envoyer `/newbot`
|
||||
3. Nom : `Wordly Monitoring` / Username : `wordly_monitor_bot`
|
||||
4. Copier le token → `TELEGRAM_BOT_TOKEN`
|
||||
5. Envoyer un message à votre bot
|
||||
6. Aller sur `https://api.telegram.org/bot<TOKEN>/getUpdates`
|
||||
7. Copier le `chat.id` → `TELEGRAM_CHAT_ID`
|
||||
|
||||
#### Cas B : Si NPM tourne sur une machine externe dédiée
|
||||
Connectez-vous à l'interface web de votre NPM (http://IP_NPM:81), modifiez les Proxy Hosts de `wordly.art` et changez le champ **Forward Hostname/IP** pour remplacer `192.168.1.151` par la nouvelle IP `192.168.1.152`.
|
||||
---
|
||||
|
||||
### Étape 4 : Configurer SSH sans mot de passe vers le NAS
|
||||
|
||||
```bash
|
||||
# Sur le serveur .151 (en root)
|
||||
sudo bash scripts/setup-nas.sh
|
||||
```
|
||||
|
||||
Ce script :
|
||||
- Génère une clé SSH dédiée : `/root/.ssh/wordly_nas_key`
|
||||
- La copie sur le NAS (**mot de passe demandé une seule fois**)
|
||||
- Teste la connexion sans mot de passe
|
||||
- Crée la structure de dossiers sur le NAS
|
||||
- Configure `~/.ssh/config` avec l'alias `wordly-nas`
|
||||
- Copie les scripts sur le NAS (disponibles depuis `.98` pour la restauration)
|
||||
|
||||
---
|
||||
|
||||
### Étape 5 : Tester le premier backup
|
||||
|
||||
```bash
|
||||
bash scripts/backup-to-nas.sh --full
|
||||
|
||||
# Vérifier que l'archive est bien arrivée sur le NAS
|
||||
bash scripts/backup-to-nas.sh --list
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Étape 6 : Tester la vérification automatique
|
||||
|
||||
```bash
|
||||
bash scripts/verify-backups.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Étape 7 : Tester le failover NPM (sans rien changer)
|
||||
|
||||
```bash
|
||||
bash scripts/npm-failover.sh --dry-run --target-ip 192.168.1.98
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Étape 8 : Activer les crons
|
||||
|
||||
```bash
|
||||
bash scripts/install-crontab.sh
|
||||
crontab -l # Vérifier
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. VÉRIFICATION QUOTIDIENNE (automatique)
|
||||
|
||||
```
|
||||
0 */6 * * * backup-to-nas.sh → Snapshot DB + archive → NAS via rsync SSH
|
||||
30 */6 * * * verify-backups.sh → 8 vérifications + alerte Telegram si erreur
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. RESTAURATION D'URGENCE (quand .151 est mort)
|
||||
|
||||
> **Durée estimée : 20–25 minutes**
|
||||
|
||||
### Sur le serveur de secours `192.168.1.98`
|
||||
|
||||
```bash
|
||||
# 1. Installer les prérequis (Docker déjà installé)
|
||||
apt-get install -y rsync jq
|
||||
|
||||
# 2. Récupérer la clé SSH depuis le NAS (ou depuis une autre source sécurisée)
|
||||
# Option A : copier la clé depuis un endroit sûr (gestionnaire de mots de passe, etc.)
|
||||
mkdir -p /root/.ssh && chmod 700 /root/.ssh
|
||||
# collez le contenu de /root/.ssh/wordly_nas_key ici
|
||||
nano /root/.ssh/wordly_nas_key
|
||||
chmod 600 /root/.ssh/wordly_nas_key
|
||||
|
||||
# 3. Tester la connexion NAS
|
||||
ssh -i /root/.ssh/wordly_nas_key wordly-backup@192.168.1.146 "echo OK"
|
||||
|
||||
# 4. Voir les archives disponibles
|
||||
ssh -i /root/.ssh/wordly_nas_key wordly-backup@192.168.1.146 \
|
||||
"ls -lht /volume1/backups/wordly/snapshots/ | head -10"
|
||||
|
||||
# 5. Télécharger la dernière archive depuis le NAS
|
||||
rsync -az \
|
||||
-e "ssh -i /root/.ssh/wordly_nas_key" \
|
||||
wordly-backup@192.168.1.146:/volume1/backups/wordly/snapshots/wordly_dr_TIMESTAMP.tar.gz \
|
||||
/tmp/
|
||||
|
||||
# 6. Télécharger les scripts de restauration depuis le NAS
|
||||
rsync -az \
|
||||
-e "ssh -i /root/.ssh/wordly_nas_key" \
|
||||
wordly-backup@192.168.1.146:/volume1/backups/wordly/scripts/ \
|
||||
/opt/wordly/scripts/
|
||||
|
||||
# 7. Lancer la restauration complète
|
||||
bash /opt/wordly/scripts/disaster-recovery.sh \
|
||||
--restore /tmp/wordly_dr_TIMESTAMP.tar.gz
|
||||
```
|
||||
|
||||
**Le script fait automatiquement :**
|
||||
1. Extrait `.env`, `docker-compose.yml`, configs Docker
|
||||
2. Lance tous les containers Docker
|
||||
3. Attend que PostgreSQL soit healthy
|
||||
4. Restaure le dump SQL
|
||||
5. Health check sur `http://localhost:8001/health` (max 180s)
|
||||
6. **Si OK → appelle NPM API → bascule le trafic vers `192.168.1.98`**
|
||||
7. **Alerte Telegram : "✅ Wordly.art DR COMPLET"**
|
||||
|
||||
**Si NPM failover automatique échoue (dernier recours) :**
|
||||
```
|
||||
http://192.168.1.184:81 → Proxy Hosts → wordly.art → Edit
|
||||
Forward Hostname : 192.168.1.98
|
||||
→ Save
|
||||
# Changement immédiat, 0 redémarrage nécessaire
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. CONSERVATION DE LA CLÉ SSH NAS
|
||||
|
||||
> [!IMPORTANT]
|
||||
> La clé `/root/.ssh/wordly_nas_key` est **critique** pour la restauration depuis `.98`.
|
||||
> Conservez-la dans au minimum 2 endroits sécurisés :
|
||||
> - Gestionnaire de mots de passe (Bitwarden, 1Password, etc.)
|
||||
> - Coffre-fort KeePass chiffré sur un support physique
|
||||
>
|
||||
> Sans cette clé, vous ne pouvez pas accéder aux archives sur le NAS depuis `.98`.
|
||||
|
||||
---
|
||||
|
||||
## 5. SCRIPTS DE RÉFÉRENCE
|
||||
|
||||
| Script | Usage | Déclenchement |
|
||||
|--------|-------|---------------|
|
||||
| `setup-nas.sh` | Configure SSH → NAS, génère clé, copie scripts | **Once** (root requis) |
|
||||
| `backup-to-nas.sh` | pg_dump + archive DR → NAS via rsync SSH | Cron toutes les 6h |
|
||||
| `backup-to-nas.sh --list` | Lister les archives disponibles sur le NAS | Manuel |
|
||||
| `verify-backups.sh` | 8 checks intégrité + Telegram | Cron toutes les 6h+30m |
|
||||
| `disaster-recovery.sh --backup` | Archive DR → NAS | Inclus dans backup-to-nas |
|
||||
| `disaster-recovery.sh --restore <archive>` | Restauration complète | **Urgence** |
|
||||
| `npm-failover.sh --target-ip <IP>` | Bascule NPM vers une IP | Appelé automatiquement |
|
||||
| `npm-failover.sh --dry-run --target-ip <IP>` | Test sans modifier NPM | Test initial |
|
||||
| `install-crontab.sh` | Installe les crons | **Once** |
|
||||
|
||||
---
|
||||
|
||||
## 6. LOGS
|
||||
|
||||
```bash
|
||||
# Logs backup (sur .151)
|
||||
tail -f /var/log/wordly-backup.log
|
||||
|
||||
# Logs vérification (sur .151)
|
||||
tail -f /var/log/wordly-verify.log
|
||||
|
||||
# Logs Docker (sur le serveur actif)
|
||||
docker compose logs -f backend
|
||||
docker compose logs -f postgres
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user