99 lines
5.3 KiB
Markdown
99 lines
5.3 KiB
Markdown
|
|
# Backup & Restore — Laufzeit-Zustand (PVE-LXCs + VPS-Guacamole-Stack)
|
|||
|
|
|
|||
|
|
Sichert den Laufzeit-Zustand, nicht nur Dateien: LXC-Configs, Datenbanken, Guacamole-Verbindungsdefinitionen/TOTP-Secrets, Vaultwardens verschlüsselten Tresor. Der ZFS-Mirror (`nas/freigabe`) deckt reinen Dateiverlust ab — dieses Kapitel deckt den Fall "Server/Container ist weg und muss aus dem Nichts wiederhergestellt werden".
|
|||
|
|
|
|||
|
|
## 1. PVE-Host: Backup-Dataset + Ausschluss der NAS-Freigabe
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
zfs create nas/backups
|
|||
|
|
mkdir -p /nas/backups/pve /nas/backups/vps
|
|||
|
|
pct set 101 -mp0 /nas/freigabe,mp=/srv/freigabe,backup=0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`nas/backups` bewusst **nicht** als PVE-Storage registrieren (gleiche Begründung wie beim `nas`-Pool selbst, siehe `01-pve-host.md` Abschnitt 3) — `vzdump --dumpdir` schreibt direkt dorthin, kein `pvestatd`-Polling nötig. CT101s `mp0` (Bind-Mount von `/nas/freigabe`, 37GB) wird von Container-Backups ausgeschlossen, sonst läge die komplette NAS-Freigabe redundant auch im Backup-Ziel auf demselben Pool.
|
|||
|
|
|
|||
|
|
## 2. PVE-Host: nächtlicher vzdump für alle 4 LXCs
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
(crontab -l 2>/dev/null; echo "0 3 * * * vzdump 100 101 102 103 --mode snapshot --compress zstd --dumpdir /nas/backups/pve --prune-backups keep-last=3 >> /var/log/vzdump-cron.log 2>&1") | crontab -
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
LVM-Thin-Snapshot-Modus (`--mode snapshot`) → alle 4 Container laufen auf `local-lvm`, minimale Downtime. Vaultwarden (CT103, SQLite im WAL-Modus) ist damit ohne separates DB-Skript crash-konsistent abgedeckt — SQLite ist genau für diesen Fall ausgelegt.
|
|||
|
|
|
|||
|
|
## 3. VPS: rsync/cron nachinstallieren
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
apt update && apt install -y rsync cron
|
|||
|
|
systemctl enable --now cron
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
⚠️ **NICHT vergessen:** Die minimale Debian-13-Installation auf dem VPS hat weder `rsync` (liefert `rrsync` mit, ab Version 3.4.x fertig unter `/usr/bin/rrsync`) noch `cron` vorinstalliert. Ohne `cron`-Paket schlägt `crontab -l`/`crontab -` mit "command not found" fehl.
|
|||
|
|
|
|||
|
|
## 4. VPS: Backup-Skript für die Guacamole-Stack
|
|||
|
|
|
|||
|
|
`/usr/local/bin/backup-guacamole.sh`:
|
|||
|
|
```bash
|
|||
|
|
#!/bin/bash
|
|||
|
|
set -e
|
|||
|
|
DEST=/root/backups
|
|||
|
|
DATE=$(date +%F)
|
|||
|
|
mkdir -p "$DEST"
|
|||
|
|
docker exec guac-postgres pg_dump -U guacamole_user guacamole_db | gzip > "$DEST/guacamole_db_$DATE.sql.gz"
|
|||
|
|
tar czf "$DEST/guacamole-opt_$DATE.tar.gz" --exclude=pg-data -C /opt guacamole
|
|||
|
|
find "$DEST" -mtime +7 -delete
|
|||
|
|
```
|
|||
|
|
```bash
|
|||
|
|
chmod +x /usr/local/bin/backup-guacamole.sh
|
|||
|
|
(crontab -l 2>/dev/null; echo "0 2 * * * /usr/local/bin/backup-guacamole.sh >> /var/log/backup-guacamole.log 2>&1") | crontab -
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`pg_dump` statt rohem Kopieren von `pg-data` — transaktionssicher bei laufendem Postgres, roher Kopiervorgang eines laufenden Datenverzeichnisses wäre das nicht. Rotation: 7 Tage lokal auf dem VPS (das eigentliche Archiv liegt nach Schritt 6 auf dem NAS).
|
|||
|
|
|
|||
|
|
## 5. Restriktiver Pull-Key (VPS → NAS)
|
|||
|
|
|
|||
|
|
Dediziertes Keypair auf dem PVE-Host, **nicht** `id_rsa` oder `wol_vps_key` wiederverwenden:
|
|||
|
|
```bash
|
|||
|
|
ssh-keygen -t ed25519 -f /root/.ssh/backup_pull_key -N "" -C "backup-pull@pve"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Public Key auf dem VPS in `authorized_keys` ergänzen:
|
|||
|
|
```bash
|
|||
|
|
PUBKEY=$(cat /root/.ssh/backup_pull_key.pub)
|
|||
|
|
ssh root@<TS_IP_VPS> "echo 'restrict,command=\"rrsync -ro /root/backups/\" $PUBKEY' >> /root/.ssh/authorized_keys"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
→ Der Key kann ausschließlich lesend aus `/root/backups/` syncen, keine Shell, kein Port-/Agent-Forwarding. Im Gegensatz zum `wolonly`-Key (`01-pve-host.md` Abschnitt 6) ist `restrict` hier unproblematisch — der Pull läuft per Cron ohne PTY-Anforderung, das PTY-Problem betraf nur Guacamoles SSH-Client.
|
|||
|
|
|
|||
|
|
## 6. PVE-Host: nächtlicher Pull-Cron
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
(crontab -l 2>/dev/null; echo '0 4 * * * rsync -av -e "ssh -i /root/.ssh/backup_pull_key" root@<TS_IP_VPS>: /nas/backups/vps/ >> /var/log/vps-backup-pull.log 2>&1') | crontab -
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Zeitlich nach beiden vorherigen Jobs (02:00 VPS-Backup, 03:00 vzdump).
|
|||
|
|
|
|||
|
|
⚠️ **NICHT TUN:** Auf dem Client den vollen Pfad wiederholen (`root@vps:/root/backups/ ziel/`). `rrsync -ro /root/backups/` fixiert das Restricted-Verzeichnis bereits serverseitig; ein vom Client mitgegebener Pfad wird (nach Entfernen des führenden `/`) daran **angehängt** — Ergebnis ist der doppelte, nicht existierende Pfad `/root/backups/root/backups/` (Fehler "No such file or directory"). Korrekt ist ein leerer Pfad nach dem Doppelpunkt: `root@<TS_IP_VPS>:` — das adressiert die Wurzel des Restricted-Verzeichnisses.
|
|||
|
|
|
|||
|
|
## 7. Restore
|
|||
|
|
|
|||
|
|
**LXC (jede der 4 Instanzen):**
|
|||
|
|
```bash
|
|||
|
|
pct restore <vmid> /nas/backups/pve/vzdump-lxc-<vmid>-<timestamp>.tar.zst --storage local-lvm
|
|||
|
|
```
|
|||
|
|
Vaultwarden (CT103) braucht keinen separaten Restore-Schritt — Tresor + `rsa_key.pem` sind im LXC-Restore enthalten.
|
|||
|
|
|
|||
|
|
**Guacamole-Stack (VPS):** Compose-Stack aus dem Tar-Archiv wiederherstellen, dann DB einspielen:
|
|||
|
|
```bash
|
|||
|
|
gunzip -c guacamole-opt_<date>.tar.gz | tar x -C /opt # falls /opt/guacamole fehlt
|
|||
|
|
cd /opt/guacamole && docker compose up -d postgres guacd
|
|||
|
|
gunzip -c guacamole_db_<date>.sql.gz | docker exec -i guac-postgres psql -U guacamole_user guacamole_db
|
|||
|
|
docker compose up -d guacamole
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Referenz: Backup-Übersicht
|
|||
|
|
|
|||
|
|
| Was | Wo (Quelle) | Mechanismus | Ziel | Rotation |
|
|||
|
|
|---|---|---|---|---|
|
|||
|
|
| 4 LXCs (100–103) | PVE-Host | `vzdump` Snapshot, 03:00 | `/nas/backups/pve` | keep-last=3 |
|
|||
|
|
| Guacamole-DB + Configs | VPS | `pg_dump` + `tar`, 02:00 | `/root/backups` (VPS, Zwischenstand) | 7 Tage |
|
|||
|
|
| — Pull der VPS-Backups | PVE-Host | `rsync` über `backup_pull_key`, 04:00 | `/nas/backups/vps` | folgt VPS-Rotation |
|