Sicherung und Wiederherstellung
Zielgruppe: Technische Administration
Überblick
Die Daten von oPBWeb liegen an zwei Stellen, die zusammengehören: In der Datenbank stehen alle Metadaten, im Objektspeicher die Dateiinhalte. Die Datenbank verweist über SHA-256-Prüfsummen und Schlüssel auf Objekte im Speicher (blobs/…, artifacts/…). Eine Sicherung ist nur brauchbar, wenn beide Teile aus demselben Zeitpunkt stammen und die passenden Secrets dabei sind.
Die Befehle in diesem Kapitel sind Beispiele. Die Datenbank-Sicherung mit mariadb-dump und das Sichern der Garage-Volumes bei gestopptem Stack sind der vorgesehene Weg; die konkreten Befehle für Volumes und Wiederherstellung sind daraus abgeleitet. Passe Pfade und Namen an deine Umgebung an und teste die Wiederherstellung, bevor du dich darauf verlässt.
Was du sicherst
| Was | Wo | Sichern | Hinweis |
|---|---|---|---|
| Datenbank | Volume opbweb_mariadb-data | ja, als Dump | Pakete, Versionen, Benutzer, Jobs, Protokoll, Snippets, Configserver und Depots, Einstellungen, verschlüsselte opsi-Passwörter |
| Objektspeicher-Metadaten | Volume opbweb_garage-meta | ja, bei gestopptem Stack | Bucket, Schlüssel, Objektverzeichnis |
| Objektspeicher-Daten | Volume opbweb_garage-data | ja, bei gestopptem Stack | Inhalte der Paketdateien und gebaute Pakete |
| Secrets | secrets/ | ja | ohne opsi_key_private.pem ist das opsi-Passwort verloren, ohne die übrigen Secrets passen Datenbank und Objektspeicher nicht mehr zusammen |
| Konfiguration | .env | ja | |
| Zertifikate | certs/ (inkl. certs/ldap/) | ja | TLS-Zertifikat und Schlüssel, LDAP-CA |
| Redis | Volume opbweb_redis-data | nein | nur Warteschlange und kurzlebiger Status |
| S3-Zugangsdaten | Volume opbweb_garage-credentials | nein | garage-init schreibt sie neu |
Merke dir außerdem den Versionsstand von oPBWeb (Git-Commit oder Versionen aus der Statusleiste). Eine Datenbank lässt sich nur mit derselben oder einer neueren oPBWeb-Version weiterverwenden.
Sicherungen enthalten Passwort-Hashes, das verschlüsselte opsi-Passwort und mit secrets/ auch den Schlüssel dazu. Lege sie nur an einem geschützten Ort ab und bewahre secrets/ nicht unverschlüsselt neben den Daten auf, wenn andere Personen Zugriff auf das Sicherungsziel haben.
Sicherung erstellen
Damit Datenbank und Objektspeicher denselben Stand haben, stoppst du zuerst die Dienste, die Daten verändern, sicherst dann die Datenbank und anschließend bei gestopptem Stack den Objektspeicher.
Prüfe unter Jobs, dass keine Jobs laufen oder warten.
Lege ein geschütztes Sicherungsverzeichnis an:
mkdir -p -m 700 backupStoppe Frontend, Worker und API. Datenbank und Objektspeicher laufen weiter:
docker stop opbweb-frontend opbweb-worker opbweb-apiSichere die Datenbank:
docker exec opbweb-db sh -c 'mariadb-dump -uroot -p"$(cat /run/secrets/db_root_password)" --single-transaction opbweb' > backup/opbweb.sqlDas Root-Passwort liest der Befehl im Container aus dem Docker-Secret; es erscheint nicht auf der Kommandozeile des Hosts.
Stoppe den Stack:
./stop.shSichere die beiden Garage-Volumes (Beispiel mit einem Alpine-Hilfscontainer):
docker run --rm -v opbweb_garage-meta:/v:ro -v "$PWD/backup":/b alpine tar czf /b/garage-meta.tgz -C /v . docker run --rm -v opbweb_garage-data:/v:ro -v "$PWD/backup":/b alpine tar czf /b/garage-data.tgz -C /v .Sichere Secrets, Konfiguration und Zertifikate:
tar czf backup/config.tgz secrets .env certsStarte den Stack wieder:
./start.sh
Übertrage den Inhalt von backup/ anschließend auf dein Sicherungsziel. Die Ausfallzeit hängt vor allem von der Größe des Objektspeichers ab.
Wenn eine kurze Unterbrechung des ganzen Stacks ohnehin möglich ist, kannst du statt des Dumps auch das Volume opbweb_mariadb-data bei gestopptem Stack genauso wie die Garage-Volumes sichern. Dann gehören zur Datenbank zwingend die Passwörter aus secrets/db_password.txt und secrets/db_root_password.txt, die im Volume hinterlegt sind.
Wiederherstellen
Die Reihenfolge ist wichtig: erst Secrets und Konfiguration, dann der Objektspeicher, dann die Datenbank, zuletzt der Start.
Stack stoppen.
./stop.shProgrammstand bereitstellen. Checke dieselbe oPBWeb-Version aus, die zur Sicherung gehört (oder eine neuere). Verwendest du Images aus einer Registry, stehen deren Versionen in der gesicherten
.env(BACKEND_IMAGEusw.).Secrets,
.envund Zertifikate zurückspielen. Verwende die originalen Dateien aus der Sicherung, nicht neu erzeugte:tar xzf backup/config.tgz chmod 750 secrets && chmod 444 secrets/*Die Dateien brauchen die Rechte 444, damit die Prozesse in den Containern sie lesen können;
start.shsetzt die Rechte überinit-secrets.shohnehin bei jedem Start.Objektspeicher zurückspielen. Entferne die vorhandenen Garage-Volumes einschließlich der S3-Zugangsdaten und spiele die Sicherung ein:
docker volume rm opbweb_garage-meta opbweb_garage-data opbweb_garage-credentials docker volume create opbweb_garage-meta docker volume create opbweb_garage-data docker run --rm -v opbweb_garage-meta:/v -v "$PWD/backup":/b alpine tar xzf /b/garage-meta.tgz -C /v docker run --rm -v opbweb_garage-data:/v -v "$PWD/backup":/b alpine tar xzf /b/garage-data.tgz -C /vDas Volume
garage-credentialsmusst du entfernen, damit garage-init die Zugangsdaten aus dem zurückgespielten Schlüssel neu schreibt. Bleibt ein altes Volume liegen, passen die Zugangsdaten nicht zum Objektspeicher. Im Log von garage-init erscheint beim nächsten Start „Credential files missing, writing from existing key…“.Datenbank zurückspielen. Entferne das Datenbank-Volume, starte nur die Datenbank und spiele den Dump ein:
docker volume rm opbweb_mariadb-data docker compose up -d db docker compose ps db # warten, bis der Status "healthy" lautet docker exec -i opbweb-db sh -c 'mariadb -uroot -p"$(cat /run/secrets/db_root_password)" opbweb' < backup/opbweb.sqlMariaDB legt beim ersten Start des leeren Volumes die Datenbank
opbwebund den Anwendungsbenutzer mit den Passwörtern aussecrets/an. Hast du das Volume statt eines Dumps gesichert, spielst du es wie die Garage-Volumes zurück.Stack starten.
./start.shDie API führt fehlende Migrationen aus, falls der Programmstand neuer ist als die Sicherung. Ein erstes Administrator-Konto legt sie nicht an, weil die Benutzertabelle aus der Sicherung gefüllt ist.
Prüfen.
- Systemstatus: alle Dienste OK. Als Administrator prüfst du unter Objektspeicher, dass keine fehlenden Blobs gemeldet werden; fehlende Blobs deuten auf Datenbank und Objektspeicher aus unterschiedlichen Zeitpunkten hin.
- Depots → Configserver: für jeden Configserver Verbindung testen. Schlägt die Anmeldung fehl, passt
opsi_key_private.pemnicht zur Datenbank; trage dann das Passwort des technischen Benutzers neu ein. - Öffne ein Paket und lade eine Datei bzw. ein gebautes Paket herunter.
Jobs, die zum Zeitpunkt der Sicherung warteten oder liefen, setzt oPBWeb nach der Wiederherstellung nicht fort. Starte sie bei Bedarf neu.
Konsistenz
| Situation | Folge |
|---|---|
| Datenbank älter als Objektspeicher | überzählige Objekte; harmlos, Bereinigen … im Systemstatus entfernt sie |
| Datenbank neuer als Objektspeicher | fehlende Blobs oder Build-Ergebnisse; betroffene Dateien lassen sich nicht öffnen, betroffene Versionen müssen neu gebaut werden |
neues Schlüsselpaar opsi_key_*.pem | opsi-Passwort nicht lesbar; neu eintragen |
neue db_password.txt bzw. db_root_password.txt zu einem gesicherten Datenbank-Volume | API und Worker erreichen die Datenbank nicht |
altes Volume garage-credentials zu zurückgespieltem Objektspeicher | Zugriff auf den Objektspeicher scheitert; Volume entfernen und neu starten |
Bei regelmäßigen Sicherungen hilft eine feste Reihenfolge in einem Skript, das alle Schritte des Abschnitts Sicherung erstellen nacheinander ausführt.