Ein Restore ist der eine Moment, in dem man garantiert unter Stress steht: irgendwas ist kaputt, und ausgerechnet jetzt soll man sich die Reihenfolge aus Flux-suspend, PVC-leeren, restic restore und Flux-resume fehlerfrei aus dem Kopf klicken. Genau diese Reihenfolge gehört nicht in den Kopf, sondern in Code. Im go-task-Setup des Labs
übernimmt das die backup-Kategorie — ein dünner Wrapper um
VolSync
und
restic
, der aus dem VolSync-Aufbau
ein paar einprägsame Kommandos macht.
Drei Befehle für den Alltag#
Der harmlose Teil zuerst. Snapshots ansehen:
1task backup:list APP=snac NS=fediverse-system
Das startet einen ephemeren Job (restic:0.18.1 snapshots, der das ${APP}-volsync-secret als envFrom zieht), wartet auf dessen Abschluss, gibt die Logs aus und räumt sich selbst wieder weg. Einen Snapshot sofort erzwingen — etwa direkt vor einem riskanten Upgrade:
1task backup:snapshot APP=snac NS=fediverse-system
Hier wird die ReplicationSource mit trigger.manual=<timestamp> gepatcht und auf den volsync-src-<app>-Job gewartet. Beide Tasks brauchen nur APP (und einen KUBE_CONTEXT); NS defaultet auf default, und ob die App ein Deployment oder ein StatefulSet ist, ermittelt der Task selbst.
Der gefährliche Teil: restore#
Ein Restore ist kein einzelner Aufruf, sondern eine Kette — und jede Stufe kann man von Hand vergessen oder vertauschen:
1task backup:restore APP=snac NS=fediverse-system PREV=2
2 │
3 ├─ _label ns -> pod-security: privileged (nur falls nicht ohnehin privileged)
4 ├─ _suspend flux suspend ks/snac + scale deploy|sts -> 0 (auf Pod-Delete warten)
5 ├─ _wipe Job (busybox, privileged): cd /data && find . -delete
6 ├─ _restore ReplicationDestination -> restic (copyMethod: Direct, previous: 2)
7 ├─ _resume flux resume ks/snac
8 └─ _unlabel ns -> pod-security zurücksetzen
Der Clou steckt darin, dass der Task die Parameter des Restores nicht erfindet, sondern aus der vorhandenen ReplicationSource ausliest: den Quell-PVC (CLAIM) und die moverSecurityContext-UID/GID. So schreibt der Restore-Mover mit exakt der uid:gid der App zurück in genau die Live-PVC, an der die App später wieder hochkommt. PREV steuert, wie viele Snapshots zurück restauriert wird (0 = neuester) — direkt durchgereicht an previous im ReplicationDestination:
1kind: ReplicationDestination
2spec:
3 trigger: { manual: restore-once }
4 restic:
5 repository: "${APP}-volsync-secret"
6 destinationPVC: "$CLAIM"
7 copyMethod: Direct # schreibt direkt in die Live-PVC
8 previous: $PREV
Weil copyMethod: Direct direkt auf das Live-Volume schreibt, ist die Reihenfolge nicht optional: erst die App stoppen (_suspend), dann das Volume leeren (_wipe), dann restaurieren. Würde man bei laufender App restaurieren, kämpften App und Mover um dieselben Dateien.
Der Pod-Security-Trick#
Der wipe-Job ist bewusst brachial — eine busybox, die find . -delete auf dem gemounteten PVC ausführt, und dafür privileged: true braucht:
1command: ["/bin/sh", "-c", "cd /data; find . -delete"]
2securityContext:
3 privileged: true
In einem Namespace mit strikten
Pod Security Standards
würde dieser Pod schlicht nicht zugelassen. Deshalb labelt _label den Namespace temporär auf pod-security.kubernetes.io/enforce=privileged — aber nur, wenn er nicht ohnehin schon privileged ist (sonst bliebe das Label nach dem _unlabel fälschlich hängen). Nach erfolgreichem Restore setzt _unlabel die ursprüngliche Posture wieder. Das ist die operative Kehrseite der privileged-movers-Notiz aus dem VolSync-Artikel
: Mover und wipe-Job brauchen kurz mehr Rechte, als die App selbst je bekommen darf.
Wenn das Repo klemmt: unlock#
Stirbt ein Mover mitten im Lauf, hinterlässt restic ein stehengebliebenes Lock — und jeder folgende Snapshot oder Restore hängt. Dafür gibt es den Notnagel:
1task backup:unlock APP=snac NS=fediverse-system
Ein Job mit restic unlock --remove-all, mehr nicht. Aber genau die Art Einzeiler, an die man sich um drei Uhr nachts nicht erinnert.
Kein Hexenwerk — und genau das ist der Punkt. Der Unterschied zwischen „ich tippe sechs kubectl- und flux-Kommandos in der richtigen Reihenfolge" und „ich tippe task backup:restore" ist im Ernstfall der Unterschied, der zählt. Eine Restore-Prozedur, die man nicht regelmäßig übt, ist im Zweifel keine — als Task ist sie wenigstens reproduzierbar.
