#!/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 # 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" ( # 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" # File temporaneo per catturare errori del backup TMPLOG=$(mktemp /tmp/mariadb-backup-full.XXXXXX) echo "---------------------" >> "$LOGFILE" echo "[$(date '+%F %T')] [$SERVER_NAME] START 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 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) 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)" >> "$LOGFILE" echo "[$(date '+%F %T')] [$SERVER_NAME] SIZE on-disk: $size_disk_human ($size_disk_bytes bytes)" >> "$LOGFILE" echo "[$(date '+%F %T')] [$SERVER_NAME] RESULT: OK, no errors" >> "$LOGFILE" rm -f "$TMPLOG" else # 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 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')] [$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')] [$SERVER_NAME] ROTATE: no FULL backups to remove (<=7 days)" >> "$LOGFILE" fi echo " " >> "$LOGFILE" ) 9>"$LOCKFILE"