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:
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'])"state | Bedeutung | Was du tun kannst |
|---|---|---|
installing, verifying | ein Lauf ist im Gang oder wurde unterbrochen | warten; läuft kein Installer (pgrep -af install-nova-), rollback mit denselben Eingaben ausführen |
installed | installiert und geprüft, Rollback-Fenster offen | accept oder rollback |
accepting | Akzeptieren hat begonnen | accept erneut ausführen (setzt fort); ein Rollback ist nicht mehr möglich |
accepted | abgeschlossen | upgraden oder rekonfigurieren |
rolling-back | ein Rollback wurde unterbrochen | rollback mit denselben Eingaben erneut ausführen |
rolled-back | der Vorgänger ist wieder aktiv | nach 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):
python3 $SRC/src/installer/operations/install-nova-combined-runtime.py rollback "$COMB" "$SHA" "$P" "$S"Erwartet:
PASS: combined Nova runtime rolled back; database and owner state retained, packages an upgrade added removed, packages never downgradedWas 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/previousals 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, undnova-updateinstalliert 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:
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 nowDer 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-updateausgehen:rearm-upgradeoderrollback); - 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
| Komponente | Last-known-good | Ort |
|---|---|---|
| Application-Release | vorheriges Release | /opt/nova-controlpanel/application/previous |
| Application-Konfiguration, Secrets, Sites | Snapshot der offenen Transaktion | /opt/nova-controlpanel/application/.application-transaction (bis accept) |
| Datenbankschema | Dump vor der Evolution (0600) | vermerkt in fresh-database-evolution-v1.json |
| Paketschicht | vorherige Quittung | /var/lib/ncp-fresh-os/package-update/previous.json (bis accept) |
| Node Agent | vorherige Kopie | /opt/.nova-controlpanel-node-agent.previous (bis finalize oder rearm) |
| BIND-Keyring | von dns.keyring.apply behaltene Kopie | auf 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:
- Wurde eine Application-Transaktion unterbrochen (etwa durch einen Stromausfall während eines Upgrades), stellt sie den Zustand über den abgesicherten Rollback wieder her.
- 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):
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-controlpanelzeigtusrquota), die Quota-Units müssen gesund sein.nova-web-node-quota.sh boot-unitsrepariert die Boot-Drop-ins eines vorhandenen Quota-Mounts. - Nodes:
nova-controlpanel-node-agent.timerist aktiv, und das Journal vonnova-controlpanel-node-agent.servicezeigt einen erfolgreichen Poll ("status":"drained"oder Leerlauf), nichtAGENT_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 /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
rollbackvorzuziehen; 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):
| Bereich | Betroffene Ressourcen |
|---|---|
| Web | Websites, Domains, HTTP-Ordner und -Benutzer, FTP-/Shell-/WebDAV-Benutzer, Cronjobs |
| jede Mail-Ressource | |
| Datenbanken | Datenbanken mit ihren Rollen und Backups sowie auf dem Node gespeicherte Backup-Artefakte |
| Plattform | der Server mit Adressen, Maps, PHP-Runtimes, Erweiterungen, Firewall und Node-Konfiguration |
| DNS | eine 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 unterawaiting_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 Eventplatform.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)
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=3600Verwende 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:
| Code | Bedeutung |
|---|---|
NATIVE_NODE_ABANDONMENT_NODE_RECENTLY_SEEN | Der Node hatte innerhalb der Mindeststille Kontakt. |
NATIVE_NODE_ABANDONMENT_CONTROL_PLANE_DENIED | Der Node ist der Control-Plane-Node. |
NATIVE_NODE_ABANDONMENT_CONFIRMATION_MISMATCH | --confirm nennt nicht diesen Node. |
NATIVE_NODE_ABANDONMENT_SILENCE_INVALID | Die 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):
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
- Warte, bis
awaiting_settlement0 ist – der Settlement-Worker rechnet die abgebrochenen verteilten Operationen ab. Wiederhole dannplan, bisremainingleer ist.remainingverwendet genau die Zähler der Belegungsprüfung des Topologiewechsels. Ein offener Service-Blueprint-Schritt auf dem Node wird über seine Saga abgerechnet. - Entferne den Node mit einem normalen Topologiewechsel (Topologie ohne ihn schreiben, dann
native-topology-change.php planundreconfigure … --topology-change,verify,accept; siehe Anleitungen). In Operations ist er bereits widerrufen. - Lege verlorene Kundenressourcen bei Bedarf anhand des Mandantenberichts auf einem lebenden Node neu an. Primäre DNS-Zonen im Zustand
errorkannst du löschen oder neu anlegen, sobald es einen Primary gibt.