From af8ca83425e4a9952b909553e49cd3abb80b1337 Mon Sep 17 00:00:00 2001 From: "marco.locatelli@steamware.net" Date: Thu, 19 Mar 2026 15:50:04 +0100 Subject: [PATCH 1/6] commenti --- mariadb-backup/mariadb-backup-full.sh | 1 + mariadb-backup/mariadb-backup-incremental.sh | 2 ++ mariadb-backup/mariadb-backup-logic.sh | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mariadb-backup/mariadb-backup-full.sh b/mariadb-backup/mariadb-backup-full.sh index 25b9326..0148c80 100644 --- a/mariadb-backup/mariadb-backup-full.sh +++ b/mariadb-backup/mariadb-backup-full.sh @@ -14,6 +14,7 @@ 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; } diff --git a/mariadb-backup/mariadb-backup-incremental.sh b/mariadb-backup/mariadb-backup-incremental.sh index 617736e..332be54 100644 --- a/mariadb-backup/mariadb-backup-incremental.sh +++ b/mariadb-backup/mariadb-backup-incremental.sh @@ -11,10 +11,12 @@ TARGET="$BACKUP_BASE/${INC_PREFIX}${TIMESTAMP}" LOGFILE=/var/log/mariadb-backup.log LOCKFILE=/var/lock/mariadb-backup.lock +# 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; } diff --git a/mariadb-backup/mariadb-backup-logic.sh b/mariadb-backup/mariadb-backup-logic.sh index 35e301e..b4e699f 100644 --- a/mariadb-backup/mariadb-backup-logic.sh +++ b/mariadb-backup/mariadb-backup-logic.sh @@ -8,7 +8,6 @@ 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 @@ -21,6 +20,7 @@ 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; } From 8b9f44cf89e051ad24f282438ca8785e2e1553a7 Mon Sep 17 00:00:00 2001 From: "marco.locatelli@steamware.net" Date: Thu, 26 Mar 2026 10:09:31 +0100 Subject: [PATCH 2/6] cambio base directory --- mariadb-backup/mariadb-backup-full.sh | 2 +- mariadb-backup/mariadb-backup-incremental.sh | 2 +- mariadb-backup/mariadb-backup-logic.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mariadb-backup/mariadb-backup-full.sh b/mariadb-backup/mariadb-backup-full.sh index 0148c80..1e46737 100644 --- a/mariadb-backup/mariadb-backup-full.sh +++ b/mariadb-backup/mariadb-backup-full.sh @@ -3,7 +3,7 @@ set -euo pipefail ulimit -n 65536 -BACKUP_BASE=/var/lib/mariadb-backup +BACKUP_BASE=/var/backups/nfs TIMESTAMP=$(date +%Y%m%d-%H%M%S) TARGET="$BACKUP_BASE/backup-full-$TIMESTAMP" LOGFILE=/var/log/mariadb-backup.log diff --git a/mariadb-backup/mariadb-backup-incremental.sh b/mariadb-backup/mariadb-backup-incremental.sh index 332be54..5afd8f6 100644 --- a/mariadb-backup/mariadb-backup-incremental.sh +++ b/mariadb-backup/mariadb-backup-incremental.sh @@ -3,7 +3,7 @@ set -euo pipefail ulimit -n 65536 -BACKUP_BASE=/var/lib/mariadb-backup +BACKUP_BASE=/var/backups/nfs FULL_PREFIX=backup-full- INC_PREFIX=backup-inc- TIMESTAMP=$(date +%Y%m%d-%H%M%S) diff --git a/mariadb-backup/mariadb-backup-logic.sh b/mariadb-backup/mariadb-backup-logic.sh index b4e699f..4403ded 100644 --- a/mariadb-backup/mariadb-backup-logic.sh +++ b/mariadb-backup/mariadb-backup-logic.sh @@ -3,7 +3,7 @@ set -euo pipefail ulimit -n 65536 -BACKUP_BASE=/var/lib/mariadb-backup +BACKUP_BASE=/var/backups/nfs PREFIX=backup-logic- TIMESTAMP=$(date +%Y%m%d-%H%M%S) TARGET="$BACKUP_BASE/${PREFIX}${TIMESTAMP}" From fe1820db7b042dab2baeddb178678fc34de34816 Mon Sep 17 00:00:00 2001 From: "marco.locatelli@steamware.net" Date: Fri, 27 Mar 2026 13:08:24 +0100 Subject: [PATCH 3/6] aggiunta backup logic per database --- mariadb-backup/mariadb-backup-full.sh | 2 +- mariadb-backup/mariadb-backup-incremental.sh | 2 +- .../mariadb-backup-logic-per-database.sh | 104 ++++++++++++++++++ mariadb-backup/mariadb-backup-logic.sh | 2 +- 4 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 mariadb-backup/mariadb-backup-logic-per-database.sh diff --git a/mariadb-backup/mariadb-backup-full.sh b/mariadb-backup/mariadb-backup-full.sh index 1e46737..176dd41 100644 --- a/mariadb-backup/mariadb-backup-full.sh +++ b/mariadb-backup/mariadb-backup-full.sh @@ -3,7 +3,7 @@ set -euo pipefail ulimit -n 65536 -BACKUP_BASE=/var/backups/nfs +BACKUP_BASE=/var/backups/tscale01 TIMESTAMP=$(date +%Y%m%d-%H%M%S) TARGET="$BACKUP_BASE/backup-full-$TIMESTAMP" LOGFILE=/var/log/mariadb-backup.log diff --git a/mariadb-backup/mariadb-backup-incremental.sh b/mariadb-backup/mariadb-backup-incremental.sh index 5afd8f6..f434221 100644 --- a/mariadb-backup/mariadb-backup-incremental.sh +++ b/mariadb-backup/mariadb-backup-incremental.sh @@ -3,7 +3,7 @@ set -euo pipefail ulimit -n 65536 -BACKUP_BASE=/var/backups/nfs +BACKUP_BASE=/var/backups/tscale01 FULL_PREFIX=backup-full- INC_PREFIX=backup-inc- TIMESTAMP=$(date +%Y%m%d-%H%M%S) diff --git a/mariadb-backup/mariadb-backup-logic-per-database.sh b/mariadb-backup/mariadb-backup-logic-per-database.sh new file mode 100644 index 0000000..223e488 --- /dev/null +++ b/mariadb-backup/mariadb-backup-logic-per-database.sh @@ -0,0 +1,104 @@ +#!/bin/bash +set -euo pipefail + +ulimit -n 65536 + +BACKUP_BASE=/var/backups/tscale01 +PREFIX=eqn-bck-logic- +TIMESTAMP=$(date +%Y%m%d-%H%M%S) +TARGET="$BACKUP_BASE/${PREFIX}${TIMESTAMP}" + +LOGFILE=/var/log/mariadb-backup.log +LOCKFILE=/var/lock/mariadb-backup.lock + +SERVER_NAME=$(hostname -s) + +# binari e opzioni (root senza password) +MYSQL_BIN=$(command -v mariadb || true) +MYSQLDUMP_BIN=$(command -v mariadb-dump || true) +MYSQLDUMP_OPTS="--user=root --single-transaction --quick --skip-lock-tables" + +# logfile +touch "$LOGFILE" +chown root:root "$LOGFILE" +chmod 600 "$LOGFILE" + +( + flock -n 9 || { echo "[$(date '+%F %T')] [$SERVER_NAME] SKIP: another backup is running" >> "$LOGFILE"; exit 0; } + + if [ -z "$MYSQL_BIN" ]; then + echo "[$(date '+%F %T')] [$SERVER_NAME] ERROR: mariadb client not found in PATH" >> "$LOGFILE" + exit 1 + fi + + if [ -z "$MYSQLDUMP_BIN" ]; then + echo "[$(date '+%F %T')] [$SERVER_NAME] ERROR: mariadb-dump 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')] [$SERVER_NAME] START logic backup (per-database) $TARGET" >> "$LOGFILE" + + # elenco database (escludo schemi di sistema) + DB_LIST=$($MYSQL_BIN -N -e "SHOW DATABASES" | grep -vE '^(information_schema|performance_schema|mysql|sys)$' || true) + + if [ -z "$DB_LIST" ]; then + echo "[$(date '+%F %T')] [$SERVER_NAME] WARNING: no user databases found to dump" >> "$LOGFILE" + fi + + FAILED=0 + FAILED_DBS="" + + for db in $DB_LIST; do + DUMPFILE="$TARGET/${db}.sql" + echo "[$(date '+%F %T')] [$SERVER_NAME] START dump database '$db' -> $DUMPFILE" >> "$LOGFILE" + + if $MYSQLDUMP_BIN $MYSQLDUMP_OPTS "$db" >"$DUMPFILE" 2>"$TMPLOG"; then + echo "[$(date '+%F %T')] [$SERVER_NAME] END dump database '$db'" >> "$LOGFILE" + else + echo "[$(date '+%F %T')] [$SERVER_NAME] ERROR dumping database '$db'. See $TMPLOG" >> "$LOGFILE" + tail -n 100 "$TMPLOG" >> "$LOGFILE" + FAILED=1 + FAILED_DBS="$FAILED_DBS $db" + # continuo con i successivi + fi + done + + # misura dimensioni complessive + 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") + + 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')] [$SERVER_NAME] SIZE apparent: $size_apparent_human ($size_apparent_bytes bytes) for $TARGET" >> "$LOGFILE" + echo "[$(date '+%F %T')] [$SERVER_NAME] SIZE on-disk: $size_disk_human ($size_disk_bytes bytes) for $TARGET" >> "$LOGFILE" + + if [ "$FAILED" -eq 0 ]; then + echo "[$(date '+%F %T')] [$SERVER_NAME] RESULT: OK, all databases dumped successfully" >> "$LOGFILE" + else + echo "[$(date '+%F %T')] [$SERVER_NAME] RESULT: PARTIAL, failed databases:$FAILED_DBS" >> "$LOGFILE" + fi + + rm -f "$TMPLOG" + + # rotazione: mantieni 7 giorni per i logic + 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')] [$SERVER_NAME] 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')] [$SERVER_NAME] ROTATE: no logic backups to remove (<=7 days)" >> "$LOGFILE" + fi + + echo " " >> "$LOGFILE" + +) 9>"$LOCKFILE" diff --git a/mariadb-backup/mariadb-backup-logic.sh b/mariadb-backup/mariadb-backup-logic.sh index 4403ded..6a2d06a 100644 --- a/mariadb-backup/mariadb-backup-logic.sh +++ b/mariadb-backup/mariadb-backup-logic.sh @@ -3,7 +3,7 @@ set -euo pipefail ulimit -n 65536 -BACKUP_BASE=/var/backups/nfs +BACKUP_BASE=/var/backups/tscale01 PREFIX=backup-logic- TIMESTAMP=$(date +%Y%m%d-%H%M%S) TARGET="$BACKUP_BASE/${PREFIX}${TIMESTAMP}" From 4bf188eecedec39064e3b1d47c32b000e842abfd Mon Sep 17 00:00:00 2001 From: "marco.locatelli@steamware.net" Date: Fri, 27 Mar 2026 13:26:31 +0100 Subject: [PATCH 4/6] aggiunta commenti backup logic --- .../mariadb-backup-logic-per-database.sh | 77 ++++++++++++++++--- 1 file changed, 66 insertions(+), 11 deletions(-) diff --git a/mariadb-backup/mariadb-backup-logic-per-database.sh b/mariadb-backup/mariadb-backup-logic-per-database.sh index 223e488..956bd8f 100644 --- a/mariadb-backup/mariadb-backup-logic-per-database.sh +++ b/mariadb-backup/mariadb-backup-logic-per-database.sh @@ -1,31 +1,57 @@ #!/bin/bash +# Backup logico per-database con retry, sicuro per cluster Galera +# Autore: Marco + Copilot +# Obiettivo: dump affidabile anche su nodi molto attivi + set -euo pipefail +# Aumenta i file descriptor disponibili (mysqldump può aprirne molti) ulimit -n 65536 +# Directory base dei backup BACKUP_BASE=/var/backups/tscale01 + +# Prefisso per distinguere i backup logici di questo nodo PREFIX=eqn-bck-logic- + +# Timestamp per la directory del backup 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 + +# Lockfile per evitare esecuzioni concorrenti LOCKFILE=/var/lock/mariadb-backup.lock +# Nome del server per i log (utile in ambienti multi-nodo) SERVER_NAME=$(hostname -s) -# binari e opzioni (root senza password) +# Binari necessari MYSQL_BIN=$(command -v mariadb || true) MYSQLDUMP_BIN=$(command -v mariadb-dump || true) + +# Opzioni dump ottimizzate per Galera (no lock, snapshot consistente) MYSQLDUMP_OPTS="--user=root --single-transaction --quick --skip-lock-tables" -# logfile +# 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 touch "$LOGFILE" chown root:root "$LOGFILE" chmod 600 "$LOGFILE" ( + # FLOCK: evita che due backup partano insieme flock -n 9 || { echo "[$(date '+%F %T')] [$SERVER_NAME] SKIP: another backup is running" >> "$LOGFILE"; exit 0; } + # Controllo binari if [ -z "$MYSQL_BIN" ]; then echo "[$(date '+%F %T')] [$SERVER_NAME] ERROR: mariadb client not found in PATH" >> "$LOGFILE" exit 1 @@ -36,16 +62,18 @@ chmod 600 "$LOGFILE" exit 1 fi + # Crea directory del backup mkdir -p "$TARGET" chown mysql:mysql "$TARGET" chmod 750 "$TARGET" + # File temporaneo per catturare errori del dump TMPLOG=$(mktemp /tmp/mariadb-backup-logic.XXXXXX) echo "---------------------" >> "$LOGFILE" echo "[$(date '+%F %T')] [$SERVER_NAME] START logic backup (per-database) $TARGET" >> "$LOGFILE" - # elenco database (escludo schemi di sistema) + # Elenco database utente (escludiamo schemi di sistema) DB_LIST=$($MYSQL_BIN -N -e "SHOW DATABASES" | grep -vE '^(information_schema|performance_schema|mysql|sys)$' || true) if [ -z "$DB_LIST" ]; then @@ -55,22 +83,48 @@ chmod 600 "$LOGFILE" FAILED=0 FAILED_DBS="" + # Loop su ogni database for db in $DB_LIST; do DUMPFILE="$TARGET/${db}.sql" echo "[$(date '+%F %T')] [$SERVER_NAME] START dump database '$db' -> $DUMPFILE" >> "$LOGFILE" - if $MYSQLDUMP_BIN $MYSQLDUMP_OPTS "$db" >"$DUMPFILE" 2>"$TMPLOG"; then - echo "[$(date '+%F %T')] [$SERVER_NAME] END dump database '$db'" >> "$LOGFILE" - else - echo "[$(date '+%F %T')] [$SERVER_NAME] ERROR dumping database '$db'. See $TMPLOG" >> "$LOGFILE" - tail -n 100 "$TMPLOG" >> "$LOGFILE" + attempt=1 + success=0 + + # Retry intelligente in caso di deadlock (errore 1213) + while [ "$attempt" -le "$MAX_RETRIES" ]; do + : >"$TMPLOG" # pulisce il file temporaneo + + if $MYSQLDUMP_BIN $MYSQLDUMP_OPTS "$db" >"$DUMPFILE" 2>"$TMPLOG"; then + echo "[$(date '+%F %T')] [$SERVER_NAME] END dump database '$db' (attempt $attempt)" >> "$LOGFILE" + success=1 + break + else + echo "[$(date '+%F %T')] [$SERVER_NAME] ERROR dumping database '$db' (attempt $attempt). See $TMPLOG" >> "$LOGFILE" + tail -n 50 "$TMPLOG" >> "$LOGFILE" + + # Se è un deadlock, riproviamo + 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" + sleep "$RETRY_SLEEP" + else + # Errori non recuperabili → stop retry + echo "[$(date '+%F %T')] [$SERVER_NAME] NON-RETRYABLE error for database '$db'" >> "$LOGFILE" + break + fi + fi + + attempt=$((attempt + 1)) + done + + # Se dopo i retry non è andata → segna come fallito + if [ "$success" -ne 1 ]; then FAILED=1 FAILED_DBS="$FAILED_DBS $db" - # continuo con i successivi fi done - # misura dimensioni complessive + # Calcolo dimensioni del backup 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") @@ -80,6 +134,7 @@ chmod 600 "$LOGFILE" echo "[$(date '+%F %T')] [$SERVER_NAME] SIZE apparent: $size_apparent_human ($size_apparent_bytes bytes) for $TARGET" >> "$LOGFILE" echo "[$(date '+%F %T')] [$SERVER_NAME] SIZE on-disk: $size_disk_human ($size_disk_bytes bytes) for $TARGET" >> "$LOGFILE" + # Risultato finale if [ "$FAILED" -eq 0 ]; then echo "[$(date '+%F %T')] [$SERVER_NAME] RESULT: OK, all databases dumped successfully" >> "$LOGFILE" else @@ -88,7 +143,7 @@ chmod 600 "$LOGFILE" rm -f "$TMPLOG" - # rotazione: mantieni 7 giorni per i logic + # 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 From 0e7c5139ffba316f1179af67c69314e647d2d3db Mon Sep 17 00:00:00 2001 From: "marco.locatelli@steamware.net" Date: Mon, 30 Mar 2026 10:21:06 +0200 Subject: [PATCH 5/6] Gestione backup falliti e cleanup --- ...b-backup-logic.sh => mariadb-backup-logic-old.sh} | 0 mariadb-backup/mariadb-backup-logic-per-database.sh | 12 ++++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) rename mariadb-backup/{mariadb-backup-logic.sh => mariadb-backup-logic-old.sh} (100%) diff --git a/mariadb-backup/mariadb-backup-logic.sh b/mariadb-backup/mariadb-backup-logic-old.sh similarity index 100% rename from mariadb-backup/mariadb-backup-logic.sh rename to mariadb-backup/mariadb-backup-logic-old.sh diff --git a/mariadb-backup/mariadb-backup-logic-per-database.sh b/mariadb-backup/mariadb-backup-logic-per-database.sh index 956bd8f..660e014 100644 --- a/mariadb-backup/mariadb-backup-logic-per-database.sh +++ b/mariadb-backup/mariadb-backup-logic-per-database.sh @@ -1,7 +1,7 @@ #!/bin/bash -# Backup logico per-database con retry, sicuro per cluster Galera +# Backup logico per-database con retry e cleanup automatico +# Sicuro per cluster Galera ad alto carico # Autore: Marco + Copilot -# Obiettivo: dump affidabile anche su nodi molto attivi set -euo pipefail @@ -134,11 +134,15 @@ chmod 600 "$LOGFILE" echo "[$(date '+%F %T')] [$SERVER_NAME] SIZE apparent: $size_apparent_human ($size_apparent_bytes bytes) for $TARGET" >> "$LOGFILE" echo "[$(date '+%F %T')] [$SERVER_NAME] SIZE on-disk: $size_disk_human ($size_disk_bytes bytes) for $TARGET" >> "$LOGFILE" - # Risultato finale + # Risultato finale con cleanup in caso di fallimento if [ "$FAILED" -eq 0 ]; then echo "[$(date '+%F %T')] [$SERVER_NAME] RESULT: OK, all databases dumped successfully" >> "$LOGFILE" else - echo "[$(date '+%F %T')] [$SERVER_NAME] RESULT: PARTIAL, failed databases:$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" + rm -rf "$TARGET" + rm -f "$TMPLOG" + exit 1 fi rm -f "$TMPLOG" From a0d4ff3f3bfc239b68f79e3dc4c313df21ed414b Mon Sep 17 00:00:00 2001 From: "marco.locatelli@steamware.net" Date: Mon, 30 Mar 2026 10:53:17 +0200 Subject: [PATCH 6/6] miglioramento script full e incremental e commenti --- mariadb-backup/mariadb-backup-full.sh | 68 +++++++++++++----- mariadb-backup/mariadb-backup-incremental.sh | 72 +++++++++++++++----- 2 files changed, 106 insertions(+), 34 deletions(-) diff --git a/mariadb-backup/mariadb-backup-full.sh b/mariadb-backup/mariadb-backup-full.sh index 176dd41..fca5ff7 100644 --- a/mariadb-backup/mariadb-backup-full.sh +++ b/mariadb-backup/mariadb-backup-full.sh @@ -1,66 +1,100 @@ #!/bin/bash +# Backup FULL con atomicità, cleanup, log completi e sicurezza operativa +# Sicuro per cluster Galera +# Autore: Marco + Copilot + set -euo pipefail +# Aumenta i file descriptor disponibili ulimit -n 65536 +# Directory base dei backup BACKUP_BASE=/var/backups/tscale01 + +# Timestamp per la directory del backup TIMESTAMP=$(date +%Y%m%d-%H%M%S) + +# Directory finale del backup FULL TARGET="$BACKUP_BASE/backup-full-$TIMESTAMP" + +# File di log generale LOGFILE=/var/log/mariadb-backup.log + +# Lockfile per evitare esecuzioni concorrenti LOCKFILE=/var/lock/mariadb-backup.lock -# assicurati che il logfile esista e abbia permessi restrittivi +# Nome del server per i log +SERVER_NAME=$(hostname -s) + +# Assicura che il logfile esista e abbia permessi sicuri 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; } + # FLOCK: evita che due backup partano insieme + flock -n 9 || { + echo "[$(date '+%F %T')] [$SERVER_NAME] SKIP: another backup is running" >> "$LOGFILE" + exit 0 + } + # Crea directory del backup mkdir -p "$TARGET" chown mysql:mysql "$TARGET" chmod 750 "$TARGET" - TMPLOG=$(mktemp /tmp/mariadb-backup.XXXXXX) + # File temporaneo per catturare errori del backup + TMPLOG=$(mktemp /tmp/mariadb-backup-full.XXXXXX) echo "---------------------" >> "$LOGFILE" - echo "[$(date '+%F %T')] START backup full $TARGET" >> "$LOGFILE" + echo "[$(date '+%F %T')] [$SERVER_NAME] START backup FULL $TARGET" >> "$LOGFILE" - if mariadb-backup --defaults-file=/etc/mysql/backup.conf --backup --target-dir="$TARGET" --verbose >"$TMPLOG" 2>&1; then - echo "[$(date '+%F %T')] NOTE: full created UNPREPARED at $TARGET" >> "$LOGFILE" - echo "[$(date '+%F %T')] END backup full $TARGET" >> "$LOGFILE" + # Esecuzione backup FULL + if mariadb-backup \ + --defaults-file=/etc/mysql/backup.conf \ + --backup \ + --target-dir="$TARGET" \ + --verbose >"$TMPLOG" 2>&1; then - # misura dimensione apparente (byte logici) + echo "[$(date '+%F %T')] [$SERVER_NAME] NOTE: FULL created UNPREPARED at $TARGET" >> "$LOGFILE" + echo "[$(date '+%F %T')] [$SERVER_NAME] END backup FULL $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) + # 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')] [$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" - echo "[$(date '+%F %T')] RESULT: OK, no errors" >> "$LOGFILE" + echo "[$(date '+%F %T')] [$SERVER_NAME] RESULT: OK, no errors" >> "$LOGFILE" rm -f "$TMPLOG" + else - echo "[$(date '+%F %T')] ERROR during backup for $TARGET. See $TMPLOG" >> "$LOGFILE" + # Backup fallito → log + cleanup directory + echo "[$(date '+%F %T')] [$SERVER_NAME] ERROR during FULL backup for $TARGET. See $TMPLOG" >> "$LOGFILE" tail -n 200 "$TMPLOG" >> "$LOGFILE" + + echo "[$(date '+%F %T')] [$SERVER_NAME] CLEANUP: removing incomplete FULL backup directory $TARGET" >> "$LOGFILE" + rm -rf "$TARGET" + rm -f "$TMPLOG" exit 1 fi - # rotazione: mantieni 7 giorni solo per i full (log dettagliato come per gli incrementali) + # Rotazione: mantieni 7 giorni di FULL TO_DELETE=$(find "$BACKUP_BASE" -maxdepth 1 -type d -name 'backup-full-*' -mtime +7 -print 2>/dev/null || true) if [ -n "$TO_DELETE" ]; then - echo "[$(date '+%F %T')] ROTATE: removing old fulls:" >> "$LOGFILE" + echo "[$(date '+%F %T')] [$SERVER_NAME] ROTATE: removing old FULL backups:" >> "$LOGFILE" echo "$TO_DELETE" >> "$LOGFILE" echo "$TO_DELETE" | tr '\n' '\0' | xargs -0 -r rm -rf -- else - echo "[$(date '+%F %T')] ROTATE: no fulls to remove (<=7 days)" >> "$LOGFILE" + echo "[$(date '+%F %T')] [$SERVER_NAME] ROTATE: no FULL backups to remove (<=7 days)" >> "$LOGFILE" fi echo " " >> "$LOGFILE" diff --git a/mariadb-backup/mariadb-backup-incremental.sh b/mariadb-backup/mariadb-backup-incremental.sh index f434221..e4bd59a 100644 --- a/mariadb-backup/mariadb-backup-incremental.sh +++ b/mariadb-backup/mariadb-backup-incremental.sh @@ -1,79 +1,117 @@ #!/bin/bash +# Backup INCREMENTAL con atomicità, cleanup, log completi e sicurezza operativa +# Sicuro per cluster Galera +# Autore: Marco + Copilot + set -euo pipefail +# Aumenta i file descriptor disponibili ulimit -n 65536 +# Directory base dei backup BACKUP_BASE=/var/backups/tscale01 + +# Prefissi per full e incremental FULL_PREFIX=backup-full- INC_PREFIX=backup-inc- + +# Timestamp per la directory del backup TIMESTAMP=$(date +%Y%m%d-%H%M%S) + +# Directory finale del backup INCREMENTAL TARGET="$BACKUP_BASE/${INC_PREFIX}${TIMESTAMP}" + +# File di log generale LOGFILE=/var/log/mariadb-backup.log + +# Lockfile per evitare esecuzioni concorrenti LOCKFILE=/var/lock/mariadb-backup.lock -# assicurati che il logfile esista e abbia permessi restrittivi +# Nome del server per i log +SERVER_NAME=$(hostname -s) + +# Assicura che il logfile esista e abbia permessi sicuri 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; } + # FLOCK: evita che due backup partano insieme + flock -n 9 || { + echo "[$(date '+%F %T')] [$SERVER_NAME] SKIP: another backup is running" >> "$LOGFILE" + exit 0 + } + # Trova l'ultimo FULL disponibile LATEST_FULL=$(ls -1d ${BACKUP_BASE}/${FULL_PREFIX}* 2>/dev/null | sort | tail -n1 || true) if [ -z "$LATEST_FULL" ]; then - echo "[$(date '+%F %T')] ERROR: no full backup found in $BACKUP_BASE. Create a full backup first." >> "$LOGFILE" + echo "[$(date '+%F %T')] [$SERVER_NAME] ERROR: no FULL backup found. Create a FULL backup first." >> "$LOGFILE" exit 1 fi BASE_DIR="$LATEST_FULL" + # Crea directory del backup mkdir -p "$TARGET" chown mysql:mysql "$TARGET" chmod 750 "$TARGET" + # File temporaneo per catturare errori del backup TMPLOG=$(mktemp /tmp/mariadb-backup-inc.XXXXXX) echo "---------------------" >> "$LOGFILE" - echo "[$(date '+%F %T')] START incremental backup $TARGET (base: $BASE_DIR)" >> "$LOGFILE" + echo "[$(date '+%F %T')] [$SERVER_NAME] START incremental backup $TARGET (base: $BASE_DIR)" >> "$LOGFILE" - if mariadb-backup --defaults-file=/etc/mysql/backup.conf --backup --target-dir="$TARGET" --incremental-basedir="$BASE_DIR" --verbose >"$TMPLOG" 2>&1; then - echo "[$(date '+%F %T')] END incremental backup $TARGET" >> "$LOGFILE" + # Esecuzione backup INCREMENTAL + if mariadb-backup \ + --defaults-file=/etc/mysql/backup.conf \ + --backup \ + --target-dir="$TARGET" \ + --incremental-basedir="$BASE_DIR" \ + --verbose >"$TMPLOG" 2>&1; then - # misura dimensione apparente (byte logici) + echo "[$(date '+%F %T')] [$SERVER_NAME] END incremental 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) + # 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')] [$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" - echo "[$(date '+%F %T')] RESULT: OK, no errors" >> "$LOGFILE" + echo "[$(date '+%F %T')] [$SERVER_NAME] RESULT: OK, no errors" >> "$LOGFILE" rm -f "$TMPLOG" + else - echo "[$(date '+%F %T')] ERROR during incremental backup for $TARGET. See $TMPLOG" >> "$LOGFILE" + # Backup fallito → log + cleanup directory + echo "[$(date '+%F %T')] [$SERVER_NAME] ERROR during incremental backup for $TARGET. See $TMPLOG" >> "$LOGFILE" tail -n 200 "$TMPLOG" >> "$LOGFILE" + + echo "[$(date '+%F %T')] [$SERVER_NAME] CLEANUP: removing incomplete incremental backup directory $TARGET" >> "$LOGFILE" + rm -rf "$TARGET" + rm -f "$TMPLOG" exit 1 fi - # rotazione: mantieni esattamente 11 incrementali più recenti + # Rotazione: mantieni esattamente 11 incrementali più recenti cd "$BACKUP_BASE" || exit 1 TO_DELETE=$(ls -1d ${INC_PREFIX}* 2>/dev/null | sort | head -n -11 || true) if [ -n "$TO_DELETE" ]; then - echo "[$(date '+%F %T')] ROTATE: removing old incrementals:" >> "$LOGFILE" + echo "[$(date '+%F %T')] [$SERVER_NAME] ROTATE: removing old incrementals:" >> "$LOGFILE" echo "$TO_DELETE" >> "$LOGFILE" echo "$TO_DELETE" | xargs -r -d '\n' rm -rf -- else - echo "[$(date '+%F %T')] ROTATE: no incrementals to remove (<=11 present)" >> "$LOGFILE" + echo "[$(date '+%F %T')] [$SERVER_NAME] ROTATE: no incrementals to remove (<=11 present)" >> "$LOGFILE" fi echo " " >> "$LOGFILE" -) 9>"$LOCKFILE" \ No newline at end of file +) 9>"$LOCKFILE"