Wiederherstellung

Wie du Installationen, Upgrades und Rekonfigurationen zurückrollst, was Boot-Recovery prüft und wie du einen endgültig verlorenen Node aufgibst.

Das kombinierte Journal

Jede Installation, jedes Upgrade und jede Rekonfiguration schreibt /var/lib/nova-controlpanel/installer/combined/transaction.json (root, 0600). Lies es, bevor du handelst:

sh
python3 -c "import json;j=json.load(open('/var/lib/nova-controlpanel/installer/combined/transaction.json'));print(j['state'],j.get('operation'),j['completed'],j['accepted'],j['candidate'],j['profile'])"
stateBedeutungWas du tun kannst
installing, verifyingein Lauf ist im Gang oder wurde unterbrochenwarten; läuft kein Installer (pgrep -af install-nova-), rollback mit denselben Eingaben ausführen
installedinstalliert und geprüft, Rollback-Fenster offenaccept oder rollback
acceptingAkzeptieren hat begonnenaccept erneut ausführen (setzt fort); ein Rollback ist nicht mehr möglich
acceptedabgeschlossenupgraden oder rekonfigurieren
rolling-backein Rollback wurde unterbrochenrollback mit denselben Eingaben erneut ausführen
rolled-backder Vorgänger ist wieder aktivnach einem Upgrade: rearm-upgrade, dann ein neuer Versuch; nach einer Neuinstallation ohne abgeschlossene Stufe (completed leer): derselbe Kandidat erneut oder archive-empty für einen anderen

operation fehlt bei einer Neuinstallation, sonst lautet es upgrade, reconfigure oder topology-change (ein Rollback des Letzteren stellt die vorherige Topologiedatei wieder her). predecessor nennt den akzeptierten Zustand, von dem die Transaktion ausging.

Rollback durch den Betreiber

Vor dem Akzeptieren kannst du eine Installation, ein Upgrade oder eine Rekonfiguration zurückrollen. Verwende den vertrauenswürdigen Quellbaum des Kandidaten der Transaktion und genau die Eingaben des Laufs (candidate, candidate_sha256, profile, stage aus dem Journal):

sh
python3 $SRC/src/installer/operations/install-nova-combined-runtime.py rollback "$COMB" "$SHA" "$P" "$S"

Erwartet:

text
PASS: combined Nova runtime rolled back; database and owner state retained, packages an upgrade added removed, packages never downgraded

Was ein Rollback tut, in umgekehrter Reihenfolge der Stufen:

  • Application: stellt aus ihrem Snapshot den vorherigen Release-Link, Konfiguration, Secrets, Apache-Sites (beider Instanzen), PHP-FPM-Pool, Units und Quittung wieder her. Eine Datenbank-Evolution wird aus ihrem privaten Dump rückgängig gemacht (seit dem Dump angelegte Tabellen werden entfernt).
  • Node Agent, Operations-Bridge, Operations-Runtime: stellen ihren vorherigen Code und ihre Units wieder her.
  • Paketschicht (nur Upgrade): stellt die vorherige Quittung byte-genau wieder her und entfernt Pakete, die das Update hinzugefügt hat, sofern kein anderes Paket sie inzwischen braucht. Paketversionen werden nie herabgestuft.
  • Upgrade und Rekonfiguration: prüfen zum Schluss den wiederhergestellten Vorgänger.

Was ein Rollback behält:

  • den Host-Layer, die Datenbank und den Owner einer Neuinstallation (auf diesem Host lässt sich nur derselbe Kandidat erneut installieren);
  • Registrierungsdateien von Node Agents und Workspace-Nodes (Node-Zustand, den kein Installer entfernt);
  • Kundendaten.

Wichtig: Nach dem Akzeptieren gibt es keinen Rollback. Der Vorgänger des akzeptierten Releases bleibt unter /opt/nova-controlpanel/application/previous als Last-known-good-Code, und die Sicherung der Datenbank-Evolution bleibt auf der Platte. Der kombinierte Installer hat aber keinen Modus, der eine akzeptierte Installation auf ihren Vorgänger zurücksetzt, und nova-update installiert nie eine ältere Version (NOVA_UPDATE_RELEASE_NOT_NEWER). Der Weg zurück nach dem Akzeptieren führt über deine eigenen Backups und Provider-Snapshots. Plane das vor dem ersten produktiven Upgrade ein (Updates).

