Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 27faed361a | |||
| 8e89c6c7bc | |||
| a0c3fad69b | |||
| f09555f933 | |||
| c81f3c4d53 | |||
| db31f111d3 |
@@ -9,7 +9,7 @@ set -euo pipefail
|
|||||||
ulimit -n 65536
|
ulimit -n 65536
|
||||||
|
|
||||||
# Directory base dei backup
|
# Directory base dei backup
|
||||||
BACKUP_BASE=/var/backups/tscale01
|
BACKUP_BASE=/var/backups/tscale01/mariadb
|
||||||
|
|
||||||
# Timestamp per la directory del backup
|
# Timestamp per la directory del backup
|
||||||
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ set -euo pipefail
|
|||||||
ulimit -n 65536
|
ulimit -n 65536
|
||||||
|
|
||||||
# Directory base dei backup
|
# Directory base dei backup
|
||||||
BACKUP_BASE=/var/backups/tscale01
|
BACKUP_BASE=/var/backups/tscale01/mariadb
|
||||||
|
|
||||||
# Prefissi per full e incremental
|
# Prefissi per full e incremental
|
||||||
FULL_PREFIX=backup-full-
|
FULL_PREFIX=backup-full-
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
ulimit -n 65536
|
|
||||||
|
|
||||||
BACKUP_BASE=/var/backups/tscale01
|
|
||||||
PREFIX=backup-logic-
|
|
||||||
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
|
||||||
TARGET="$BACKUP_BASE/${PREFIX}${TIMESTAMP}"
|
|
||||||
DUMPFILE="$TARGET/all-dbs.sql"
|
|
||||||
LOGFILE=/var/log/mariadb-backup.log
|
|
||||||
LOCKFILE=/var/lock/mariadb-backup.lock
|
|
||||||
|
|
||||||
# mysqldump bin e opzioni (root senza password: non passiamo --defaults-file)
|
|
||||||
MYSQLDUMP_BIN=$(command -v mysqldump || true)
|
|
||||||
MYSQLDUMP_OPTS="--user=root --all-databases --single-transaction --quick --lock-tables=FALSE"
|
|
||||||
|
|
||||||
# assicurati che il logfile esista e abbia permessi restrittivi
|
|
||||||
touch "$LOGFILE"
|
|
||||||
chown root:root "$LOGFILE"
|
|
||||||
chmod 600 "$LOGFILE"
|
|
||||||
|
|
||||||
#verifico file lock per non sovrapporre backup
|
|
||||||
(
|
|
||||||
flock -n 9 || { echo "[$(date '+%F %T')] SKIP: another backup is running" >> "$LOGFILE"; exit 0; }
|
|
||||||
|
|
||||||
if [ -z "$MYSQLDUMP_BIN" ]; then
|
|
||||||
echo "[$(date '+%F %T')] ERROR: mysqldump not found in PATH" >> "$LOGFILE"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p "$TARGET"
|
|
||||||
chown mysql:mysql "$TARGET"
|
|
||||||
chmod 750 "$TARGET"
|
|
||||||
|
|
||||||
TMPLOG=$(mktemp /tmp/mariadb-backup-logic.XXXXXX)
|
|
||||||
|
|
||||||
echo "---------------------" >> "$LOGFILE"
|
|
||||||
echo "[$(date '+%F %T')] START logic backup $TARGET" >> "$LOGFILE"
|
|
||||||
|
|
||||||
# esegui il dump: stdout -> file, stderr -> TMPLOG
|
|
||||||
if $MYSQLDUMP_BIN $MYSQLDUMP_OPTS >"$DUMPFILE" 2>"$TMPLOG"; then
|
|
||||||
echo "[$(date '+%F %T')] END logic backup $TARGET" >> "$LOGFILE"
|
|
||||||
|
|
||||||
# misura dimensione apparente (byte logici)
|
|
||||||
size_apparent_bytes=$(find "$TARGET" -type f -printf '%s\n' 2>/dev/null | awk '{s+=$1} END{print s+0}')
|
|
||||||
size_apparent_human=$(numfmt --to=iec --suffix=B "$size_apparent_bytes" 2>/dev/null || echo "${size_apparent_bytes}B")
|
|
||||||
|
|
||||||
# misura spazio su disco effettivo (byte allocati)
|
|
||||||
size_disk_bytes=$(du -s --block-size=1 "$TARGET" 2>/dev/null | cut -f1 || echo 0)
|
|
||||||
size_disk_human=$(numfmt --to=iec --suffix=B "$size_disk_bytes" 2>/dev/null || echo "${size_disk_bytes}B")
|
|
||||||
|
|
||||||
echo "[$(date '+%F %T')] SIZE apparent: $size_apparent_human ($size_apparent_bytes bytes) for $TARGET" >> "$LOGFILE"
|
|
||||||
echo "[$(date '+%F %T')] SIZE on-disk: $size_disk_human ($size_disk_bytes bytes) for $TARGET" >> "$LOGFILE"
|
|
||||||
|
|
||||||
echo "[$(date '+%F %T')] RESULT: OK, no errors" >> "$LOGFILE"
|
|
||||||
|
|
||||||
rm -f "$TMPLOG"
|
|
||||||
else
|
|
||||||
echo "[$(date '+%F %T')] ERROR during logic backup for $TARGET. See $TMPLOG" >> "$LOGFILE"
|
|
||||||
tail -n 200 "$TMPLOG" >> "$LOGFILE"
|
|
||||||
rm -f "$TMPLOG"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# rotazione: mantieni 7 giorni per i logic (log dettagliato)
|
|
||||||
TO_DELETE=$(find "$BACKUP_BASE" -maxdepth 1 -type d -name "${PREFIX}*" -mtime +7 -print 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [ -n "$TO_DELETE" ]; then
|
|
||||||
echo "[$(date '+%F %T')] ROTATE: removing old logic backups:" >> "$LOGFILE"
|
|
||||||
echo "$TO_DELETE" >> "$LOGFILE"
|
|
||||||
echo "$TO_DELETE" | tr '\n' '\0' | xargs -0 -r rm -rf --
|
|
||||||
else
|
|
||||||
echo "[$(date '+%F %T')] ROTATE: no logic backups to remove (<=7 days)" >> "$LOGFILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo " " >> "$LOGFILE"
|
|
||||||
|
|
||||||
) 9>"$LOCKFILE"
|
|
||||||
@@ -1,163 +1,209 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Backup logico per-database con retry e cleanup automatico
|
# Backup logico per-database con retry globale e retry per-database
|
||||||
# Sicuro per cluster Galera ad alto carico
|
# Sicuro per cluster Galera ad alto carico
|
||||||
# Autore: Marco + Copilot
|
# Autore: Marco + Copilot
|
||||||
|
|
||||||
|
# Uscita immediata in caso di:
|
||||||
|
# - comando con exit code != 0 (set -e)
|
||||||
|
# - uso di variabile non definita (set -u)
|
||||||
|
# - errore in una pipe (set -o pipefail)
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Aumenta i file descriptor disponibili (mysqldump può aprirne molti)
|
# Aumenta i file descriptor disponibili (mariadb-dump può aprirne molti)
|
||||||
ulimit -n 65536
|
ulimit -n 65536
|
||||||
|
|
||||||
# Directory base dei backup
|
# Directory base dei backup logici
|
||||||
BACKUP_BASE=/var/backups/tscale01
|
BACKUP_BASE=/var/backups/tscale01/mariadb
|
||||||
|
|
||||||
# Prefisso per distinguere i backup logici di questo nodo
|
# Prefisso per distinguere i backup logici
|
||||||
PREFIX=eqn-bck-logic-
|
PREFIX=backup-logic-
|
||||||
|
|
||||||
# Timestamp per la directory del backup
|
# File di log condiviso
|
||||||
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
|
||||||
|
|
||||||
# Directory finale del backup
|
|
||||||
TARGET="$BACKUP_BASE/${PREFIX}${TIMESTAMP}"
|
|
||||||
|
|
||||||
# File di log generale
|
|
||||||
LOGFILE=/var/log/mariadb-backup.log
|
LOGFILE=/var/log/mariadb-backup.log
|
||||||
|
|
||||||
# Lockfile per evitare esecuzioni concorrenti
|
# Lockfile per evitare esecuzioni concorrenti
|
||||||
LOCKFILE=/var/lock/mariadb-backup.lock
|
LOCKFILE=/var/lock/mariadb-backup.lock
|
||||||
|
|
||||||
# Nome del server per i log (utile in ambienti multi-nodo)
|
# Nome breve del server per i log
|
||||||
SERVER_NAME=$(hostname -s)
|
SERVER_NAME=$(hostname -s)
|
||||||
|
|
||||||
# Binari necessari
|
# Numero massimo di tentativi globali dell’intero backup
|
||||||
|
GLOBAL_MAX_RETRIES=5
|
||||||
|
|
||||||
|
# Attesa tra un tentativo globale e il successivo
|
||||||
|
GLOBAL_RETRY_SLEEP=30
|
||||||
|
|
||||||
|
# Numero massimo di tentativi per ogni database
|
||||||
|
DB_MAX_RETRIES=10
|
||||||
|
|
||||||
|
# Attesa tra un tentativo e l’altro per lo stesso database
|
||||||
|
DB_RETRY_SLEEP=20
|
||||||
|
|
||||||
|
# Percorso del client mariadb
|
||||||
MYSQL_BIN=$(command -v mariadb || true)
|
MYSQL_BIN=$(command -v mariadb || true)
|
||||||
|
|
||||||
|
# Percorso di mariadb-dump
|
||||||
MYSQLDUMP_BIN=$(command -v mariadb-dump || true)
|
MYSQLDUMP_BIN=$(command -v mariadb-dump || true)
|
||||||
|
|
||||||
# Opzioni dump ottimizzate per Galera (no lock, snapshot consistente)
|
# Opzioni ottimizzate per Galera
|
||||||
MYSQLDUMP_OPTS="--user=root --single-transaction --quick --skip-lock-tables"
|
MYSQLDUMP_OPTS="--user=root --single-transaction --quick --skip-lock-tables"
|
||||||
|
|
||||||
# Numero massimo di retry per ogni database (deadlock = retry)
|
|
||||||
MAX_RETRIES=10
|
|
||||||
|
|
||||||
# Secondi di attesa tra un retry e l'altro
|
|
||||||
RETRY_SLEEP=30
|
|
||||||
|
|
||||||
# Assicura che il logfile esista e abbia permessi sicuri
|
# Assicura che il logfile esista e abbia permessi sicuri
|
||||||
touch "$LOGFILE"
|
touch "$LOGFILE"
|
||||||
chown root:root "$LOGFILE"
|
|
||||||
chmod 600 "$LOGFILE"
|
chmod 600 "$LOGFILE"
|
||||||
|
|
||||||
(
|
###############################################################################
|
||||||
# FLOCK: evita che due backup partano insieme
|
# FUNZIONE PRINCIPALE: esegue un singolo tentativo completo di backup logico
|
||||||
flock -n 9 || { echo "[$(date '+%F %T')] [$SERVER_NAME] SKIP: another backup is running" >> "$LOGFILE"; exit 0; }
|
###############################################################################
|
||||||
|
run_backup() {
|
||||||
|
|
||||||
# Controllo binari
|
# Timestamp per la directory del backup
|
||||||
if [ -z "$MYSQL_BIN" ]; then
|
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] ERROR: mariadb client not found in PATH" >> "$LOGFILE"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$MYSQLDUMP_BIN" ]; then
|
# Directory di destinazione del backup
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] ERROR: mariadb-dump not found in PATH" >> "$LOGFILE"
|
TARGET="$BACKUP_BASE/${PREFIX}${TIMESTAMP}"
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Crea directory del backup
|
# File temporaneo per catturare errori di mariadb-dump
|
||||||
|
TMPLOG=$(mktemp /tmp/mariadb-backup-logic.XXXXXX)
|
||||||
|
|
||||||
|
# Crea la directory del backup con permessi sicuri
|
||||||
mkdir -p "$TARGET"
|
mkdir -p "$TARGET"
|
||||||
chown mysql:mysql "$TARGET"
|
chown mysql:mysql "$TARGET"
|
||||||
chmod 750 "$TARGET"
|
chmod 750 "$TARGET"
|
||||||
|
|
||||||
# File temporaneo per catturare errori del dump
|
# Log di inizio backup
|
||||||
TMPLOG=$(mktemp /tmp/mariadb-backup-logic.XXXXXX)
|
echo "[$(date '+%F %T')] [$SERVER_NAME] START logic backup $TARGET" >> "$LOGFILE"
|
||||||
|
|
||||||
echo "---------------------" >> "$LOGFILE"
|
# Elenco dei database utente (escludiamo schemi di sistema)
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] START logic backup (per-database) $TARGET" >> "$LOGFILE"
|
|
||||||
|
|
||||||
# Elenco database utente (escludiamo schemi di sistema)
|
|
||||||
DB_LIST=$($MYSQL_BIN -N -e "SHOW DATABASES" | grep -vE '^(information_schema|performance_schema|mysql|sys)$' || true)
|
DB_LIST=$($MYSQL_BIN -N -e "SHOW DATABASES" | grep -vE '^(information_schema|performance_schema|mysql|sys)$' || true)
|
||||||
|
|
||||||
if [ -z "$DB_LIST" ]; then
|
# Flag di fallimento globale
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] WARNING: no user databases found to dump" >> "$LOGFILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
FAILED=0
|
FAILED=0
|
||||||
FAILED_DBS=""
|
FAILED_DBS=""
|
||||||
|
|
||||||
# Loop su ogni database
|
# Ciclo su ogni database
|
||||||
for db in $DB_LIST; do
|
for DB in $DB_LIST; do
|
||||||
DUMPFILE="$TARGET/${db}.sql"
|
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] START dump database '$db' -> $DUMPFILE" >> "$LOGFILE"
|
|
||||||
|
|
||||||
attempt=1
|
# File di destinazione del dump
|
||||||
success=0
|
DUMPFILE="$TARGET/${DB}.sql"
|
||||||
|
|
||||||
# Retry intelligente in caso di deadlock (errore 1213)
|
# Log di inizio dump
|
||||||
while [ "$attempt" -le "$MAX_RETRIES" ]; do
|
echo "[$(date '+%F %T')] [$SERVER_NAME] START dump database '$DB' -> $DUMPFILE" >> "$LOGFILE"
|
||||||
: >"$TMPLOG" # pulisce il file temporaneo
|
|
||||||
|
|
||||||
if $MYSQLDUMP_BIN $MYSQLDUMP_OPTS "$db" >"$DUMPFILE" 2>"$TMPLOG"; then
|
# Tentativi per questo database
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] END dump database '$db' (attempt $attempt)" >> "$LOGFILE"
|
ATTEMPT=1
|
||||||
success=1
|
SUCCESS=0
|
||||||
|
|
||||||
|
# Ciclo di retry per il singolo database
|
||||||
|
while [ "$ATTEMPT" -le "$DB_MAX_RETRIES" ]; do
|
||||||
|
|
||||||
|
# Pulisce il file temporaneo
|
||||||
|
: >"$TMPLOG"
|
||||||
|
|
||||||
|
# Esegue il dump
|
||||||
|
if $MYSQLDUMP_BIN $MYSQLDUMP_OPTS "$DB" >"$DUMPFILE" 2>"$TMPLOG"; then
|
||||||
|
# Dump riuscito
|
||||||
|
echo "[$(date '+%F %T')] [$SERVER_NAME] END dump database '$DB' (attempt $ATTEMPT)" >> "$LOGFILE"
|
||||||
|
SUCCESS=1
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] ERROR dumping database '$db' (attempt $attempt). See $TMPLOG" >> "$LOGFILE"
|
# Dump fallito
|
||||||
|
echo "[$(date '+%F %T')] [$SERVER_NAME] ERROR dumping database '$DB' (attempt $ATTEMPT). See $TMPLOG" >> "$LOGFILE"
|
||||||
tail -n 50 "$TMPLOG" >> "$LOGFILE"
|
tail -n 50 "$TMPLOG" >> "$LOGFILE"
|
||||||
|
|
||||||
# Se è un deadlock, riproviamo
|
# Deadlock 1213 → retry
|
||||||
if grep -q "Error 1213" "$TMPLOG"; then
|
if grep -q "Error 1213" "$TMPLOG"; then
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] RETRY for database '$db' in ${RETRY_SLEEP}s due to deadlock (1213)" >> "$LOGFILE"
|
echo "[$(date '+%F %T')] [$SERVER_NAME] RETRY for '$DB' in ${DB_RETRY_SLEEP}s due to deadlock (1213)" >> "$LOGFILE"
|
||||||
sleep "$RETRY_SLEEP"
|
sleep "$DB_RETRY_SLEEP"
|
||||||
else
|
else
|
||||||
# Errori non recuperabili → stop retry
|
# Errore non recuperabile → interrompe retry
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] NON-RETRYABLE error for database '$db'" >> "$LOGFILE"
|
echo "[$(date '+%F %T')] [$SERVER_NAME] NON-RETRYABLE error for '$DB'" >> "$LOGFILE"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
attempt=$((attempt + 1))
|
# Incrementa tentativo
|
||||||
|
ATTEMPT=$((ATTEMPT + 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
# Se dopo i retry non è andata → segna come fallito
|
# Se il database non è stato dumpato correttamente
|
||||||
if [ "$success" -ne 1 ]; then
|
if [ "$SUCCESS" -ne 1 ]; then
|
||||||
FAILED=1
|
FAILED=1
|
||||||
FAILED_DBS="$FAILED_DBS $db"
|
FAILED_DBS="$FAILED_DBS $DB"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Calcolo dimensioni del backup
|
# Calcolo dimensioni apparenti e su disco
|
||||||
size_apparent_bytes=$(find "$TARGET" -type f -printf '%s\n' 2>/dev/null | awk '{s+=$1} END{print s+0}')
|
SIZE_APPARENT_BYTES=$(find "$TARGET" -type f -printf '%s\n' | awk '{s+=$1} END{print s+0}')
|
||||||
size_apparent_human=$(numfmt --to=iec --suffix=B "$size_apparent_bytes" 2>/dev/null || echo "${size_apparent_bytes}B")
|
SIZE_APPARENT_HUMAN=$(numfmt --to=iec --suffix=B "$SIZE_APPARENT_BYTES")
|
||||||
|
|
||||||
size_disk_bytes=$(du -s --block-size=1 "$TARGET" 2>/dev/null | cut -f1 || echo 0)
|
SIZE_DISK_BYTES=$(du -s --block-size=1 "$TARGET" | cut -f1)
|
||||||
size_disk_human=$(numfmt --to=iec --suffix=B "$size_disk_bytes" 2>/dev/null || echo "${size_disk_bytes}B")
|
SIZE_DISK_HUMAN=$(numfmt --to=iec --suffix=B "$SIZE_DISK_BYTES")
|
||||||
|
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] SIZE apparent: $size_apparent_human ($size_apparent_bytes bytes) for $TARGET" >> "$LOGFILE"
|
# Log delle dimensioni
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] SIZE on-disk: $size_disk_human ($size_disk_bytes bytes) for $TARGET" >> "$LOGFILE"
|
echo "[$(date '+%F %T')] [$SERVER_NAME] SIZE apparent: $SIZE_APPARENT_HUMAN ($SIZE_APPARENT_BYTES bytes)" >> "$LOGFILE"
|
||||||
|
echo "[$(date '+%F %T')] [$SERVER_NAME] SIZE on-disk: $SIZE_DISK_HUMAN ($SIZE_DISK_BYTES bytes)" >> "$LOGFILE"
|
||||||
|
|
||||||
# Risultato finale con cleanup in caso di fallimento
|
# Se tutto è andato bene
|
||||||
if [ "$FAILED" -eq 0 ]; then
|
if [ "$FAILED" -eq 0 ]; then
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] RESULT: OK, all databases dumped successfully" >> "$LOGFILE"
|
echo "[$(date '+%F %T')] [$SERVER_NAME] RESULT: OK" >> "$LOGFILE"
|
||||||
|
rm -f "$TMPLOG"
|
||||||
|
return 0
|
||||||
else
|
else
|
||||||
|
# Fallimento → pulizia directory
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] RESULT: FAILED, databases with errors:$FAILED_DBS" >> "$LOGFILE"
|
echo "[$(date '+%F %T')] [$SERVER_NAME] RESULT: FAILED, databases with errors:$FAILED_DBS" >> "$LOGFILE"
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] CLEANUP: removing incomplete backup directory $TARGET" >> "$LOGFILE"
|
echo "[$(date '+%F %T')] [$SERVER_NAME] CLEANUP: removing incomplete backup directory $TARGET" >> "$LOGFILE"
|
||||||
rm -rf "$TARGET"
|
rm -rf "$TARGET"
|
||||||
rm -f "$TMPLOG"
|
rm -f "$TMPLOG"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# BLOCCO PRINCIPALE CON FLOCK E RETRY GLOBALE
|
||||||
|
###############################################################################
|
||||||
|
(
|
||||||
|
# Evita esecuzioni concorrenti
|
||||||
|
flock -n 9 || {
|
||||||
|
echo "[$(date '+%F %T')] [$SERVER_NAME] SKIP: another backup is running" >> "$LOGFILE"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ciclo di retry globale
|
||||||
|
for GLOBAL_ATTEMPT in $(seq 1 $GLOBAL_MAX_RETRIES); do
|
||||||
|
|
||||||
|
# Separatore visivo per ogni tentativo globale
|
||||||
|
echo "" >> "$LOGFILE"
|
||||||
|
echo "---------------------" >> "$LOGFILE"
|
||||||
|
|
||||||
|
# Log del tentativo globale
|
||||||
|
echo "[$(date '+%F %T')] [$SERVER_NAME] GLOBAL attempt $GLOBAL_ATTEMPT/$GLOBAL_MAX_RETRIES" >> "$LOGFILE"
|
||||||
|
|
||||||
|
# Esegue un tentativo completo
|
||||||
|
if run_backup; then
|
||||||
|
echo "[$(date '+%F %T')] [$SERVER_NAME] GLOBAL RESULT: SUCCESS" >> "$LOGFILE"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
# Se non è l’ultimo tentativo → retry globale
|
||||||
|
if [ "$GLOBAL_ATTEMPT" -lt "$GLOBAL_MAX_RETRIES" ]; then
|
||||||
|
echo "[$(date '+%F %T')] [$SERVER_NAME] GLOBAL RETRY in ${GLOBAL_RETRY_SLEEP}s" >> "$LOGFILE"
|
||||||
|
sleep "$GLOBAL_RETRY_SLEEP"
|
||||||
|
else
|
||||||
|
# Fallimento definitivo
|
||||||
|
echo "[$(date '+%F %T')] [$SERVER_NAME] GLOBAL RESULT: FAILED after $GLOBAL_MAX_RETRIES attempts" >> "$LOGFILE"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
rm -f "$TMPLOG"
|
# Rotazione: elimina backup logici più vecchi di 7 giorni
|
||||||
|
TO_DELETE=$(find "$BACKUP_BASE" -maxdepth 1 -type d -name "${PREFIX}*" -mtime +7)
|
||||||
# Rotazione: elimina backup più vecchi di 7 giorni
|
|
||||||
TO_DELETE=$(find "$BACKUP_BASE" -maxdepth 1 -type d -name "${PREFIX}*" -mtime +7 -print 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [ -n "$TO_DELETE" ]; then
|
if [ -n "$TO_DELETE" ]; then
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] ROTATE: removing old logic backups:" >> "$LOGFILE"
|
echo "[$(date '+%F %T')] [$SERVER_NAME] ROTATE: removing old logic backups:" >> "$LOGFILE"
|
||||||
echo "$TO_DELETE" >> "$LOGFILE"
|
|
||||||
echo "$TO_DELETE" | tr '\n' '\0' | xargs -0 -r rm -rf --
|
echo "$TO_DELETE" | tr '\n' '\0' | xargs -0 -r rm -rf --
|
||||||
else
|
else
|
||||||
echo "[$(date '+%F %T')] [$SERVER_NAME] ROTATE: no logic backups to remove (<=7 days)" >> "$LOGFILE"
|
echo "[$(date '+%F %T')] [$SERVER_NAME] ROTATE: no logic backups to remove (<=7 days)" >> "$LOGFILE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo " " >> "$LOGFILE"
|
|
||||||
|
|
||||||
) 9>"$LOCKFILE"
|
) 9>"$LOCKFILE"
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SRC="/opt/redmine/redmine_files"
|
||||||
|
BASE="/var/backups/tscale01/redmine_files"
|
||||||
|
PREFIX="redmine_files"
|
||||||
|
KEEP=3
|
||||||
|
|
||||||
|
LOG="/var/log/backup-redmine-files.log"
|
||||||
|
LOCK="/var/lock/redmine-files-backup.lock"
|
||||||
|
|
||||||
|
timestamp="$(date '+%Y%m%d_%H%M%S')"
|
||||||
|
DEST="${BASE}/${PREFIX}-${timestamp}"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
printf "%s %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$1" | tee -a "$LOG"
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -f "$LOCK"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# Lock semplice
|
||||||
|
if ! ( set -o noclobber; echo "$$" > "$LOCK" ) 2>/dev/null; then
|
||||||
|
log "ERRORE: un'altra istanza è in esecuzione."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verifiche base
|
||||||
|
[ -d "$SRC" ] || { log "ERRORE: sorgente $SRC inesistente"; exit 1; }
|
||||||
|
mkdir -p "$BASE"
|
||||||
|
mkdir -p "$DEST"
|
||||||
|
|
||||||
|
log "Inizio snapshot → $DEST"
|
||||||
|
|
||||||
|
rsync -a --delete "$SRC"/ "$DEST"/
|
||||||
|
|
||||||
|
log "Snapshot completato."
|
||||||
|
|
||||||
|
# Retention per numero: tieni gli ultimi $KEEP, cancella il resto
|
||||||
|
log "Avvio retention: mantengo ultimi $KEEP snapshot."
|
||||||
|
|
||||||
|
# Lista directory in ordine dal più recente al più vecchio
|
||||||
|
snapshots=( $(ls -1dt "${BASE}/${PREFIX}-"* 2>/dev/null || true) )
|
||||||
|
|
||||||
|
if [ "${#snapshots[@]}" -gt "$KEEP" ]; then
|
||||||
|
to_delete=( "${snapshots[@]:$KEEP}" )
|
||||||
|
for dir in "${to_delete[@]}"; do
|
||||||
|
log "Elimino snapshot vecchio: $dir"
|
||||||
|
rm -rf -- "$dir"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Retention completata."
|
||||||
|
exit 0
|
||||||
Reference in New Issue
Block a user