Clusterward
← Zurück zum Blog
ArchitekturAktualisiert Florian Apel

Persistente Volumes auf Kubernetes: ReadWriteOnce, Recreate und der Snapshot vor dem Löschen

Ein Rolling Update mit einem Volume ist ein Deadlock mit Ansage. Und ein Volume aus dem Manifest zu löschen ist ein Datenverlust mit Ansage. Fünf Regeln, die beides verhindern.

Titelbild: Persistente Volumes auf Kubernetes: ReadWriteOnce, Recreate und der Snapshot vor dem Löschen

Stateless ist das Ideal, aber die meisten Anwendungen haben irgendwo Dateien: Uploads eines CMS, ein Suchindex, ein Cache, eine kleine SQLite. Auf Kubernetes heißt das ein PersistentVolumeClaim auf Block Storage. Das funktioniert gut, wenn man fünf Eigenschaften von Block Storage respektiert, die Kubernetes einem nicht abnimmt.

Regel 1: ReadWriteOnce heißt genau ein Node

Scaleway Block Storage ist ein Datenträger, der an einer virtuellen Maschine hängt. Kubernetes nennt das ReadWriteOnce: Ein Volume kann zur selben Zeit an einem Node eingehängt sein. Zwei Pods auf zwei Nodes können es nicht teilen. Wer drei Replikas mit demselben Volume startet, bekommt eine laufende und zwei mit Multi-Attach error.

Konsequenz: Ein Service mit Volume hat genau eine Instanz. Kein Autoscaler, keine feste Zahl größer eins. Wer mehrere Instanzen mit gemeinsamen Dateien braucht, braucht Object Storage oder ein Netzwerk-Dateisystem, nicht Block Storage.

Regel 2: Recreate statt RollingUpdate

Die Standard-Strategie eines Deployments ist RollingUpdate: Der neue Pod startet, während der alte noch läuft. Mit einem ReadWriteOnce-Volume ist das ein Deadlock. Der neue Pod wartet auf das Volume, das der alte noch hält, und der alte wird erst beendet, wenn der neue bereit ist. Das Deployment bleibt für immer bei "1 von 1 aktualisiert, 0 bereit".

Die Lösung ist strategy: Recreate: Der alte Pod wird beendet, das Volume freigegeben, der neue Pod startet. Das kostet einige Sekunden Ausfall pro Rollout. Für eine Anwendung mit einer Instanz ist das ohnehin der Fall.

Regel 3: Undeploy löscht kein Volume

Ein Claim ist ein eigenes Objekt. Wer ein Deployment löscht, löscht den Claim nicht, und das ist richtig so. Wer den Claim löscht, überlässt den Datenträger der Reclaim-Policy der Storage-Klasse, und die steht meistens auf Delete. Der Datenträger ist dann weg, sofort, ohne Rückfrage.

Deshalb gehört ein Volume nie in denselben Löschweg wie der Workload. Undeploy entfernt Deployment, Service, Ingress und Secrets. Der Claim bleibt, bis jemand ausdrücklich die Daten löscht, mit Bestätigung, und nicht, weil er eine Zeile aus einem Manifest entfernt hat.

Regel 4: Vor dem Löschen ein Snapshot, der fertig ist

Block Storage kann Snapshots: eine Kopie des Datenträgers zu einem Zeitpunkt, unabhängig vom Node, wiederherstellbar als neuer Datenträger. Ein Snapshot vor jedem Löschen ist der Unterschied zwischen "die Daten sind weg" und "die Daten lassen sich zurückholen". Zwei Details, die zählen:

  • Der Snapshot muss den Zustand available erreicht haben, bevor der Claim gelöscht wird. Ein angestoßener Snapshot ist kein Snapshot.
  • Scheitert der Snapshot, wird nicht gelöscht. Abbruch statt Überspringen.

Dazu ein Zeitplan: täglich oder wöchentlich ein Snapshot, mit einer Aufbewahrung in Tagen, und eine Automatik, die nur die Snapshots löscht, die sie selbst angelegt hat. Manuell angelegte Snapshots in der Konsole bleiben unangetastet.

Regel 5: Größe wächst nur, Klasse bleibt

Ein Datenträger lässt sich vergrößern, nicht verkleinern. Und die Storage-Klasse eines Claims ist nach dem Anlegen fest. Beides sollte ein Werkzeug erzwingen, statt es dem Anwender zu überlassen: Ein Claim mit kleinerer Größe im Manifest wird abgelehnt, eine andere Klasse ebenso. Wer wirklich verkleinern will, legt ein neues Volume an, kopiert und löscht das alte über den geschützten Weg.

Was hat fsGroup damit zu tun?

