2026-10-07 21:57:04 -04:00
#!/bin/bash
# Géré par Set-OPS (rôle client_backup). Ne pas éditer à la main.
#
2026-10-07 23:44:04 -04:00
# setops-copie-a-froid SERVICE SOURCE DESTINATION [--blocs-immuables] [--lier REL]... [--copier REL]...
2026-10-07 21:57:04 -04:00
#
# Copie COHERENTE d'une base qui ecrit en continu (Prometheus, Loki) : le service est
# arrete le temps de la copie, puis redemarre — QUOI QU'IL ARRIVE a la copie, et seulement
# s'il tournait avant. La destination n'est remplacee qu'une fois la copie complete.
#
# POURQUOI A FROID (decision de l'exploitant, 2026-10-07 : « on ne prend pas de risque »).
# Copier a chaud un WAL qu'on ecrit, c'est sauvegarder un fichier a moitie ecrit. L'autre
# voie — ne garder que les blocs fermes — perdait jusqu'aux deux dernieres heures de
# metriques a chaque reconstruction. Mesure au site : 2,9 Go de Prometheus se lisent en
# 4,2 s, son WAL seul en 0,3 s ; Loki entier en 2 s.
#
# `--blocs-immuables` : les sous-dossiers nommes par un ULID (26 caracteres, `01…`) sont
# les blocs de Prometheus, que rien ne reecrit apres leur fermeture. On les recopie par
# LIENS DURS — instantane, sans place — et le reste (WAL, `chunks_head`) par une vraie
# copie. Si la destination est sur un autre systeme de fichiers, on copie tout.
2026-10-07 23:44:04 -04:00
#
# `--lier REL` lie tout un sous-dossier ; `--copier REL` y copie vraiment un sous-dossier qui,
# lui, bouge (2026-10-07). Pour Loki : `--lier chunks --copier chunks/index`. Ses morceaux sont
# adresses par leur contenu (`debut:fin:somme`, decode sur site-mon-01) et ne bougent jamais ;
# mais `chunks/index/` loge aussi `delete_requests.gz`, la base du compacteur, mesuree
# reecrite trois semaines apres sa naissance. La lier, c'etait laisser la sauvegarde changer
# avec elle. Copier 6 550 morceaux arretait Loki 13 s ; les lier prend 40 ms.
2026-10-07 21:57:04 -04:00
set -euo pipefail
service = " ${ 1 : ?service } "
source = " ${ 2 : ?source } "
dest = " ${ 3 : ?destination } "
2026-10-07 23:44:04 -04:00
shift 3
blocs = "" lier = ( ) copier = ( )
while ( ( $# ) ) ; do
case " $1 " in
--blocs-immuables) blocs = 1; shift ; ;
--lier) lier += ( " $2 " ) ; shift 2 ; ;
--copier) copier += ( " $2 " ) ; shift 2 ; ;
*) echo " copie a froid : option inconnue : $1 " >& 2; exit 2 ; ;
esac
done
2026-10-07 21:57:04 -04:00
[ [ -d " ${ source } " ] ] || { echo " copie a froid : ${ source } n'existe pas — rien a copier. " >& 2; exit 0; }
tmp = " ${ dest } .en-cours "
rm -rf " ${ tmp } "
mkdir -p " $( dirname " ${ dest } " ) "
actif = 0
if systemctl is-active --quiet " ${ service } " ; then actif = 1; fi
relancer( ) { if ( ( actif ) ) ; then systemctl start " ${ service } " ; fi ; }
debut = $( date +%s%N)
if ( ( actif ) ) ; then systemctl stop " ${ service } " ; fi
trap relancer EXIT
2026-10-07 23:44:04 -04:00
lie( ) { cp -al " $1 " " $2 " 2>/dev/null || { rm -rf " $2 " ; cp -a " $1 " " $2 " ; } ; }
mkdir -p " ${ tmp } "
for e in " ${ source } " /* " ${ source } " /.[ !.] *; do
[ [ -e " ${ e } " ] ] || continue
n = " $( basename " ${ e } " ) "
if [ [ -n " ${ blocs } " && -d " ${ e } " && ${# n } -eq 26 && " ${ n } " = = 01* ] ] \
|| [ [ " ${ lier [*] :- } " = = *" ${ n } " * ] ] ; then
lie " ${ e } " " ${ tmp } / ${ n } "
else
cp -a " ${ e } " " ${ tmp } / ${ n } "
fi
done
# Ce qui bouge, a l'interieur d'un dossier lie : remplace par une vraie copie.
for c in " ${ copier [@] : + ${ copier [@] } } " ; do
if [ [ -e " ${ source } / ${ c } " ] ] ; then
rm -rf " ${ tmp : ? } / ${ c } "
mkdir -p " $( dirname " ${ tmp } / ${ c } " ) "
cp -a " ${ source } / ${ c } " " ${ tmp } / ${ c } "
fi
done
2026-10-07 21:57:04 -04:00
trap - EXIT
relancer
fin = $( date +%s%N)
rm -rf " ${ dest } "
mv " ${ tmp } " " ${ dest } "
echo " copie a froid de ${ source } : ${ service } $( ( ( actif ) ) && echo " arrete ( jusqu a la relance, rejeu du WAL non compris) $(( ( fin - debut) / 1000000 )) ms" || echo " n' etait pas actif")"