fleet-manager: add backup.sh for the durable-state volumes
Snapshot fleet-data (inventory + admins.json + enroll.key — the admin-lockout risk) and influx-data to timestamped tarballs via a throwaway alpine container (read-only, live-safe), with BACKUP_KEEP retention. Documented in DEPLOY.md Backups with a restore pointer.
This commit is contained in:
@@ -222,6 +222,19 @@ and the `influx-data` volume. Losing `admins.json` locks every admin out — you
|
||||
re-bootstrap with `register-admin`. Losing `enroll.key` invalidates any
|
||||
outstanding (unused) enrol links.
|
||||
|
||||
`./backup.sh [dest-dir]` tars both volumes (read-only, safe to run live) into
|
||||
timestamped `.tar.gz` files (default `./backups`, keeps the newest
|
||||
`BACKUP_KEEP=14`). Schedule it with a systemd timer / cron and copy the output
|
||||
offsite:
|
||||
|
||||
```bash
|
||||
./backup.sh /srv/backups/fleet-manager
|
||||
# restore (manager stopped) — see the header of backup.sh
|
||||
```
|
||||
|
||||
If you run compose under a non-default project name, pass
|
||||
`COMPOSE_PROJECT_NAME=<name>` so it finds the `<name>_fleet-data` volume.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **`permission denied` writing the inventory on approve** — the runtime image is
|
||||
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# backup.sh — snapshot the fleet-manager durable state to timestamped tarballs.
|
||||
#
|
||||
# Two docker named volumes hold everything that isn't reproducible from the image:
|
||||
# - fleet-data : inventory.json + admins.json + enroll.key (CRITICAL — losing
|
||||
# admins.json/enroll.key locks every admin out of the UI)
|
||||
# - influx-data : the metrics time-series (history; regenerates going forward)
|
||||
#
|
||||
# Runs a throwaway alpine container to tar each volume read-only into $DEST. Safe
|
||||
# to run live. Schedule it (systemd timer / cron); copy $DEST offsite.
|
||||
#
|
||||
# Usage:
|
||||
# ./backup.sh [dest-dir] # default dest: ./backups
|
||||
# COMPOSE_PROJECT_NAME=foo ./backup.sh # if you run compose under another project
|
||||
#
|
||||
# Restore (manager stopped):
|
||||
# docker run --rm -v <project>_fleet-data:/dest -v "$PWD/backups":/src alpine \
|
||||
# sh -c 'rm -rf /dest/* && tar xzf /src/fleet-data-YYYYmmdd-HHMMSS.tar.gz -C /dest'
|
||||
set -euo pipefail
|
||||
|
||||
DEST=${1:-./backups}
|
||||
PROJECT=${COMPOSE_PROJECT_NAME:-fleet-manager} # compose derives volume names as <project>_<key>
|
||||
STAMP=$(date -u +%Y%m%d-%H%M%S)
|
||||
VOLUMES=${BACKUP_VOLUMES:-"fleet-data influx-data"}
|
||||
|
||||
mkdir -p "$DEST"
|
||||
DEST_ABS=$(cd "$DEST" && pwd)
|
||||
|
||||
for key in $VOLUMES; do
|
||||
vol="${PROJECT}_${key}"
|
||||
if ! docker volume inspect "$vol" >/dev/null 2>&1; then
|
||||
echo "backup: SKIP $vol (no such volume — wrong COMPOSE_PROJECT_NAME?)"
|
||||
continue
|
||||
fi
|
||||
out="${key}-${STAMP}.tar.gz"
|
||||
echo "backup: $vol -> $DEST_ABS/$out"
|
||||
docker run --rm \
|
||||
-v "${vol}:/src:ro" \
|
||||
-v "${DEST_ABS}:/backup" \
|
||||
alpine tar czf "/backup/${out}" -C /src .
|
||||
done
|
||||
|
||||
# Retention: keep the newest $KEEP of each volume's tarballs (0 = keep all).
|
||||
KEEP=${BACKUP_KEEP:-14}
|
||||
if [ "$KEEP" -gt 0 ]; then
|
||||
for key in $VOLUMES; do
|
||||
# ls -t for mtime order; our filenames are safe (key + UTC timestamp).
|
||||
# shellcheck disable=SC2012
|
||||
ls -1t "$DEST_ABS/${key}-"*.tar.gz 2>/dev/null | tail -n +"$((KEEP + 1))" | while read -r old; do
|
||||
echo "backup: prune $old"
|
||||
rm -f "$old"
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
echo "backup: done ($STAMP) — copy $DEST_ABS offsite."
|
||||
Reference in New Issue
Block a user