Synchronize repository with Athena deployment
This commit is contained in:
Executable
+78
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env bash
|
||||
# Encrypted off-host backup for data-disk and total-loss recovery.
|
||||
set -Eeuo pipefail
|
||||
umask 077
|
||||
|
||||
CONFIG=${DISASTER_BACKUP_CONFIG:-/etc/mike-ai/disaster-backup.env}
|
||||
STATE=/var/lib/mike-ai-disaster-backup
|
||||
|
||||
log() { printf '\n==> %s\n' "$*"; }
|
||||
die() { printf 'FEHLER: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
[[ $EUID -eq 0 ]] || die "Bitte als root ausführen."
|
||||
[[ -r $CONFIG ]] || die "Konfiguration fehlt: $CONFIG"
|
||||
# shellcheck disable=SC1090
|
||||
source "$CONFIG"
|
||||
[[ ${DISASTER_BACKUP_ENABLED:-false} == true ]] || die \
|
||||
"Externes Backup ist noch nicht freigeschaltet (DISASTER_BACKUP_ENABLED=true)."
|
||||
[[ -n ${RESTIC_REPOSITORY:-} ]] || die "RESTIC_REPOSITORY fehlt."
|
||||
if [[ -n ${RESTIC_REQUIRE_MOUNT:-} ]]; then
|
||||
mountpoint -q "$RESTIC_REQUIRE_MOUNT" || die \
|
||||
"Externes Backupziel ist nicht eingehängt: $RESTIC_REQUIRE_MOUNT"
|
||||
fi
|
||||
[[ -n ${RESTIC_PASSWORD_FILE:-} && -r $RESTIC_PASSWORD_FILE ]] || die \
|
||||
"RESTIC_PASSWORD_FILE fehlt oder ist nicht lesbar."
|
||||
command -v restic >/dev/null || die "restic ist nicht installiert."
|
||||
command -v docker >/dev/null || die "Docker ist nicht installiert."
|
||||
exec 9>/run/lock/athena-disaster-backup.lock
|
||||
flock -n 9 || die "Ein Disaster-Backup läuft bereits."
|
||||
|
||||
install -d -m 0700 "$STATE/latest"
|
||||
|
||||
log "Konsistentes Docker-Schnellbackup erzeugen"
|
||||
docker inspect mike-ai-backup >/dev/null 2>&1 || die "mike-ai-backup fehlt."
|
||||
docker exec mike-ai-backup backup
|
||||
latest=$(readlink -f /data/docker-backups/athena-latest.tar.gz)
|
||||
[[ -s $latest ]] || die "Lokales Docker-Backup wurde nicht erzeugt."
|
||||
gzip -t "$latest" || die "Lokales Docker-Backup ist beschädigt."
|
||||
install -m 0600 "$latest" "$STATE/latest/docker-state.tar.gz"
|
||||
sha256sum "$STATE/latest/docker-state.tar.gz" >"$STATE/latest/docker-state.tar.gz.sha256"
|
||||
|
||||
log "Wiederaufbau-Metadaten erfassen"
|
||||
{
|
||||
printf 'created_utc=%s\n' "$(date -u +%FT%TZ)"
|
||||
printf 'hostname=%s\n' "$(hostname)"
|
||||
printf 'source_commit=%s\n' "$(git -C /opt/mike-ai/stack rev-parse HEAD 2>/dev/null || printf unknown)"
|
||||
findmnt -rn -o SOURCE,UUID,FSTYPE,TARGET / /data 2>/dev/null || true
|
||||
} >"$STATE/latest/manifest.txt"
|
||||
find /data/models -type f -printf '%P\t%s\n' 2>/dev/null | sort \
|
||||
>"$STATE/latest/model-manifest.tsv"
|
||||
docker ps -a --format '{{.Names}}\t{{.Image}}\t{{.Status}}' \
|
||||
>"$STATE/latest/container-manifest.tsv"
|
||||
|
||||
paths=(/etc/mike-ai /opt/mike-ai "$STATE/latest")
|
||||
for path in \
|
||||
/data/voice /data/music /data/audio /data/llama-dashboard \
|
||||
/data/mike-ai-operator /data/benchmarks /data/model-benchmarks \
|
||||
/data/image-comparison /data/backups /data/deploy-backups; do
|
||||
[[ ! -e $path ]] || paths+=("$path")
|
||||
done
|
||||
|
||||
tag=${RESTIC_TAG:-athena-disaster}
|
||||
log "Verschlüsseltes externes Backup schreiben"
|
||||
if ! restic snapshots >/dev/null 2>&1; then
|
||||
log "Neues Restic-Repository initialisieren"
|
||||
restic init
|
||||
fi
|
||||
restic backup --tag "$tag" "${paths[@]}"
|
||||
|
||||
log "Aufbewahrung anwenden"
|
||||
restic forget --tag "$tag" \
|
||||
--keep-daily "${RESTIC_KEEP_DAILY:-14}" \
|
||||
--keep-weekly "${RESTIC_KEEP_WEEKLY:-8}" \
|
||||
--keep-monthly "${RESTIC_KEEP_MONTHLY:-12}" --prune
|
||||
|
||||
log "Letzten Snapshot verifizieren"
|
||||
restic snapshots --tag "$tag" --latest 1
|
||||
restic check
|
||||
printf 'ATHENA_DISASTER_BACKUP_OK\n'
|
||||
Reference in New Issue
Block a user