Ein Datenträger wird mit Root-Rechten eingehängt. Ein Container, der als Nicht-Root läuft, kann dann nicht schreiben. fsGroup im Pod-SecurityContext setzt die Gruppe des Mounts, und Kubernetes passt die Rechte beim Einhängen an. Das ist der übliche Grund für "Permission denied" beim ersten Start einer Anwendung mit Volume, und er hat nichts mit dem Datenträger zu tun.

Wie stellen Sie ein Volume wieder her?

Aus einem Snapshot entsteht ein neuer Datenträger, der den alten hinter dem Claim ersetzt. Von Hand heißt das: den Pod stoppen, in der Scaleway-Konsole aus dem Snapshot einen Datenträger erzeugen, das PersistentVolume austauschen und den Claim neu binden. Drei Regeln gehören dazu. Erstens den Service vorher stoppen, damit niemand auf den alten Datenträger schreibt. Zweitens den aktuellen Stand vorher selbst als Snapshot sichern, falls der gewählte Snapshot der falsche war. Drittens erst danach wieder starten.

In Clusterward ist das ein Klick: "Wiederherstellen…" am Snapshot auf der Volumes-Karte stoppt den Service, sichert den aktuellen Stand als zusätzlichen Snapshot, ersetzt den Datenträger und startet den Service wieder, mit dem Fortschritt auf der Karte. Wiederherstellen bleibt trotzdem ein bewusster Schritt und kein automatischer Rückfall, beschrieben unter Volumes & Snapshots.

Welche Anwendungen brauchen wirklich ein Volume?

Nicht jede Datei braucht Block Storage. Die Faustregel: Ein Volume für Daten, die eine einzelne Instanz lokal und schnell braucht. Object Storage für Daten, die mehrere Instanzen oder mehrere Anwendungen teilen. Eine Managed-Datenbank für alles, was Abfragen braucht.

Daten

Richtiger Ort

Warum

Uploads eines CMS

Object Storage

Mehrere Instanzen, direkte Auslieferung, Backup als Objekte

Suchindex

Volume

Lokal, schnell, eine Instanz, neu aufbaubar

SQLite einer kleinen App

Volume

Eine Instanz, Snapshot als Backup

Sessions und Caches

Kein Volume

Flüchtig, im Speicher oder in Redis

Kundendaten mit Abfragen

Managed-Datenbank

Rollen, Backups, Import, kein Multi-Attach-Problem

Viele Anwendungen, die "ein Volume brauchen", brauchen in Wahrheit einen S3-Adapter für Uploads. Der ist meist eine Konfigurationszeile und macht die Anwendung horizontal skalierbar.

Snapshots sind kein Backup, bis man sie getestet hat

Ein Snapshot ist eine Kopie des Datenträgers, konsistent auf Blockebene. Für eine Anwendung, die gerade schreibt, kann das eine halb geschriebene Datei bedeuten. Für einen Suchindex ist das egal, er wird neu aufgebaut. Für eine SQLite kann es ein beschädigter Zustand sein. Die Anwendung sollte vor dem Snapshot ruhig sein, oder das Dateisystem und die Datenbank müssen mit halben Schreibvorgängen umgehen können; SQLite mit WAL-Modus kann das.

Und wie beim Offboarding gilt: Ein Snapshot, aus dem noch nie jemand wiederhergestellt hat, ist eine Hoffnung. Einmal im Quartal aus dem letzten Snapshot einen Datenträger erzeugen, an einen Testpod hängen, Dateien prüfen.

Wie Clusterward das umsetzt

Ein Service mit Volumes bekommt automatisch Recreate und eine Instanz, der Claim wird vor dem Deployment angelegt und überlebt jedes Undeploy. Löschen geht nur über einen eigenen Weg mit Eintippen des Namens, und erst nach einem Snapshot, der bereit ist. Snapshots laufen nach Zeitplan mit Aufbewahrung; gelöscht wird nur aus der eigenen Liste. Größe nur nach oben, Klasse fest, Entfernen aus der Liste abgelehnt. Details unter Volumes und Snapshots.

Fazit

Volumes auf Kubernetes sind sicher, wenn fünf Regeln gelten: eine Instanz, Recreate, Claim überlebt Undeploy, Snapshot vor dem Löschen, nur wachsen. Jede Regel steht für einen Datenverlust, den jemand schon hatte. Gießen Sie sie in ein Werkzeug, bevor Sie ihn auch haben.

Daten auf Kubernetes sicher ablegen? Schreiben Sie uns, welche Anwendung Dateien auf der Platte braucht. Wir zeigen, wie Volume, Snapshots und Wiederherstellung dafür aussehen. Frage zu Volumes stellen →

Quellen und weiterführende Links

Häufige Fragen

  • Weil ein ReadWriteOnce-Volume nur an einem Node eingehängt sein kann. Der neue Pod wartet auf das Volume, das der alte noch hält, und der alte wird erst beendet, wenn der neue bereit ist. Die Lösung ist strategy: Recreate; das kostet einige Sekunden Ausfall pro Rollout.