Über den Update-Kanal läuft derselbe Rollback aus dem Installer des Releases selbst (/var/lib/nova-controlpanel/updates/releases/<v>/trusted/...), und nova-update rearm ersetzt rearm-upgrade.

--no-automatic-rollback lässt eine gescheiterte Transaktion zur Diagnose stehen. Rolle sie selbst zurück, bevor du irgendetwas anderes änderst.

Leere zurückgerollte Installation archivieren

Ein zurückgerolltes Journal bindet seinen Kandidaten: Nur derselbe Kandidat mit demselben Profil, derselben Stufe und demselben Paketmodus darf erneut installieren, weil die behaltene Datenbank und der Owner zu ihm gehören. Scheitert eine Neuinstallation, bevor ihre erste Stufe abgeschlossen ist (state rolled-back, completed und accepted leer, keine operation), bleibt nichts davon übrig – trotzdem würde jeder andere Kandidat mit existing combined transaction requires explicit resolution abgelehnt. Um einen anderen Kandidaten zu installieren, archivierst du dieses Journal ausdrücklich, als root, aus dem vertrauenswürdigen Quellbaum des neuen (oder des gescheiterten) Kandidaten:

sh
python3 $SRC/src/installer/operations/install-nova-combined-runtime.py archive-empty
# PASS: empty rolled-back combined installation archived: /var/lib/nova-controlpanel/installer/combined/history/rolled-back-<uuid>.json (candidate <sha>); another candidate may be installed now

Der Befehl nimmt die Installer-Sperre und lehnt alles andere ab, wobei das Journal unverändert bleibt:

  • ein Journal, das nicht zurückgerollt ist;
  • eines, das eine Stufe abgeschlossen oder akzeptiert hat (dann binden die behaltene Datenbank und der Owner den Kandidaten: denselben Kandidaten installieren oder auf einem neuen Host beginnen);
  • jedes Update, jede Rekonfiguration und jeden Topologiewechsel (deren Journal nennt den Vorgänger, von dem das nächste Upgrade und nova-update ausgehen: rearm-upgrade oder rollback);
  • einen Host, auf dem bereits eine Nova-Runtime (Application, Node Agent, Bridge, Operations) vorhanden ist.

Das archivierte Journal bleibt in history/ lesbar (nur root). Die Quittung der Paketschicht (/var/lib/ncp-fresh-os/package-layer.json) bleibt unberührt; hat der gescheiterte Versuch Pakete installiert, prüft oder aktualisiert die nächste Installation sie.

Last-known-good

KomponenteLast-known-goodOrt
Application-Releasevorheriges Release/opt/nova-controlpanel/application/previous
Application-Konfiguration, Secrets, SitesSnapshot der offenen Transaktion/opt/nova-controlpanel/application/.application-transaction (bis accept)
DatenbankschemaDump vor der Evolution (0600)vermerkt in fresh-database-evolution-v1.json
Paketschichtvorherige Quittung/var/lib/ncp-fresh-os/package-update/previous.json (bis accept)
Node Agentvorherige Kopie/opt/.nova-controlpanel-node-agent.previous (bis finalize oder rearm)
BIND-Keyringvon dns.keyring.apply behaltene Kopieauf jedem DNS-Node

Boot-Recovery

nova-application-recover.service ist eine Oneshot-Unit. Sie läuft vor dem Webserver des Panels (apache2@nova.service ab Profil v26, der sie per Requires= voraussetzt) und vor dem Kunden-Webserver apache2.service (der sie ab v26 nur per Wants= anfordert). Sie läuft bei jedem Start dieser Server: beim Booten und immer dann, wenn ein Installer oder Betreiber sie stoppt und startet. Ein reload löst sie nicht aus.

Sie tut genau zwei Dinge:

  1. Wurde eine Application-Transaktion unterbrochen (etwa durch einen Stromausfall während eines Upgrades), stellt sie den Zustand über den abgesicherten Rollback wieder her.
  2. Sie prüft die Installation statisch: Quittung, Release-Dateien und Digests, Konfiguration, Secrets und deren Modi, Units, Apache-Sites beider Instanzen, Isolation der PHP-FPM-Sockets und die Fleet-Adresse in den Listenern.

