CloudNativePG Restore

Dieses Runbook beschreibt die Wiederherstellung eines CloudNativePG-Clusters aus einem Barman-Cloud-Backup.

Die Wiederherstellung ersetzt den bestehenden Cluster einschließlich seiner Pods und PersistentVolumeClaims. Die im Cluster-Objekt konfigurierte Storage-Größe und Storage-Konfiguration müssen unverändert übernommen werden.

Voraussetzungen

  • Zugriff auf den Kubernetes-Cluster mit kubectl

  • Zugriff auf Flux mit der flux CLI

  • Ein vorhandenes und lesbares CloudNativePG-Backup

  • Das zugehörige Barman-Object-Store-Objekt ist vorhanden

  • Das vom Object Store referenzierte Secret ist vorhanden

Die folgenden Variablen werden in den Beispielen verwendet:

export NAMESPACE=paperless-ngx
export CLUSTER=cnpg-paperless-ngx
export KUSTOMIZATION=paperless
export RECOVERY_FILE="${CLUSTER}-recovery.yaml"

Ablauf

1. Flux-Kustomization pausieren

Die verwaltende Flux-Kustomization pausieren, damit Flux den Cluster während der Wiederherstellung nicht auf den Git-Zustand zurücksetzt:

flux suspend kustomization "${KUSTOMIZATION}" \
  --namespace flux-system

Status prüfen:

flux get kustomization "${KUSTOMIZATION}" \
  --namespace flux-system

Die Kustomization muss als suspendiert angezeigt werden.

2. Cluster-Objekt exportieren

Das bestehende Cluster-Objekt als bereinigtes YAML exportieren:

kubectl -n "${NAMESPACE}" get cluster.postgresql.cnpg.io "${CLUSTER}" \
  -o yaml |
kubectl neat > "${RECOVERY_FILE}"

Falls kubectl neat nicht verfügbar ist, das Objekt zunächst vollständig exportieren:

kubectl -n "${NAMESPACE}" get cluster.postgresql.cnpg.io "${CLUSTER}" \
  -o yaml > "${RECOVERY_FILE}"

In diesem Fall vor dem erneuten Anwenden mindestens folgende serverseitig verwaltete Felder entfernen:

  • metadata.creationTimestamp

  • metadata.generation

  • metadata.resourceVersion

  • metadata.uid

  • metadata.managedFields

  • status

Die exportierte Datei dient als Grundlage für den Recovery-Cluster. Insbesondere die folgenden Einstellungen müssen aus dem bestehenden Objekt erhalten bleiben:

  • spec.instances

  • spec.imageName

  • spec.storage

  • Ressourcenanforderungen und Limits

  • Affinity und Scheduling

  • Monitoring

  • Backup-Plugins

  • weitere anwendungsspezifische Einstellungen

Die Storage-Konfiguration darf nicht aus einem generischen Beispiel übernommen werden. Größe, StorageClass und weitere Storage-Einstellungen müssen dem exportierten Cluster-Objekt entsprechen.

3. Recovery-Konfiguration eintragen

In der Receovery-Datei unter spec.bootstrap den vorhandenen Abschnitt initdb durch recovery ersetzen.

Zusätzlich spec.externalClusters ergänzen. Beispiel:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: cnpg-paperless-ngx
  namespace: paperless-ngx
spec:
  instances: 1
  imageName: ghcr.io/cloudnative-pg/postgresql:18-standard-trixie

  storage:
    size: 50Gi

  bootstrap:
    recovery:
      source: origin
      database: paperless
      owner: paperless

  externalClusters:
    - name: origin
      plugin:
        name: barman-cloud.cloudnative-pg.io
        parameters:
          barmanObjectName: cnpg-paperless-ngx
          serverName: cnpg-paperless-ngx

  plugins:
    - name: barman-cloud.cloudnative-pg.io
      isWALArchiver: true
      parameters:
        barmanObjectName: cnpg-paperless-ngx

