Cours NestJS · Module 9: Déploiement et infrastructure

Sauvegarde et récupération - sauver les tributs après une catastrophe

5 min de lecture
Dans cette leçon4

Gardien des tributs ! Une nuit, une migration contenant une faute de frappe supprime une colonne, et au matin on découvre que la dernière copie fonctionnelle de la base date de trois semaines, car le script de sauvegarde échouait depuis un mois sans que personne ne le remarque. Même les meilleurs forts peuvent tomber en ruine pendant un siège, c'est pourquoi chaque légionnaire avisé a un plan pour sauver ses tributs.

Deux nombres par lesquels tout commence

Un plan de reprise commence par deux objectifs. Le RTO (Recovery Time Objective) est le temps maximal pour rétablir le service après une panne. Le RPO (Recovery Point Objective) est la perte de données acceptable, mesurée en temps : une sauvegarde par jour signifie que tu perds jusqu'à 24 heures de travail. Tu veux moins - il te faut des copies plus fréquentes ou un archivage continu du journal des modifications.

Garde les copies selon la règle 3-2-1 : trois copies, sur deux supports différents, dont une hors du site principal.

Database Backup Strategy

Le script pour PostgreSQL fait une sauvegarde, la vérifie, l'envoie sur S3 et nettoie les anciennes copies :

1#!/bin/bash
2# backup-database.sh
3set -euo pipefail
4
5# Sans adresse de base, le script échoue au lieu de sauvegarder une base par défaut
6: "${DATABASE_URL:?Définis DATABASE_URL}"
7BACKUP_DIR="${BACKUP_DIR:-/backups}"
8DATE=$(date +%Y%m%d_%H%M%S)
9BACKUP_NAME="legionariusze_legion_backup_${DATE}.dump"
10
11# Create backup (le format custom est déjà compressé)
12echo "Creating database backup..."
13pg_dump --format=custom --file="$BACKUP_DIR/$BACKUP_NAME" "$DATABASE_URL"
14
15# Verify backup - pg_restore doit pouvoir lire la table des matières
16pg_restore --list "$BACKUP_DIR/$BACKUP_NAME" > /dev/null
17
18# Upload to S3
19aws s3 cp "$BACKUP_DIR/$BACKUP_NAME" s3://legionariusze-legion-backups/
20
21# Cleanup old backups (keep last 7 days)
22find "$BACKUP_DIR" -name "*.dump" -mtime +7 -delete
23
24echo "Backup completed: ${BACKUP_NAME}"

set -euo pipefail interrompt le script à la première erreur, à une variable non définie ou à un échec dans un pipeline. La ligne avec :? est une correction : dans la première version, un DATABASE_URL vide faisait que pg_dump se connectait en silence à la base locale par défaut. Le format custom est déjà compressé, donc le gzip séparé a disparu, et pg_restore --list vérifie que le fichier est lisible.

Avec MongoDB, sur laquelle tournent beaucoup d'applications NestJS, la même stratégie ressemble à ceci :

1#!/bin/bash
2# backup-mongo.sh - la même stratégie pour MongoDB
3set -euo pipefail
4
5: "${MONGODB_URI:?Définis MONGODB_URI}"
6BACKUP_DIR="${BACKUP_DIR:-/backups/mongo}"
7TIMESTAMP=$(date +%Y%m%d_%H%M%S)
8ARCHIVE="$BACKUP_DIR/imperium_$TIMESTAMP.archive.gz"
9
10mkdir -p "$BACKUP_DIR"
11
12# Sauvegarder la base dans une seule archive compressée
13mongodump --uri="$MONGODB_URI" --archive="$ARCHIVE" --gzip
14
15# Vérification : restauration à blanc sans écriture de données
16mongorestore --uri="$MONGODB_URI" --archive="$ARCHIVE" --gzip --dryRun
17
18# Somme de contrôle pour vérifier la copie après le transfert
19sha256sum "$ARCHIVE" > "$ARCHIVE.sha256"
20
21# Rotation : supprimer les copies de plus de 7 jours
22find "$BACKUP_DIR" -name "imperium_*" -mtime +7 -delete
23
24echo "Backup completed: $ARCHIVE"

mongodump écrit la base dans une seule archive compressée, mongorestore --dryRun tente de la lire sans écrire de données, et la somme de contrôle permet de vérifier la copie après le transfert. Une restauration à blanc n'est pourtant pas un test : une copie que tu n'as jamais restaurée dans une base séparée n'est qu'un espoir.

Disaster Recovery Plan

Il vaut la peine d'écrire le plan de reprise sous forme de code, pour que personne ne saute une étape sous la pression :

1// Recovery service
2@Injectable()
3export class RecoveryService {
4  async restoreFromBackup(backupFile: string) {
5    // 1. Download backup from S3
6    const backup = await this.downloadBackup(backupFile);
7
8    // 2. Create new database
9    await this.createRecoveryDatabase();
10
11    // 3. Restore data
12    await this.restoreData(backup);
13
14    // 4. Verify integrity
15    await this.verifyDataIntegrity();
16
17    // 5. Switch traffic
18    await this.switchTraffic();
19  }
20}