Eine abweichende oder manipulierte Installation lässt die Boot-Recovery scheitern, und die Panel-Instanz startet nicht. Kunden-Websites starten trotzdem (ab v26).

Die Laufzeitbereitschaft (Settlement-Worker, Gateway, Fleet-Exposure) prüft die Boot-Recovery nie; diese Dienste starten erst danach und werden von verify und accept geprüft. Während ein Installer läuft, hält er die Application-Sperre; die Boot-Recovery endet dann erfolgreich, ohne etwas anzufassen.

Nach einem Neustart

Erwarteter Zustand auf dem Control-Host (Profil v26):

sh
systemctl is-system-running                        # running (nicht degraded)
systemctl is-active apache2 apache2@nova mariadb php8.3-fpm \
    nova-controlpanel-operations-agent-coordinator nova-controlpanel-node-agent.timer
systemctl is-enabled nova-controlpanel-operations-agent-coordinator   # enabled
ss -ltn | grep ':19443 '                           # lauscht auf der Fleet-Adresse
journalctl -b -u nova-application-recover.service --no-pager | tail -3
  • Coordinator: Er wartet auf das private Netz und wiederholt das Binden mit wachsender Verzögerung (bis 60 s) ohne Startlimit; sobald die private Adresse existiert, kommt er hoch. Passiert das nie, ist die private Schnittstelle nicht konfiguriert (Voraussetzungen).
  • Web-Nodes: Der Quota-Mount muss zurück sein (findmnt /var/www/nova-controlpanel zeigt usrquota), die Quota-Units müssen gesund sein. nova-web-node-quota.sh boot-units repariert die Boot-Drop-ins eines vorhandenen Quota-Mounts.
  • Nodes: nova-controlpanel-node-agent.timer ist aktiv, und das Journal von nova-controlpanel-node-agent.service zeigt einen erfolgreichen Poll ("status":"drained" oder Leerlauf), nicht AGENT_MTLS_CONNECT_FAILED.

Melde dich danach im Panel an. Eine gescheiterte Recovery siehst du in journalctl -b -u nova-application-recover.service.

Boot-Recovery hat den Start des Panels verweigert

  • drift, candidate content digest invalid, metadata invalid, receipt invalid: Installierte Dateien wurden verändert. Das ist der Manipulationsschutz bei der Arbeit. Untersuche die Ursache, bevor du etwas änderst, und umgehe ihn nicht.
  • Ein Bereitschaftsfehler im Recovery-Journal (zum Beispiel DNS installed settlement readiness failed) tritt nur bei Releases auf, die älter als die statische Boot-Recovery sind. Upgrade auf ein Release mit statischer Boot-Recovery; Hinweise dazu im Kapitel Fehlersuche.

Wiederherstellung ohne Neustart

Der Application-Installer hat einen ausdrücklichen Recovery-Modus für eine unterbrochene Transaktion. Er tut dasselbe wie die Boot-Recovery und prüft zusätzlich die Laufzeitbereitschaft:

sh
sh /opt/nova-controlpanel/application/current/src/installer/application/install-nova-application-runtime.sh \
    recover /opt/nova-controlpanel/application/current /

Erwartet: PASS: Nova-owned application has no pending recovery transaction. oder PASS: Nova-owned interrupted application transaction recovered by rollback.

Hinweis: Zeigt das kombinierte Journal eine offene Transaktion, ist der kombinierte rollback vorzuziehen; er setzt auch die anderen Komponenten zurück.

Einen endgültig verlorenen Node aufgeben

Ein Fleet-Node wird über den Topologiewechsel nur entfernt, wenn die Application nachweist, dass er leer ist. Ein endgültig verlorener Node (Hardwareverlust, gelöschte VM, Host unter anderer Node-ID neu registriert) lässt sich nie leeren: Löschvorgänge seiner Ressourcen warten auf seine Antwort, seine Operationen in der Warteschlange werden nie abgerechnet, und jeder verteilte Konfigurations-Schreibvorgang scheitert weiter mit NODE_RECONCILE_REQUIRED. Einen solchen Node gibst du ausdrücklich auf.

Wann das Aufgeben verweigert wird