Dabei gilt:

  • bootstrap.recovery.source muss dem Namen unter externalClusters entsprechen.

  • database und owner müssen der ursprünglichen initdb-Konfiguration entsprechen.

  • barmanObjectName muss auf das vorhandene Object-Store-Objekt verweisen.

  • serverName muss dem Namen entsprechen, unter dem die Sicherungen abgelegt wurden.

  • spec.storage muss vollständig aus dem ursprünglichen Cluster übernommen werden.

  • Der bestehende Abschnitt spec.plugins für die spätere Archivierung bleibt erhalten.

4. Cluster löschen

Das bestehende CloudNativePG-Cluster-Objekt löschen:

kubectl -n "${NAMESPACE}" delete cluster.postgresql.cnpg.io "${CLUSTER}"

Warten, bis alle zugehörigen Pods gelöscht wurden:

kubectl -n "${NAMESPACE}" wait \
  --for=delete pod \
  --selector="cnpg.io/cluster=${CLUSTER}" \
  --timeout=10m

Prüfen, ob noch zugehörige Pods oder PersistentVolumeClaims vorhanden sind:

kubectl -n "${NAMESPACE}" get pods \
  --selector="cnpg.io/cluster=${CLUSTER}"

kubectl -n "${NAMESPACE}" get pvc \
  --selector="cnpg.io/cluster=${CLUSTER}"

Mit dem nächsten Schritt erst fortfahren, wenn keine zum Cluster gehörenden Pods und PersistentVolumeClaims mehr vorhanden sind.

Falls PVCs noch vorhanden sind, deren Status und Finalizer prüfen:

kubectl -n "${NAMESPACE}" get pvc \
  --selector="cnpg.io/cluster=${CLUSTER}" \
  -o wide

kubectl -n "${NAMESPACE}" describe pvc \
  --selector="cnpg.io/cluster=${CLUSTER}"

Finalizer nicht ohne vorherige Prüfung der Ursache entfernen.

5. Recovery-Cluster anwenden

Die vorbereitete Cluster-Ressource anwenden:

kubectl apply -f "${RECOVERY_FILE}"

Cluster, Pods und PVCs beobachten:

kubectl -n "${NAMESPACE}" get \
  cluster.postgresql.cnpg.io,pods,pvc \
  --watch

Während der Wiederherstellung wird ein Full-Recovery-Pod gestartet. Dessen Logs können über das Cluster-Label verfolgt werden:

kubectl -n "${NAMESPACE}" logs \
  --selector="cnpg.io/cluster=${CLUSTER}" \
  --all-containers \
  --follow \
  --prefix

6. Recovery validieren

Warten, bis der Cluster den Status Ready erreicht:

kubectl -n "${NAMESPACE}" wait \
  --for=condition=Ready \
  cluster.postgresql.cnpg.io/"${CLUSTER}" \
  --timeout=30m

Anschließend prüfen:

kubectl -n "${NAMESPACE}" get \
  cluster.postgresql.cnpg.io "${CLUSTER}"

kubectl -n "${NAMESPACE}" get pods \
  --selector="cnpg.io/cluster=${CLUSTER}" \
  -o wide

kubectl -n "${NAMESPACE}" get pvc \
  --selector="cnpg.io/cluster=${CLUSTER}" \
  -o wide

Erwarteter Zustand:

  • Der CloudNativePG-Cluster ist Ready.

  • Die erwartete Anzahl Instanzen ist verfügbar.

  • Der primäre PostgreSQL-Pod ist Ready.

  • Alle benötigten PVCs sind Bound.

  • Die erwartete StorageClass und Storage-Größe werden verwendet.

  • In den Logs sind keine Recovery- oder PostgreSQL-Fehler vorhanden.

  • Die Anwendung kann auf die wiederhergestellte Datenbank zugreifen.

7. Bootstrap-Konfiguration zurücksetzen

Nach erfolgreicher Wiederherstellung das Cluster-Objekt bearbeiten:

kubectl -n "${NAMESPACE}" edit \
  cluster.postgresql.cnpg.io "${CLUSTER}"

Unter spec.bootstrap den Abschnitt recovery wieder durch die ursprüngliche initdb-Konfiguration ersetzen:

spec:
  bootstrap:
    initdb:
      database: paperless
      owner: paperless

Den vollständigen Abschnitt spec.externalClusters entfernen.

Die Änderung beeinflusst den bereits initialisierten Datenbank-Cluster nicht. Sie stellt den normalen deklarativen Zustand für spätere GitOps-Abgleiche wieder her.