Les méthodes du service sont un squelette à compléter selon ton infrastructure. L'ordre compte : tu crées la nouvelle base à côté de celle qui est endommagée, pas par-dessus, pour conserver des traces à analyser, et tu vérifies l'intégrité par le nombre d'enregistrements, une somme de contrôle et un smoke test de l'application avant de basculer le trafic. L'ensemble du processus de reprise après sinistre se déroule ainsi :

  1. détection et évaluation de l'incident,
  2. activation du plan de reprise après sinistre,
  3. restauration des services depuis les sauvegardes,
  4. validation de l'intégrité et reprise des opérations.

L'infrastructure en tant que code

Les données représentent la moitié de la reprise, l'autre moitié est l'infrastructure. L'Infrastructure as Code (IaC), par ex. Terraform ou Pulumi, décrit les serveurs, les réseaux et les bases dans des fichiers conservés dans un dépôt. Elle apporte le versionnement, la reproductibilité et l'automatisation : après une catastrophe, tu recrées l'environnement en une seule commande, même dans une autre région, au lieu de cliquer dans une console de mémoire.

L'image de l'application doit être tout aussi fiable. Un pipeline sécurisé de construction d'images Docker comporte ces étapes :

  1. un Dockerfile optimisé avec un build multi-étapes,
  2. l'analyse de sécurité et des vulnérabilités, par ex. avec Trivy,
  3. le tagging de l'image et le push vers un registry,
  4. la signature de l'image et l'attestation, par ex. avec cosign.

La signature est créée après le push, car elle porte sur un condensé précis de l'image dans le registry ; lors d'une reprise, tu ne déploies que des images à la signature valide.

Je te recommande de t'entraîner une fois par mois à une restauration complète sur un environnement neuf en mesurant sa durée : c'est le seul test honnête de ton RTO. Dans le projet qui clôt le module, tu réuniras la sauvegarde et tout le déploiement.

Retiens : on peut rebâtir un fort à partir de plans, mais on ne récupère pas les tributs sans une copie que quelqu'un a vraiment vérifiée.

Code de cette leçon : scripts/backup.sh
1#!/bin/bash
2# Sauvegarde et récupération - sauver les tributs après une catastrophe
3
4# === CONFIGURATION ===
5BACKUP_DIR="/backups/roman-empire"
6DB_NAME="roman_empire"
7DB_HOST="localhost"
8DB_PORT="27017"
9RETENTION_DAYS=7
10TIMESTAMP=$(date +%Y%m%d_%H%M%S)
11BACKUP_PATH="$BACKUP_DIR/$TIMESTAMP"
12
13echo "=== Roman Empire Database Backup ==="
14echo "Timestamp: $TIMESTAMP"
15echo "Database: $DB_NAME"
16
17# === 1. MONGODUMP ===
18mkdir -p "$BACKUP_PATH"
19
20echo "Starting mongodump..."
21mongodump \
22  --host "$DB_HOST" \
23  --port "$DB_PORT" \
24  --db "$DB_NAME" \
25  --out "$BACKUP_PATH"
26
27# Vérifier si le backup a réussi
28if [ $? -eq 0 ]; then
29  echo "Backup successful!"
30else
31  echo "ERROR: Backup FAILED!"
32  exit 1
33fi
34
35# === 2. COMPRESSION ===
36echo "Compressing backup..."
37tar -czf "$BACKUP_PATH.tar.gz" -C "$BACKUP_DIR" "$TIMESTAMP"
38rm -rf "$BACKUP_PATH"
39echo "Compressed: $BACKUP_PATH.tar.gz"
40
41# === 3. ROTATION (remove old backups) ===
42echo "Removing backups older than $RETENTION_DAYS days..."
43find "$BACKUP_DIR" -name "*.tar.gz" \
44  -mtime +$RETENTION_DAYS -delete
45
46# === 4. RESTORE (restauration) ===
47# mongorestore \
48#   --host "$DB_HOST" \
49#   --port "$DB_PORT" \
50#   --db "$DB_NAME" \
51#   --drop \
52#   "$BACKUP_PATH/$DB_NAME"
53
54# === 5. CRON - sauvegardes automatiques ===
55# Ajouter au crontab :
56# 0 2 * * * /scripts/backup.sh >> /var/log/backup.log 2>&1
57# (tous les jours à 2h00 du matin)
58
59# === 6. STRATÉGIE DE SAUVEGARDE ===
60# - Daily: sauvegarde complète toutes les 24h
61# - Incremental: changements toutes les 6h
62# - Offsite: copie sur un serveur externe
63# - Test restore: test recovery every month!
64
65echo "=== Backup Complete ==="
66echo "File: $BACKUP_PATH.tar.gz"
67echo "Size: $(du -h "$BACKUP_PATH.tar.gz" | cut -f1)"
68

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. L'Infrastructure as Code (IaC) permet :

  2. 2. RTO (Recovery Time Objective) et RPO (Recovery Point Objective) définissent :

Tâches pratiques dans le jeu

  • Éditeur de code

    Écris un script de sauvegarde avec mongodump, rotation et vérification de l'intégrité

  • Tri vertical

    Ordonne les étapes du processus de reprise après sinistre :

  • Tri vertical

    Ordonne les étapes d'un pipeline de build d'image Docker sécurisé :