#!/bin/bash # Géré par Set-OPS (rôle client_backup). Ne pas éditer à la main. # # setops-copie-a-froid SERVICE SOURCE DESTINATION [--blocs-immuables] [--lier REL]... [--copier REL]... # # 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. # # `--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. set -euo pipefail service="${1:?service}" source="${2:?source}" dest="${3:?destination}" 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 [[ -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 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 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")"