Vor jeder Änderung abgelehnt wird das Aufgeben für den Control-Plane-Node (Rolle master), ohne die getippte Bestätigung (noch einmal die Node-ID) und solange der Node innerhalb der Mindeststille Kontakt hatte (Standard 3600 s, nie unter 900 s; Kontakt = der letzte angenommene Befehl oder das letzte gesendete Ergebnis).

Was das Aufgeben bewirkt

1. Operations (eine Transaktion, einmalig und nur anhängend protokolliert): Der Node wird widerrufen – der Coordinator weist ihn ab, ein spätes Ergebnis einer bereits zugelassenen Sitzung wird mit AGENT_NODE_REVOKED abgelehnt. Jede offene Operation für ihn wird abgebrochen, seine wartenden oder versandten Agent-Anfragen scheitern.

2. Application (eine auditierte Datenbanktransaktion). Als verloren abgerechnet werden (Zustand deleted, Zeilen bleiben als Tombstones, keine Wirkung auf dem Node):

BereichBetroffene Ressourcen
WebWebsites, Domains, HTTP-Ordner und -Benutzer, FTP-/Shell-/WebDAV-Benutzer, Cronjobs
Mailjede Mail-Ressource
DatenbankenDatenbanken mit ihren Rollen und Backups sowie auf dem Node gespeicherte Backup-Artefakte
Plattformder Server mit Adressen, Maps, PHP-Runtimes, Erweiterungen, Firewall und Node-Konfiguration
DNSeine Zone, deren einziger Primary der Node war, geht auf error (verloren)

Außerdem:

  • Abgeschlossene Journale: Jedes offene Journal auf dem Node scheitert mit NATIVE_NODE_ABANDONED (Plattform, Web, Mail, User Access, Website-Backup, Datenbank, DNS-Node, Secondary und Keyring). Eine noch nicht gesendete Mail-Operation wird zurückgezogen. Eine späte Antwort kann deshalb nichts mehr abrechnen oder wiederbeleben. Verteilte Direktiven- und globale Konfigurationsoperationen rechnet der Settlement-Worker als gescheitertes Kohortenmitglied ab (mit Kompensation); der Bericht zählt sie unter awaiting_settlement.
  • Zurückgezogen: DNS-Replika- und Secondary-Platzierungen (die Zonen konvergieren auf den verbleibenden Secondaries), der DNS-Endpunkt und die TSIG-Paarschlüssel des Nodes, Mandanten- und Service-Blueprint-Platzierungen, Workspace-Bindungen, Uploads und Upload-Ziele sowie aktive Datenbank-Secrets.
  • Der Node erhält den Lebenszyklus abandoned: Keine Platzierung, Planung oder Zulassung nutzt ihn wieder.
  • Der Verlust wird pro Mandant auditiert (platform.fleet-node.abandoned.tenant-loss, mit Zählern und Ressourcen-IDs); der Node selbst erhält ein Event platform.fleet-node.abandoned.

Beide Schritte sind idempotent: Ein wiederholtes apply schließt ein unterbrochenes Aufgeben ab und meldet nichts Neues, sobald es vollständig ist.

Zertifikate nach Widerruf oder Aufgeben

Ein widerrufener oder aufgegebener Node behält ein Client-Zertifikat, das bis zu seinem Ablauf noch zur Fleet-CA führt. Kein Listener, den Nodes ansprechen, vertraut aber der Kette allein. Der Agent-Coordinator und die internen mTLS-Listener der Application (Secret-Leases auf 8092–8096, Backup-Exporte auf den Web-, Mail- und Datenbank-Listenern) lassen eine Anfrage nur über eine Entscheidung der Registry zu:

  • der Node muss bekannt und aktiv sein;
  • es darf keinen Aufgabe-Eintrag für ihn geben;
  • nur am Agent-Coordinator: Der SHA-256-Fingerprint des vorgelegten Zertifikats muss das aktuell registrierte Agent-Zertifikat sein. Ein ausrotiertes oder nach einem Widerruf wiederverwendetes Agent-Zertifikat wird ebenfalls abgelehnt.

Die Application-Listener sehen zweckgebundene Lease-Client-Zertifikate (eine Lease-CA pro Zweck), nie das Agent-Zertifikat; dort wird allein anhand des Nodes entschieden. Ein ausrotiertes Lease-Zertifikat eines aktiven Nodes bleibt bis zu seinem Ablauf oder dem Widerruf des Nodes nutzbar. Zusätzlich lehnen die Listener einen Node ab, dessen Lebenszyklus in der Application abandoned oder retired ist.

