13 KiB
Monatlicher Offsite-Restore-Test
Der Restore-Test prüft den neuesten vollständigen Sicherungssatz direkt aus dem
konfigurierten rclone-Crypt-Remote. Alle Komponenten werden heruntergeladen,
entschlüsselt, validiert und in ein privates Testverzeichnis entpackt. SQL-Dumps
werden zusätzlich in eine isolierte MariaDB importiert und mit mariadb-check
geprüft. Produktive Anwendungen werden dabei nicht angehalten oder überschrieben.
Speicherbedarf und Bereinigung
Wegen des knappen Speichers auf Gemini läuft immer nur eine Komponente:
- Archiv herunterladen; Größe und SHA-256 gegen das Manifest prüfen.
- Archivstruktur und Kompression vollständig validieren.
- Platz für die entpackte Komponente abschätzen und diese mit GNU tar entpacken.
- Das komprimierte Archiv sofort löschen.
- Bei einem DB-Dump eine frische Test-DB starten, importieren und Tabellen prüfen.
- Den eigenen DB-Container samt eventuellen anonymen Volumes entfernen, danach alle temporären Dateien dieser Komponente löschen; erst dann die nächste laden.
Es liegt somit nie der gesamte Sicherungssatz gleichzeitig auf der Platte. Der Spitzenbedarf ist ungefähr das Maximum aus:
- komprimiertem Archiv plus entpackter größter Komponente und Dateisystem-Overhead;
- SQL-Dump plus zugehöriger Testdatenbank einschließlich MariaDB-Systemtabellen, Indexen und temporären DB-Dateien.
Standard: höchstens 4 GiB geplantes Test-Arbeitsvolumen, zusätzlich mindestens
2 GiB freie Reserve. Die Komponente muss vollständig in dieses Budget passen;
sonst scheitert der Test mit einem Bericht und bereinigt seine Dateien. Es wird
nicht still auf eine Zufallsdatei oder eine Teilprüfung ausgewichen. Das Budget
lässt sich nach Prüfung mit df -h /var/backups/app-backup anpassen.
Vor Download und Entpacken wird der Bedarf geprüft. Während externer Prozesse wird freier Speicher laufend kontrolliert, die tatsächliche Belegung regelmäßig nachgemessen und bei Grenzverletzung abgebrochen. Dies ist keine harte Dateisystemquote: schnelle DB-Schreibvorgänge und fremde Prozesse können zwischen Messungen weiteren Platz belegen. Daher die Reserve nicht knapp auf null setzen. Für den DB-Start werden zusätzlich mindestens 512 MiB eingeplant; das ist eine Mindestreserve, keine obere Grenze für die Größe der importierten Datenbank.
Normale Fehler, Speicher-/Zeitlimits und SIGTERM/SIGINT führen zur Bereinigung.
Nach einem harten Abbruch führt systemd ExecStopPost aus. Bei einem Fehler wird
außerdem der Bereinigungsdienst gestartet; er versucht eine fehlgeschlagene
Bereinigung alle 15 Minuten erneut. Ein aktivierter Boot-Bereinigungsdienst und
der nächste Teststart räumen unterbrochene Tests ebenfalls auf. Beide verwenden
dieselbe Sperre wie Backup und Update.
Kann beispielsweise der Docker-Daemon nicht erreicht und der Testcontainer nicht sicher entfernt werden, lautet das Ergebnis FAIL / Bereinigung FAILED. Die Dateien werden dann nicht unter einer möglicherweise noch laufenden DB gelöscht. Die Bereinigung wird wiederholt; ein solcher Zustand darf nicht als Erfolg gelten. Bei einem ausgeschalteten Server erfolgt sie naturgemäß erst nach dem Neustart.
Nur der eindeutig zu diesem Test gehörende Container und das markierte
$WORKDIR/restore-test/scratch werden bereinigt. Vorhandene Backups, reguläre
manuelle Restore-Verzeichnisse und fremde Docker-Ressourcen werden nicht gelöscht.
Es gibt kein allgemeines docker prune. Das einmal vorbereitete DB-Image bleibt
als wiederverwendbare Abhängigkeit erhalten; sein einmaliger Platzbedarf kommt
zum Arbeitsbudget hinzu. Images werden im Test weder heruntergeladen noch gebaut.
Einmalige Vorbereitung auf Gemini
Am 12.09.2026 lesend auf Gemini festgestellt: MariaDB 10.11.18, Docker-CLI
vorhanden und rund 9,1 GiB frei auf /var. Für diesen Stand passt die unten
genannte Serie mariadb:10.11. Die freie Kapazität ist eine Momentaufnahme;
vor Image-Download und Test nochmals prüfen.
Docker Engine und CLI müssen lokal vorhanden sein. Verwendet wird ausschließlich
unix:///var/run/docker.sock. Zur produktiven MariaDB passende Major-/Minor-Version
ermitteln:
sudo mariadbd --no-defaults --version
sudo docker --host unix:///var/run/docker.sock info
Beispiel nur wenn die Produktion MariaDB 10.11.x verwendet:
sudo docker --host unix:///var/run/docker.sock pull mariadb:10.11
Bei einer anderen Produktionsversion den Tag entsprechend ersetzen. Platz für Image und Testdaten vorab prüfen. Ein vorhandenes kompatibles Image kann wiederverwendet werden; für reproduzierbare Tests kann statt des Tags ein Digest konfiguriert werden. Bei einem Major-/Minor-Upgrade der Produktions-DB auch das Testimage prüfen und aktualisieren.
Vor dem Start des neuen Update-Pakets /etc/app-backup/app-backup.conf bearbeiten:
ENABLE_RESTORE_TEST="true"
RESTORE_TEST_DB_IMAGE="mariadb:10.11" # Beispiel: an Produktionsversion anpassen!
RESTORE_TEST_MAX_WORK_GB=4
RESTORE_TEST_MIN_FREE_GB=2
RESTORE_TEST_MAX_AGE_HOURS=72
RESTORE_TEST_TIMEOUT=39600 # 11 Stunden, inklusive Remote-Arbeit
MAIL_HOSTNAME="gemini.jr-it-services.de"
Danach das neue .run-Update wie in UPDATE_ANLEITUNG.md
installieren. Der Starter prüft das lokale Image und die freie Reserve, bevor er
installiert. Er lädt kein Image nach. Nach der normalen Update-Abnahme aktiviert
er den Restore-Test-Timer und den Boot-Bereinigungsdienst, wenn
ENABLE_RESTORE_TEST=true gesetzt ist. Der Timer kann bei einem nachzuholenden
Termin sofort starten. Das Update-Ergebnis bestätigt weiterhin das Paket und sein
Abnahmebackup; der neue Restore-Test hat ein eigenes Ergebnis und eine eigene Mail.
Bestehende Konfigurationen ohne die neue Option bleiben kompatibel: Der monatliche
Test ist zunächst deaktiviert. Eine reine Installation mit install.sh installiert
die Units und erhält bestehende Zeitpläne; für die erstmalige Aktivierung dann:
sudo bash /usr/local/sbin/app-restore-test.sh --check
sudo systemctl enable app-restore-test-cleanup.service
sudo systemctl enable --now app-restore-test.timer
--check prüft Konfiguration, lokale Werkzeuge, Image-Metadaten und freie Reserve.
Es lädt kein Backup herunter und führt keinen DB-Import aus. Den ersten echten
Test nach Installation anstoßen und sein Ergebnis kontrollieren:
sudo systemctl start --no-block app-restore-test.service
sudo journalctl -fu app-restore-test.service --no-pager
sudo cat /var/backups/app-backup/restore-test/state.json
sudo systemctl list-timers app-restore-test.timer --no-pager
Bei angepasstem WORKDIR die Pfade entsprechend ersetzen. Einen bestimmten Satz
kann man manuell prüfen; Altersgrenze und alle übrigen Prüfungen bleiben aktiv:
sudo bash /usr/local/sbin/app-restore-test.sh \
--remote-run appbackup_DATUM_UHRZEIT_ID
Zeitplan, Auswahl und Ergebnisse
Der Standardtermin ist der 1. jedes Monats um 06:00 Uhr Serverzeit, mit bis zu
15 Minuten zufälliger Verzögerung. Persistent=true holt verpasste Termine nach.
Ein belegter Backup-/Restore-/Update-Lock liefert Exit-Code 75; systemd versucht
es eine Stunde später erneut. Ein echter Prüffehler löst eine Fehlermail aus und
wird nicht durch automatische Neuversuche als Erfolg verdeckt.
Unvollständige neuere Sätze ohne COMPLETE werden mit Protokolleintrag übersprungen.
Für den neuesten abgeschlossenen Satz müssen Marker und Manifest zusammenpassen,
die Lauf-ID muss dem Remote-Verzeichnis entsprechen, alle konfigurierten
Komponenten müssen enthalten sein und die Remote-Dateiliste muss vollständig sein.
Ein beschädigter abgeschlossener Satz wird nicht durch einen älteren ersetzt.
Standardmäßig sind maximal 72 Stunden alte Backups zulässig; ein ungültiger
Zeitstempel oder ein Zeitstempel mehr als fünf Minuten in der Zukunft scheitert.
Der Bericht verwendet die bisherigen Mail-Einstellungen (ENABLE_MAIL_REPORT,
MAIL_TO, MAIL_FROM, MAIL_HOSTNAME, MAIL_SENDMAIL_BIN, MAIL_SEND_TIMEOUT).
Der Betreff lautet beispielsweise:
[app-restore-test] SUCCESS gemini.jr-it-services.de appbackup_2026-…
Enthalten sind Alter und ID des Sicherungssatzes, Laufzeit, Status je Komponente, Archivgröße, Anzahl der Archiveinträge, Anzahl der importierten Tabellen sowie Bereinigung: OK/FAILED. Es werden weder SQL-Inhalte noch Dateiinhalte in die Mail oder das Testprotokoll übernommen. Der Status je Komponente wird erst nach seiner Bereinigung als erfolgreich gespeichert.
Unter $WORKDIR/restore-test/ bleiben nur kleine Betriebsnachweise erhalten:
| Datei | Bedeutung |
|---|---|
state.json |
Letzter/aktueller Lauf; bei Start sofort RUNNING, abschließend SUCCESS oder FAIL, Bereinigungs- und Mailstatus |
last-success.json |
Letzter vollständig geprüfter und bereinigter Satz; bleibt bei einem späteren Fehler erhalten |
report.eml |
Jeweils letzter Mailbericht, auch bei Zustellungsfehler |
restore-test.log und .1 |
Rotierendes Testprotokoll, jeweils ungefähr 512 KiB |
.owner |
Kennzeichnung des Testverzeichnisses für sichere Bereinigung |
Temporäre Prozess-/Container-Marker verschwinden nach erfolgreicher Bereinigung.
Die Nachweise sind root-privat. Für die unabhängige Überwachung sollte ein
RUNNING, cleanup != OK, mail == FAILED oder ein mehr als etwa 40 Tage alter
last-success.json auffallen. Ein erfolgreicher Restore bei fehlgeschlagener
Mail bleibt inhaltlich SUCCESS, liefert aber Exit-Code 1 und mail: FAILED.
Der SMTP-Versand ist wie bisher eine Übergabe an den lokalen MTA, kein Nachweis
der tatsächlichen Zustellung.
Manuelle Wiederholung der Bereinigung, auch bei deaktivierter Testfunktion:
sudo bash /usr/local/sbin/app-restore-test.sh --cleanup
sudo journalctl -u app-restore-test-cleanup.service -n 50 --no-pager
Zum Aussetzen des Zeitplans app-restore-test.timer deaktivieren. Den
Boot-Bereinigungsdienst aktiviert lassen, bis eventuell unterbrochene Testdaten
nachweislich entfernt wurden.
Isolation und Aussagekraft
Jeder DB-Dump erhält eine neue Datenbankinstanz. Der Container hat ausschließlich ein Bind-Mount auf sein privates DB-Testverzeichnis, kein Netzwerk, keine veröffentlichten Ports, keine zusätzlichen Linux-Capabilities, ein schreibgeschütztes Root-Dateisystem und keinen persistenten Docker-Logtreiber. Er läuft mit maximal 512 MiB RAM, 128 Prozessen und einer CPU. MariaDB liest keine Hostkonfiguration, verwendet nur ihren eigenen Unix-Socket und hat Event-Scheduler und Binlog aus. Der SQL-Client verwendet Batch-/Binary-Modus, damit Dumps keine lokalen Client-Kommandos ausführen. Docker-API-Ausgaben und SQL-Fehlertexte gelangen nicht ungefiltert in Logs. Hintergrunddatenbanken können keine produktiven DB-Sockets oder Konfigurationen erreichen.
Der Test belegt Download, Entschlüsselung, Integrität, tatsächliches Entpacken und DB-Import mit Tabellenprüfung. Er belegt weder eine vollständige Datenabdeckung, die schon im Backup fehlte, noch fachliche Konsistenz zwischen Anwendungen. Ein Start-/Funktionstest von Nextcloud, WordPress und Gitea auf einer getrennten VM bleibt eine zusätzliche Abnahme. Dateibesitz/ACLs werden beim Entpacken angefordert; die korrekte Anwendung aller Sicherheitskontexte auf einem neuen Produktivsystem bedarf dort einer eigenen Prüfung.
Technische Referenzen: Docker run, Docker ohne Netzwerk, MariaDB-Systemtabellen initialisieren, MariaDB-Serveroptionen.
Entwicklung und lokale Verifikation
Der Standardlauf (tests/run.sh im bestehenden Testcontainer) prüfte am
12.09.2026 81 Fälle: 80 erfolgreich, der separate Docker-Test planmäßig
übersprungen. Zusätzlich bestanden alle 19 gezielten Restore-Tests mit lokalem
Docker-Daemon und dem offiziellen mariadb:10.11 (29,0 Sekunden). Vier gezielte
Sperr-/Installer-Tests bestanden ebenfalls (23,7 Sekunden). Dazu gehören echte
Crypt-Downloads, vollständiges Entpacken, DB-Import/Tabellenprüfung, ungültiges SQL,
SIGTERM/SIGKILL, fehlender Platz, beschädigte Statusdateien und ein zeitweise nicht
erreichbarer Docker-Daemon. Container-Isolation und vollständige Bereinigung
werden nachgeprüft. Die bereits vorhandene Entwicklungstest-DB mit MariaDB 11.8.6
wurde ebenfalls erfolgreich verwendet. Es werden keine echten Mails versendet.
Auf einem separaten Entwicklungs-/Testrechner mit den Abhängigkeiten aus
tests/Dockerfile, Docker-CLI und lokal vorbereitetem Image lässt sich die
gezielte Prüfung aus dem Quell-Checkout starten:
sudo env RESTORE_TEST_REAL_DOCKER_IMAGE=mariadb:10.11 \
python3 -m unittest discover -s tests -p test_restore_test.py -v
Diese Tests verwenden ausschließlich selbst angelegte temporäre Konfigurationen,
Daten und Container. Die vollständige Basissuite enthält zusätzlich simulierte
systemd-/Installertests und gehört in den bestehenden isolierten Testcontainer.
Die neuen Units wurden mit systemd-analyze verify geprüft. Ihre tatsächliche
Ausführung unter Rocky/SELinux und der produktive OneDrive-/MTA-Pfad sind nach
Installation auf Gemini noch abzunehmen.