8. Flux-Kustomization fortsetzen

Vor dem Fortsetzen sicherstellen, dass die Cluster-Definition im Git-Repository der normalen Konfiguration mit bootstrap.initdb entspricht.

Flux anschließend fortsetzen:

flux resume kustomization "${KUSTOMIZATION}" \
  --namespace flux-system

Der Restore ist abgeschlossen, wenn der CloudNativePG-Cluster und die verwaltende Flux-Kustomization den Status Ready besitzen.

Disaster Recovery

Bei einem Disaster Recovery kann der Namespace noch nicht vorhanden sein. In diesem Fall kann das bestehende Cluster-Objekt nicht exportiert werden.

Vor dem Erstellen des Recovery-Clusters müssen mindestens folgende Ressourcen vorhanden sein:

  • Ziel-Namespace

  • Barman-Object-Store-Objekt

  • vom Object Store referenziertes Secret

  • CloudNativePG- und Barman-Cloud-CRDs

  • erforderliche Operatoren und Plugins

Ressourcen prüfen:

kubectl get namespace "${NAMESPACE}"

kubectl -n "${NAMESPACE}" get objectstore

kubectl -n "${NAMESPACE}" get secret

Falls Namespace, Object Store oder Secret durch Flux bereitgestellt werden, zunächst die zuständige Kustomization abgleichen:

flux reconcile kustomization "${KUSTOMIZATION}" \
  --namespace flux-system \
  --with-source

Danach erneut prüfen:

kubectl get namespace "${NAMESPACE}"

kubectl -n "${NAMESPACE}" get objectstore

kubectl -n "${NAMESPACE}" get secret

Den Recovery-Cluster erst anwenden, wenn Object Store und Secret vorhanden sind.

Falls der ursprüngliche Cluster nicht mehr exportiert werden kann, die Cluster-Definition aus dem Git-Repository als Grundlage verwenden. Dort ausschließlich:

  • bootstrap.initdb durch bootstrap.recovery ersetzen

  • externalClusters ergänzen

Alle übrigen Einstellungen, insbesondere spec.storage, unverändert übernehmen.

Fehleranalyse

Recovery-Pod startet nicht

Prüfen:

kubectl -n "${NAMESPACE}" get events \
  --sort-by=.lastTimestamp

kubectl -n "${NAMESPACE}" describe \
  cluster.postgresql.cnpg.io "${CLUSTER}"

kubectl -n "${NAMESPACE}" get pods \
  --selector="cnpg.io/cluster=${CLUSTER}"

Typische Ursachen:

  • Object-Store-Objekt fehlt

  • referenziertes Secret fehlt

  • falscher barmanObjectName

  • falscher serverName

  • Backup ist nicht erreichbar

  • StorageClass ist nicht verfügbar

  • PVC kann nicht provisioniert werden

PVC wird nicht gelöscht

Den Zustand und vorhandene Finalizer prüfen:

kubectl -n "${NAMESPACE}" get pvc \
  --selector="cnpg.io/cluster=${CLUSTER}" \
  -o yaml

Vor manuellen Änderungen an Finalizern sicherstellen, dass kein Pod das Volume mehr verwendet und keine Storage-Operation mehr aktiv ist.

Flux stellt die Recovery-Konfiguration zurück

Prüfen, ob die Kustomization wirklich pausiert ist:

flux get kustomization "${KUSTOMIZATION}" \
  --namespace flux-system

Flux erst fortsetzen, nachdem bootstrap.initdb wiederhergestellt und externalClusters entfernt wurde.

Abschlusskontrolle

kubectl -n "${NAMESPACE}" get \
  cluster.postgresql.cnpg.io,pods,pvc

flux get kustomization "${KUSTOMIZATION}" \
  --namespace flux-system

Der Restore gilt als erfolgreich, wenn:

  • der CloudNativePG-Cluster Ready ist

  • der PostgreSQL-Pod betriebsbereit ist

  • alle PVCs Bound sind

  • die wiederhergestellten Daten verfügbar sind

  • bootstrap.initdb wieder konfiguriert ist

  • externalClusters entfernt wurde

  • Flux wieder aktiv und fehlerfrei ist