Dateien nach "scripts" hochladen
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# borg-backup-postgres.sh
|
||||
# Pull-basiertes Borg Backup für PostgreSQL via SSH
|
||||
# Läuft auf dem Raspberry Pi (Backup-Ziel)
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# --- Konfigurationsdatei laden -----------------------------------------------
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CONFIG_FILE="${SCRIPT_DIR}/../config/backup.conf"
|
||||
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo "[FEHLER] Konfigurationsdatei nicht gefunden: $CONFIG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
# --- Farben & Logging --------------------------------------------------------
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
log() { echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $*"; }
|
||||
log_ok() { echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] ✓${NC} $*"; }
|
||||
log_warn(){ echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] ⚠${NC} $*"; }
|
||||
log_err() { echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] ✗${NC} $*" >&2; }
|
||||
|
||||
# --- Umgebungsvariablen für Borg setzen --------------------------------------
|
||||
export BORG_PASSPHRASE
|
||||
export BORG_REPO
|
||||
export BORG_RSH="ssh -i ${SSH_KEY_PATH} -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10"
|
||||
|
||||
# --- Fehlerbehandlung --------------------------------------------------------
|
||||
BACKUP_START=$(date +%s)
|
||||
FAILED_DBS=()
|
||||
|
||||
cleanup() {
|
||||
local exit_code=$?
|
||||
if [[ $exit_code -ne 0 ]]; then
|
||||
log_err "Backup mit Fehlercode $exit_code beendet!"
|
||||
send_notification "FEHLER" "Borg Backup fehlgeschlagen (Exit: $exit_code). Fehlgeschlagene DBs: ${FAILED_DBS[*]:-keine}"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# --- Hilfsfunktionen ---------------------------------------------------------
|
||||
|
||||
check_dependencies() {
|
||||
local missing=()
|
||||
for cmd in borg ssh pg_restore; do
|
||||
if ! command -v "$cmd" &>/dev/null; then
|
||||
missing+=("$cmd")
|
||||
fi
|
||||
done
|
||||
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||
log_err "Fehlende Programme: ${missing[*]}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_ssh_connection() {
|
||||
log "Prüfe SSH-Verbindung zu ${PG_SSH_USER}@${PG_HOST}..."
|
||||
if ! ssh -i "${SSH_KEY_PATH}" \
|
||||
-o StrictHostKeyChecking=accept-new \
|
||||
-o ConnectTimeout=10 \
|
||||
-o BatchMode=yes \
|
||||
"${PG_SSH_USER}@${PG_HOST}" "echo ok" &>/dev/null; then
|
||||
log_err "SSH-Verbindung zu ${PG_HOST} fehlgeschlagen!"
|
||||
exit 1
|
||||
fi
|
||||
log_ok "SSH-Verbindung erfolgreich."
|
||||
}
|
||||
|
||||
check_repo() {
|
||||
log "Prüfe Borg-Repository: ${BORG_REPO}"
|
||||
if ! borg info &>/dev/null; then
|
||||
log_warn "Repository existiert nicht. Initialisiere..."
|
||||
borg init --encryption=repokey "${BORG_REPO}"
|
||||
log_ok "Repository initialisiert."
|
||||
log_warn "WICHTIG: Exportiere den Borg-Key jetzt:"
|
||||
log_warn " borg key export ${BORG_REPO} ~/borg-key-backup.txt"
|
||||
else
|
||||
log_ok "Repository OK."
|
||||
fi
|
||||
}
|
||||
|
||||
send_notification() {
|
||||
local status="$1"
|
||||
local message="$2"
|
||||
|
||||
# Nur senden wenn konfiguriert
|
||||
if [[ -n "${NOTIFY_EMAIL:-}" ]] && command -v mail &>/dev/null; then
|
||||
echo "$message" | mail -s "[Borg Backup] ${status}" "${NOTIFY_EMAIL}"
|
||||
fi
|
||||
|
||||
# Systemd-Journal / Wall-Nachricht
|
||||
if [[ "${NOTIFY_WALL:-false}" == "true" ]]; then
|
||||
wall "[Borg Backup] ${status}: ${message}" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
discover_databases() {
|
||||
log "Frage alle Datenbanken vom Server ab..."
|
||||
|
||||
# psql gibt eine Zeile pro DB aus; Systemdatenbanken ausschließen
|
||||
local list_cmd="sudo -u ${PG_DB_USER} psql -At -c \
|
||||
\"SELECT datname FROM pg_database WHERE datistemplate = false ORDER BY datname;\""
|
||||
|
||||
local all_dbs
|
||||
all_dbs=$(ssh -i "${SSH_KEY_PATH}" \
|
||||
-o StrictHostKeyChecking=accept-new \
|
||||
-o ConnectTimeout=10 \
|
||||
-o BatchMode=yes \
|
||||
"${PG_SSH_USER}@${PG_HOST}" \
|
||||
"${list_cmd}") || {
|
||||
log_err "Datenbankabfrage fehlgeschlagen!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Ausschlussliste anwenden
|
||||
local filtered=()
|
||||
while IFS= read -r db; do
|
||||
[[ -z "$db" ]] && continue
|
||||
local excluded=false
|
||||
for excl in "${PG_EXCLUDE_DATABASES[@]:-}"; do
|
||||
[[ "$db" == "$excl" ]] && excluded=true && break
|
||||
done
|
||||
if $excluded; then
|
||||
log_warn "Überspringe (ausgeschlossen): ${db}"
|
||||
else
|
||||
filtered+=("$db")
|
||||
fi
|
||||
done <<< "$all_dbs"
|
||||
|
||||
log_ok "Gefundene Datenbanken: ${filtered[*]}"
|
||||
# Ergebnis per nameref zurückgeben
|
||||
local -n _result=$1
|
||||
_result=("${filtered[@]}")
|
||||
}
|
||||
|
||||
backup_database() {
|
||||
local db="$1"
|
||||
local archive_name="${db}-$(date +%Y-%m-%dT%H:%M)"
|
||||
|
||||
log "Sichere Datenbank: ${db} → ${archive_name}"
|
||||
|
||||
local dump_cmd="sudo -u ${PG_DB_USER} pg_dump --format=custom --no-password ${db}"
|
||||
|
||||
if ssh -i "${SSH_KEY_PATH}" \
|
||||
-o StrictHostKeyChecking=accept-new \
|
||||
-o ConnectTimeout=10 \
|
||||
-o BatchMode=yes \
|
||||
"${PG_SSH_USER}@${PG_HOST}" \
|
||||
"${dump_cmd}" \
|
||||
| borg create \
|
||||
--stdin-name "${db}.pgdump" \
|
||||
--compression "${BORG_COMPRESSION:-lz4}" \
|
||||
--stats \
|
||||
--show-rc \
|
||||
"${BORG_REPO}::${archive_name}" -; then
|
||||
log_ok "Datenbank '${db}' erfolgreich gesichert."
|
||||
else
|
||||
log_err "Backup von Datenbank '${db}' fehlgeschlagen!"
|
||||
FAILED_DBS+=("$db")
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
run_prune() {
|
||||
log "Bereinige alte Backups (Retention Policy)..."
|
||||
borg prune \
|
||||
--keep-daily "${KEEP_DAILY:-7}" \
|
||||
--keep-weekly "${KEEP_WEEKLY:-4}" \
|
||||
--keep-monthly "${KEEP_MONTHLY:-6}" \
|
||||
--glob-archives '*' \
|
||||
--stats \
|
||||
--show-rc \
|
||||
"${BORG_REPO}"
|
||||
log_ok "Bereinigung abgeschlossen."
|
||||
}
|
||||
|
||||
run_compact() {
|
||||
# borg compact erst ab Version 1.2 verfügbar
|
||||
if borg --version | grep -qE "borg 1\.[2-9]|borg [2-9]"; then
|
||||
log "Kompaktiere Repository..."
|
||||
borg compact "${BORG_REPO}" && log_ok "Kompaktierung abgeschlossen."
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Hauptprogramm -----------------------------------------------------------
|
||||
|
||||
main() {
|
||||
log "========================================"
|
||||
log " Borg PostgreSQL Backup gestartet"
|
||||
log " Host: ${PG_HOST}"
|
||||
log " Repo: ${BORG_REPO}"
|
||||
log "========================================"
|
||||
|
||||
check_dependencies
|
||||
check_ssh_connection
|
||||
check_repo
|
||||
|
||||
# Datenbankliste ermitteln: automatisch oder manuell
|
||||
local databases=()
|
||||
if [[ "${PG_BACKUP_ALL:-false}" == "true" ]]; then
|
||||
discover_databases databases
|
||||
else
|
||||
databases=("${PG_DATABASES[@]}")
|
||||
log "Manuell konfigurierte Datenbanken: ${databases[*]}"
|
||||
fi
|
||||
|
||||
if [[ ${#databases[@]} -eq 0 ]]; then
|
||||
log_err "Keine Datenbanken zum Sichern gefunden!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Alle Datenbanken sichern
|
||||
local success_count=0
|
||||
for db in "${databases[@]}"; do
|
||||
if backup_database "$db"; then
|
||||
((success_count++)) || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Prune & Compact nur wenn mindestens ein Backup erfolgreich
|
||||
if [[ $success_count -gt 0 ]]; then
|
||||
run_prune
|
||||
run_compact
|
||||
fi
|
||||
|
||||
# Zusammenfassung
|
||||
local duration=$(( $(date +%s) - BACKUP_START ))
|
||||
local total=${#databases[@]}
|
||||
|
||||
log "========================================"
|
||||
log_ok "Backup abgeschlossen in ${duration}s"
|
||||
log_ok "Erfolgreich: ${success_count}/${total} Datenbanken"
|
||||
|
||||
if [[ ${#FAILED_DBS[@]} -gt 0 ]]; then
|
||||
log_err "Fehlgeschlagen: ${FAILED_DBS[*]}"
|
||||
send_notification "WARNUNG" "${success_count}/${total} DBs gesichert. Fehlgeschlagen: ${FAILED_DBS[*]}"
|
||||
exit 1
|
||||
else
|
||||
send_notification "OK" "Alle ${total} Datenbanken erfolgreich gesichert (${duration}s)."
|
||||
fi
|
||||
|
||||
log "========================================"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user