diff --git a/mariadb-backup/mariadb-backup-full.sh b/mariadb-backup/mariadb-backup-full.sh new file mode 100644 index 0000000..25b9326 --- /dev/null +++ b/mariadb-backup/mariadb-backup-full.sh @@ -0,0 +1,67 @@ +#!/bin/bash +set -euo pipefail + +ulimit -n 65536 + +BACKUP_BASE=/var/lib/mariadb-backup +TIMESTAMP=$(date +%Y%m%d-%H%M%S) +TARGET="$BACKUP_BASE/backup-full-$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" + +( + flock -n 9 || { echo "[$(date '+%F %T')] SKIP: another backup is running" >> "$LOGFILE"; exit 0; } + + mkdir -p "$TARGET" + chown mysql:mysql "$TARGET" + chmod 750 "$TARGET" + + TMPLOG=$(mktemp /tmp/mariadb-backup.XXXXXX) + + echo "---------------------" >> "$LOGFILE" + echo "[$(date '+%F %T')] 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" + + # 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 backup for $TARGET. See $TMPLOG" >> "$LOGFILE" + tail -n 200 "$TMPLOG" >> "$LOGFILE" + rm -f "$TMPLOG" + exit 1 + fi + + # rotazione: mantieni 7 giorni solo per i full (log dettagliato come per gli incrementali) + 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 "$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" + fi + + echo " " >> "$LOGFILE" + +) 9>"$LOCKFILE" diff --git a/mariadb-backup/mariadb-backup-incremental.sh b/mariadb-backup/mariadb-backup-incremental.sh new file mode 100644 index 0000000..617736e --- /dev/null +++ b/mariadb-backup/mariadb-backup-incremental.sh @@ -0,0 +1,77 @@ +#!/bin/bash +set -euo pipefail + +ulimit -n 65536 + +BACKUP_BASE=/var/lib/mariadb-backup +FULL_PREFIX=backup-full- +INC_PREFIX=backup-inc- +TIMESTAMP=$(date +%Y%m%d-%H%M%S) +TARGET="$BACKUP_BASE/${INC_PREFIX}${TIMESTAMP}" +LOGFILE=/var/log/mariadb-backup.log +LOCKFILE=/var/lock/mariadb-backup.lock + +touch "$LOGFILE" +chown root:root "$LOGFILE" +chmod 600 "$LOGFILE" + +( + flock -n 9 || { echo "[$(date '+%F %T')] SKIP: another backup is running" >> "$LOGFILE"; exit 0; } + + 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" + exit 1 + fi + + BASE_DIR="$LATEST_FULL" + + mkdir -p "$TARGET" + chown mysql:mysql "$TARGET" + chmod 750 "$TARGET" + + TMPLOG=$(mktemp /tmp/mariadb-backup-inc.XXXXXX) + + echo "---------------------" >> "$LOGFILE" + echo "[$(date '+%F %T')] 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" + + # 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 incremental backup for $TARGET. See $TMPLOG" >> "$LOGFILE" + tail -n 200 "$TMPLOG" >> "$LOGFILE" + rm -f "$TMPLOG" + exit 1 + fi + + # 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 "$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" + fi + + echo " " >> "$LOGFILE" + +) 9>"$LOCKFILE" \ No newline at end of file diff --git a/mariadb-backup/mariadb-backup-logic.sh b/mariadb-backup/mariadb-backup-logic.sh new file mode 100644 index 0000000..35e301e --- /dev/null +++ b/mariadb-backup/mariadb-backup-logic.sh @@ -0,0 +1,79 @@ +#!/bin/bash +set -euo pipefail + +ulimit -n 65536 + +BACKUP_BASE=/var/lib/mariadb-backup +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" + +( + 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"