many improvements
This commit is contained in:
86
backup.sh
86
backup.sh
@@ -11,6 +11,10 @@ readonly NOW=$(date +'%Y-%m-%d')
|
|||||||
readonly THIS_RUN="${BACKUP_BASE}/${NOW}"
|
readonly THIS_RUN="${BACKUP_BASE}/${NOW}"
|
||||||
readonly LATEST="${BACKUP_BASE}/latest"
|
readonly LATEST="${BACKUP_BASE}/latest"
|
||||||
|
|
||||||
|
# Retention policy
|
||||||
|
readonly KEEP_DAILY=3 # keep this many daily snapshots
|
||||||
|
readonly KEEP_WEEKLY=2 # keep this many weekly (Sunday) snapshots
|
||||||
|
|
||||||
# Directories (or files) to back up (without DB volumes)
|
# Directories (or files) to back up (without DB volumes)
|
||||||
declare -a SOURCE_ITEMS=(
|
declare -a SOURCE_ITEMS=(
|
||||||
"/home/oster/server"
|
"/home/oster/server"
|
||||||
@@ -37,20 +41,15 @@ RSYNC_OPTS=(
|
|||||||
"${EXCLUDES[@]}"
|
"${EXCLUDES[@]}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Link-dest array — populated later; using an array avoids unquoted-variable issues
|
||||||
|
LINK_DEST=()
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# LOGGING
|
# LOGGING
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
log() { echo "[$(date +'%F %T')] $*"; }
|
log() { echo "[$(date +'%F %T')] $*"; }
|
||||||
die() { log "FATAL: $*"; exit 1; }
|
die() { log "FATAL: $*"; exit 1; }
|
||||||
|
|
||||||
# Locking
|
|
||||||
lockfile="${BACKUP_BASE}/.backup.lock"
|
|
||||||
cleanup() { rm -f "$lockfile"; }
|
|
||||||
trap cleanup EXIT
|
|
||||||
if ! ( set -o noclobber; echo "$$" > "$lockfile" ) 2>/dev/null; then
|
|
||||||
die "Another backup is already running (lockfile exists at $lockfile)."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# FUNCTIONS
|
# FUNCTIONS
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -60,31 +59,30 @@ check_mount() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Rotation: keep 7 daily and 4 weekly
|
# Rotation: keep KEEP_DAILY daily and KEEP_WEEKLY weekly snapshots
|
||||||
rotate_old() {
|
rotate_old() {
|
||||||
log "Applying retention policy (7 daily, 4 weekly)."
|
log "Applying retention policy (${KEEP_DAILY} daily, ${KEEP_WEEKLY} weekly)."
|
||||||
cd "$BACKUP_BASE" || die "Cannot cd into $BACKUP_BASE"
|
|
||||||
|
|
||||||
# find snapshot dirs YYYY-MM-DD only
|
# Pre-compute the dates to keep so we don't fork date(1) inside every inner loop iteration
|
||||||
for dir in $(ls -1 | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'); do
|
declare -A keep_dates=()
|
||||||
keep=0
|
for i in $(seq 0 $(( KEEP_DAILY - 1 ))); do
|
||||||
|
keep_dates["$(date -d "-$i days" +%F)"]=1
|
||||||
|
done
|
||||||
|
last_sunday=$(date -d "last sunday" +%F)
|
||||||
|
for i in $(seq 0 $(( KEEP_WEEKLY - 1 ))); do
|
||||||
|
keep_dates["$(date -d "${last_sunday} -$(( i * 7 )) days" +%F)"]=1
|
||||||
|
done
|
||||||
|
|
||||||
# Keep last 7 daily
|
# Collect snapshot dirs matching YYYY-MM-DD using a glob (nullglob makes empty glob safe)
|
||||||
for i in $(seq 0 6); do
|
for dir in "$BACKUP_BASE"/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]; do
|
||||||
[ "$dir" = "$(date -d "-$i day" +%F)" ] && keep=1
|
[ -d "$dir" ] || continue
|
||||||
done
|
name=$(basename "$dir")
|
||||||
|
|
||||||
# Keep last 4 Sundays (weeklies)
|
if [[ -v keep_dates["$name"] ]]; then
|
||||||
for i in $(seq 0 27); do
|
log " Keeping snapshot $name"
|
||||||
d=$(date -d "last sunday -$i week" +%F)
|
|
||||||
[ "$dir" = "$d" ] && keep=1
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ $keep -eq 0 ]; then
|
|
||||||
log " Deleting expired snapshot $dir"
|
|
||||||
rm -rf "$dir"
|
|
||||||
else
|
else
|
||||||
log " Keeping snapshot $dir"
|
log " Deleting expired snapshot $name"
|
||||||
|
rm -rf "${BACKUP_BASE:?}/$name"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
@@ -95,14 +93,34 @@ rotate_old() {
|
|||||||
log "=== Starting backup run: $NOW ==="
|
log "=== Starting backup run: $NOW ==="
|
||||||
check_mount
|
check_mount
|
||||||
|
|
||||||
mkdir -p "$BACKUP_BASE" "$THIS_RUN"
|
# Fail early if the backup base directory doesn't exist (drive not mounted / path wrong)
|
||||||
|
[ -d "$BACKUP_BASE" ] || die "Backup base directory $BACKUP_BASE does not exist."
|
||||||
|
|
||||||
|
# Locking — base dir is confirmed to exist, so the lockfile write will succeed
|
||||||
|
lockfile="${BACKUP_BASE}/.backup.lock"
|
||||||
|
cleanup() { rm -f "$lockfile"; }
|
||||||
|
trap cleanup EXIT
|
||||||
|
if ! ( set -o noclobber; echo "$$" > "$lockfile" ) 2>/dev/null; then
|
||||||
|
# Check if the PID in the lockfile is still alive; if not, it's a stale lock
|
||||||
|
stale_pid=$(cat "$lockfile" 2>/dev/null || true)
|
||||||
|
if [[ -n "$stale_pid" ]] && ! kill -0 "$stale_pid" 2>/dev/null; then
|
||||||
|
log "Removing stale lockfile (PID $stale_pid is no longer running)."
|
||||||
|
rm -f "$lockfile"
|
||||||
|
echo "$$" > "$lockfile"
|
||||||
|
else
|
||||||
|
die "Another backup is already running (PID ${stale_pid:-unknown}, lockfile: $lockfile)."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$THIS_RUN"
|
||||||
|
|
||||||
# Incremental snapshot?
|
# Incremental snapshot?
|
||||||
if [ -d "$LATEST" ]; then
|
if [ -L "$LATEST" ] && [ -d "$LATEST" ]; then
|
||||||
LINK_DEST="--link-dest=$(readlink -f "$LATEST")"
|
latest_real=$(readlink -f "$LATEST")
|
||||||
log "Using link-dest for incremental snapshot → $LATEST"
|
LINK_DEST=("--link-dest=${latest_real}")
|
||||||
|
log "Using link-dest for incremental snapshot → $latest_real"
|
||||||
else
|
else
|
||||||
LINK_DEST=""
|
LINK_DEST=()
|
||||||
log "No previous backup; creating full snapshot."
|
log "No previous backup; creating full snapshot."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -113,7 +131,7 @@ for src in "${SOURCE_ITEMS[@]}"; do
|
|||||||
mkdir -p "$dst"
|
mkdir -p "$dst"
|
||||||
log "Backing up $src → $dst"
|
log "Backing up $src → $dst"
|
||||||
nice -n 10 ionice -c2 -n7 \
|
nice -n 10 ionice -c2 -n7 \
|
||||||
rsync "${RSYNC_OPTS[@]}" $LINK_DEST \
|
rsync "${RSYNC_OPTS[@]}" "${LINK_DEST[@]}" \
|
||||||
"$src"/ "$dst"/
|
"$src"/ "$dst"/
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user