Aller au contenu

Sauvegarde et restauration

Ce qu'il faut sauvegarder

Élément Emplacement Criticité Reconstructible ?
Secrets .env critique non — perte définitive
Métadonnées Metabase base metabase haute par script, sans les personnalisations manuelles
Métadonnées Airflow base airflow moyenne oui, l'historique est perdu
Entrepôt base warehouse faible oui, entièrement rejouable
Certificats TLS data/letsencrypt/ faible oui, réémission automatique
Code dépôt Git déjà versionné

L'entrepôt est le moins critique : le jeu synthétique est déterministe et l'open data est réingérable. En revanche, .env est irremplaçable : MB_ENCRYPTION_SECRET_KEY déchiffre les secrets stockés par Metabase, AIRFLOW_FERNET_KEY ceux d'Airflow. Les perdre rend ces bases inutilisables.

Deux dispositifs complémentaires

Instantané OVH Vidage logique
Couvre VPS détruit, disque perdu erreur humaine, corruption, régression
Granularité le VPS entier une base, voire une table
Rétention ~7 jours 90 jours
Cohérence au crash — équivalent d'une coupure de courant logique, garantie par la transaction
Restauration réinstallation complète quelques secondes

Les deux ne se substituent pas. L'instantané OVH traite le sinistre ; le vidage logique traite le cas bien plus fréquent de l'erreur humaine — casser une configuration Metabase pendant une démonstration, par exemple. Avec l'instantané seul, il faudrait restaurer tout le VPS et perdre les ingestions du jour.

Vidage logique quotidien

scripts/sauvegarde.sh, déclenché par un timer systemd à 3h.

sudo systemctl status metrika-sauvegarde.timer   # état
systemctl list-timers metrika-sauvegarde.timer   # prochaine exécution
sudo journalctl -u metrika-sauvegarde -n 20      # journal
sudo systemctl start metrika-sauvegarde.service  # exécution immédiate

Les unités sont versionnées dans infra/systemd/. Installation :

sudo cp infra/systemd/metrika-sauvegarde.* /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now metrika-sauvegarde.timer

Un timer systemd plutôt que cron : Persistent=true rattrape l'exécution si la machine était éteinte à l'heure prévue, la journalisation passe par journalctl sans configuration, et aucun paquet supplémentaire n'est requis.

Ce qui est sauvegardé

Élément Taille Format
warehouse ~4,8 Mo pg_dump -Fc
metabase ~1,0 Mo pg_dump -Fc
airflow ~0,2 Mo pg_dump -Fc
.env 2 Ko copie, permissions 600
Mémoire de l'assistant ~5 Ko archive de ~/.claude/…/memory/

6 Mo par jour, soit 530 Mo pour 90 jours de rétention. Le format -Fc est compressé et permet à pg_restore d'extraire sélectivement une seule table.

Le script échoue explicitement si un fichier produit fait moins de 512 octets : une sauvegarde vide qui réussit en silence est pire que pas de sauvegarde du tout. Ce contrôle a immédiatement révélé un cas réel — voir ci-dessous.

Les certificats TLS ne sont pas sauvegardés

data/letsencrypt/acme.json appartient à root (créé par le conteneur Traefik) et n'est pas lisible par l'utilisateur qui exécute la sauvegarde. Une première version produisait donc une archive vide de 127 octets, sans rien signaler. Plutôt que d'ajouter une règle sudo, les certificats sont exclus : Traefik les réémet en quelques secondes.

Mémoire de l'assistant

Le contexte de collaboration accumulé au fil des sessions vit dans ~/.claude/projects/-home/memory/. Il n'est ni versionné ni reconstructible, d'où sa présence ici. Seul ce dossier est archivé : le reste de ~/.claude contient des jetons d'authentification.

.env est irremplaçable

MB_ENCRYPTION_SECRET_KEY déchiffre les secrets stockés par Metabase, AIRFLOW_FERNET_KEY ceux d'Airflow. Sans ce fichier, des bases restaurées sont inexploitables. C'est le seul élément qu'aucun script ne peut reconstruire.

Tout reste sur le même disque

Les vidages vivent dans /home/debian/sauvegardes, sur le disque qu'ils sauvegardent. C'est l'instantané OVH qui couvre la perte du disque — d'où l'importance de vérifier que cette option est bien active sur le VPS (espace client → VPS → onglet Sauvegardes). Pour une protection contre la perte simultanée des deux, il faudrait une copie chiffrée vers un stockage objet externe.

Restauration

Test de restauration

Une sauvegarde jamais restaurée n'est pas une sauvegarde. Vérification dans une base jetable, sans toucher à la production :

SAUV=$(ls -d /home/debian/sauvegardes/*/ | tail -1)
docker compose exec -T postgres psql -U metrika -d postgres \
  -c "CREATE DATABASE test_restauration OWNER warehouse;"
docker compose exec -T postgres pg_restore -U metrika -d test_restauration \
  --no-owner --role=warehouse < "$SAUV/warehouse.dump"
docker compose exec -T postgres psql -U metrika -d test_restauration -Atc \
  "SELECT count(*) FROM gold.fct_orders;"   # doit correspondre à la production
docker compose exec -T postgres psql -U metrika -d postgres \
  -c "DROP DATABASE test_restauration;"

Restauration réelle

# 1. Arrêter les consommateurs
docker compose stop metabase airflow-apiserver airflow-scheduler \
                    airflow-dag-processor airflow-triggerer

# 2. Restaurer une base
docker compose exec -T postgres psql -U metrika -d postgres \
  -c "DROP DATABASE metabase;" -c "CREATE DATABASE metabase OWNER metabase;"
docker compose exec -T postgres pg_restore -U metrika -d metabase \
  --no-owner --role=metabase < sauvegardes/20260819-0300/metabase.dump

# 3. Restaurer les secrets — obligatoire pour Metabase et Airflow
cp sauvegardes/20260819-0300/env .env

# 4. Redémarrer
docker compose up -d

Reconstruction complète sans sauvegarde

Si seul le code subsiste, l'entrepôt se reconstruit intégralement :

docker compose up -d postgres
docker compose exec airflow-scheduler airflow dags test 10_seed_bronze_demo
docker compose exec airflow-scheduler airflow backfill create \
  --dag-id 20_ingest_eco2mix --from-date <début> --to-date <fin> \
  --reprocess-behavior completed
python3 scripts/metabase_provision.py

Seules les personnalisations manuelles de Metabase sont perdues — c'est précisément l'intérêt d'avoir un provisionnement scripté.

Limite de l'open data

ODRÉ ne conserve qu'une fenêtre glissante d'environ 50 jours sur le jeu temps réel. Un historique plus profond ne peut pas être reconstitué depuis cette source : il faut la sauvegarde, ou basculer sur les jeux consolidés annuels.