Eine Ablehnung liefert das feste 403 der Route (AUTHORIZATION_DENIED bei Leases, NODE_IDENTITY_REQUIRED bei Exporten) ohne Lease-Material und wird als internal.node-admission.refused mit Grund (NATIVE_NODE_ADMISSION_UNKNOWN, _REVOKED, _ABANDONED, _INACTIVE, _CERTIFICATE_MISMATCH, _IDENTITY_INVALID) und Listener-Zweck auditiert. Kann die Registry nicht antworten, erhält die Anfrage ein wiederholbares 503. Nichts wird zwischengespeichert: Widerruf, Rotation oder Aufgeben wirken ab der nächsten Anfrage nach dem Commit; ausgenommen ist nur eine Anfrage, die bereits läuft.

Vorgehen (als root auf dem Control-Host)

sh
cd /opt/nova-controlpanel/application/current
RUNTIME=/etc/nova-controlpanel/application-runtime.json
PASSWORD=/etc/nova-controlpanel/secrets/database-password
NODE=node_…

# 1. Plan (nur lesend): was jeder Mandant verlieren würde und was die Entfernung aus der Topologie noch blockiert.
php src/api/bin/native-node-abandon.php plan  "$NODE" "$RUNTIME" "$PASSWORD"
# 2. Betroffene Kunden informieren (der Plan listet sie pro Mandant), dann aufgeben:
php src/api/bin/native-node-abandon.php apply "$NODE" "$RUNTIME" "$PASSWORD" \
    --confirm="$NODE" --minimum-silence-seconds=3600

Verwende die Runtime- und Passwortdateien der installierten Application. Der Bericht kommt als JSON auf stdout; eine Ablehnung gibt einen Code aus und endet mit Exit 2:

CodeBedeutung
NATIVE_NODE_ABANDONMENT_NODE_RECENTLY_SEENDer Node hatte innerhalb der Mindeststille Kontakt.
NATIVE_NODE_ABANDONMENT_CONTROL_PLANE_DENIEDDer Node ist der Control-Plane-Node.
NATIVE_NODE_ABANDONMENT_CONFIRMATION_MISMATCH--confirm nennt nicht diesen Node.
NATIVE_NODE_ABANDONMENT_SILENCE_INVALIDDie Mindeststille liegt unter 900 s oder über einem Jahr.

Administratoren können dasselbe in der Browser-Sitzung tun: POST /fleet-node-abandonments mit {"node_id","confirm_node_id","minimum_silence_seconds"}. Das braucht die Step-up-Capability platform.node.abandon (Passwort plus TOTP), das CSRF-Token der Plattformverwaltung und einen Idempotency-Key. Der Plattform-Owner besitzt die Capability; andere Administratoren brauchen eine ausdrückliche Freigabe.

Muss Operations ohne die Application abgeschottet werden (etwa während die Application-Datenbank wiederhergestellt wird):

sh
OPERATIONS_DATABASE=… php …/operations/bin/agent-node.php abandon "$NODE" --confirm="$NODE" \
    [--minimum-silence-seconds=3600]

Die Abrechnung in der Application (apply) muss trotzdem folgen; sie übernimmt die bereits erfasste Operations-Quittung (duplicate: true).

Danach

  1. Warte, bis awaiting_settlement 0 ist – der Settlement-Worker rechnet die abgebrochenen verteilten Operationen ab. Wiederhole dann plan, bis remaining leer ist. remaining verwendet genau die Zähler der Belegungsprüfung des Topologiewechsels. Ein offener Service-Blueprint-Schritt auf dem Node wird über seine Saga abgerechnet.
  2. Entferne den Node mit einem normalen Topologiewechsel (Topologie ohne ihn schreiben, dann native-topology-change.php plan und reconfigure … --topology-change, verify, accept; siehe Anleitungen). In Operations ist er bereits widerrufen.
  3. Lege verlorene Kundenressourcen bei Bedarf anhand des Mandantenberichts auf einem lebenden Node neu an. Primäre DNS-Zonen im Zustand error kannst du löschen oder neu anlegen, sobald es einen Primary gibt.