La sauvegarde ne couvrait que /etc, /home et /root : la base MariaDB du portail et son .env (APP_KEY) en étaient absents. Ajoute aussi le réglage du quota et du gabarit du projet des labs dans Ansible, la reprise des VM au démarrage de l'hôte (Nova), et le script du test de capacité. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
105 lines
4.2 KiB
Django/Jinja
105 lines
4.2 KiB
Django/Jinja
#!/bin/bash
|
|
# Géré par Ansible (rôle sauvegardes) — ne pas modifier à la main.
|
|
#
|
|
# Archive {{ sauvegardes_repertoires | join(', ') }} en tar.zst, chiffre avec age
|
|
# (clé publique du serveur — la clé privée ne quitte jamais le serveur, voir
|
|
# {{ sauvegardes_age_cle }}), envoie vers Dedibackup en FTP, puis applique la
|
|
# rotation ({{ sauvegardes_retention_quotidiennes }} quotidiennes,
|
|
# {{ sauvegardes_retention_hebdomadaires }} hebdomadaires).
|
|
set -euo pipefail
|
|
|
|
# Les identifiants ne sont jamais affichés ni journalisés : on désactive
|
|
# l'option xtrace pour cette ligne précise, par précaution.
|
|
set +x
|
|
source /root/.dedibackup
|
|
|
|
AGE_KEY="{{ sauvegardes_age_cle }}"
|
|
AGE_PUB=$(grep -oP '(?<=public key: )age1\S+' "$AGE_KEY")
|
|
|
|
DATE=$(date +%Y%m%d)
|
|
DOW=$(date +%u)
|
|
SEMAINE=$(date +%G-W%V)
|
|
|
|
WORKDIR=$(mktemp -d)
|
|
trap 'rm -rf "$WORKDIR"' EXIT
|
|
|
|
ARCHIVE_QUOT="quotidienne-${DATE}.tar.zst.age"
|
|
|
|
# Portail (étape 13) : export cohérent de sa base (--single-transaction, sans
|
|
# bloquer le site) et ses fichiers d'état. Le mot de passe root est lu dans
|
|
# le conteneur : il n'apparaît jamais sur la ligne de commande de l'hôte.
|
|
# Absent sur un serveur neuf (la sauvegarde arrive avant le portail) : ignoré.
|
|
EN_PLUS=()
|
|
if [ "$(docker inspect -f '{% raw %}{{.State.Running}}{% endraw %}' {{ sauvegardes_portail_conteneur }} 2>/dev/null)" = "true" ]; then
|
|
docker exec {{ sauvegardes_portail_conteneur }} sh -c \
|
|
'MYSQL_PWD="$MARIADB_ROOT_PASSWORD" exec mariadb-dump -uroot --single-transaction --routines --triggers --databases {{ sauvegardes_portail_base }}' \
|
|
> "$WORKDIR/portail.sql"
|
|
EN_PLUS+=(-C "$WORKDIR" portail.sql)
|
|
fi
|
|
for chemin in {{ sauvegardes_portail_fichiers | join(' ') }}; do
|
|
if [ -e "$chemin" ]; then
|
|
EN_PLUS+=("$chemin")
|
|
fi
|
|
done
|
|
|
|
tar --exclude="{{ sauvegardes_age_dir }}" --exclude='/root/.dedibackup' \
|
|
-I zstd -cf - {{ sauvegardes_repertoires | join(' ') }} "${EN_PLUS[@]}" \
|
|
| age -r "$AGE_PUB" -o "$WORKDIR/$ARCHIVE_QUOT"
|
|
|
|
lftp -u "$DEDIBACKUP_USER,$DEDIBACKUP_PASS" "$DEDIBACKUP_HOST" -e "
|
|
set ftp:ssl-allow no
|
|
put $WORKDIR/$ARCHIVE_QUOT
|
|
quit
|
|
"
|
|
logger -t sauvegarde-dedibackup "Quotidienne envoyée : $ARCHIVE_QUOT"
|
|
|
|
# Métrique pour l'alerte Prometheus "SauvegardeAncienne" (étape 7) : écrite
|
|
# seulement une fois la sauvegarde confirmée envoyée, de façon atomique
|
|
# (fichier temporaire puis renommage) pour que node_exporter ne lise jamais
|
|
# un fichier à moitié écrit.
|
|
DOSSIER_TEXTFILE="{{ sauvegardes_dossier_textfile | default('/var/lib/node_exporter/textfile') }}"
|
|
mkdir -p "$DOSSIER_TEXTFILE"
|
|
cat > "$DOSSIER_TEXTFILE/sauvegarde.prom.tmp" << METRIQUE
|
|
# HELP syllabus_derniere_sauvegarde_reussie_timestamp Horodatage Unix de la dernière sauvegarde Dedibackup réussie.
|
|
# TYPE syllabus_derniere_sauvegarde_reussie_timestamp gauge
|
|
syllabus_derniere_sauvegarde_reussie_timestamp $(date +%s)
|
|
METRIQUE
|
|
mv "$DOSSIER_TEXTFILE/sauvegarde.prom.tmp" "$DOSSIER_TEXTFILE/sauvegarde.prom"
|
|
|
|
if [ "$DOW" = "{{ sauvegardes_jour_hebdomadaire }}" ]; then
|
|
ARCHIVE_HEBDO="hebdomadaire-${SEMAINE}.tar.zst.age"
|
|
cp "$WORKDIR/$ARCHIVE_QUOT" "$WORKDIR/$ARCHIVE_HEBDO"
|
|
lftp -u "$DEDIBACKUP_USER,$DEDIBACKUP_PASS" "$DEDIBACKUP_HOST" -e "
|
|
set ftp:ssl-allow no
|
|
put $WORKDIR/$ARCHIVE_HEBDO
|
|
quit
|
|
"
|
|
logger -t sauvegarde-dedibackup "Hebdomadaire envoyée : $ARCHIVE_HEBDO"
|
|
fi
|
|
|
|
# --- Rotation : on ne garde que les N plus récentes de chaque catégorie. ---
|
|
rotation() {
|
|
local motif="$1" garder="$2"
|
|
local liste a_supprimer
|
|
liste=$(lftp -u "$DEDIBACKUP_USER,$DEDIBACKUP_PASS" "$DEDIBACKUP_HOST" -e "
|
|
set ftp:ssl-allow no
|
|
cls -1 ${motif}
|
|
quit
|
|
" 2>/dev/null | sort)
|
|
a_supprimer=$(echo "$liste" | head -n -"$garder")
|
|
if [ -n "$a_supprimer" ]; then
|
|
local commandes=""
|
|
while IFS= read -r fichier; do
|
|
[ -n "$fichier" ] && commandes+="rm $fichier;"
|
|
done <<< "$a_supprimer"
|
|
lftp -u "$DEDIBACKUP_USER,$DEDIBACKUP_PASS" "$DEDIBACKUP_HOST" -e "
|
|
set ftp:ssl-allow no
|
|
$commandes
|
|
quit
|
|
"
|
|
logger -t sauvegarde-dedibackup "Rotation $motif : supprimé $(echo "$a_supprimer" | wc -l) fichier(s)."
|
|
fi
|
|
}
|
|
|
|
rotation "quotidienne-*.tar.zst.age" {{ sauvegardes_retention_quotidiennes }}
|
|
rotation "hebdomadaire-*.tar.zst.age" {{ sauvegardes_retention_hebdomadaires }}
|