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