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"