Best Practices für Backup & Restore von ETCD!

Schnell ist es passiert und ein Upgrade des Kubernetes-Clusters führt zum Worst Case. Die mühselig erstellten Deployments, Pods und Services sind verschwunden. Jetzt ist guter Rat teuer. Am einfachsten lässt sich so ein Fauxpas beseitigen, indem man alle K8S-Objekte wieder einspielt. Doch wie ist das möglich? Welche Art von Sicherung braucht man dafür und wie läuft das Prozedere ab?

Eine ganze Menge an Fragen, die ich in diesem Artikel beantworten werde. Eines aber gleich vorweg. Das Zauberwort lautet ETCD-Backup. In diesem Key-Value-Store stecken nämlich alle Konfigurationen deines Kubernetes-Clusters. Neben der klassischen ETCD-Sicherung via Snapshot, werde ich dir auch noch einen Geheimtrick zeigen, für Managed-Cluster ohne direkten Zugriff auf ETCD.

In Zeiten von zustandslosen Containern und GitOps werden Sicherungskopien immer stiefmütterlicher behandelt. Doch was machst du, wenn imperative Änderungen getätigt worden sind und eine Applikation nach dem Restore gar nicht mehr hochkommt? Jetzt ist Troubleshooting angesagt! Doch soweit hätte es nicht kommen müssen. Denn ETCD-Snapshots sind schnell und einfach angelegt.

Was genau ist ETCD in K8S?

Eine ETCD-Sicherung ist von entscheidender Bedeutung, da es die Datenintegrität und Kontinuität deines Kubernetes-Clusters sicherstellt. Das hört sich zwar toll an, aber was genau wird denn nun gespeichert? Das Beantworten dieser Frage ist gar nicht so leicht. Ganz grob kann man sagen, dass die Cluster-Konfiguration, alle Ressourcen-Konfigurationen sowie die Ressourcen-Historie gesichert wird.

Um ganz genau zu wissen, was ein ETCD-Backup alles abdeckt, sollten wir zunächst verstehen, was ETCD überhaupt ist und welche Daten darin abgelegt werden. Eines kann ich an dieser Stelle aber schon mal verraten, deine Container-Volumes musst du separat sichern. Das sollte aber auch jedem klar sein. Nichtsdestoweniger möchte ich dir kurz erklären, wie ETCD funktioniert.

Es handelt sich hierbei um einen persistenten und hochverfügbarer Key-Value-Store, der von Kubernetes zur Speicherung von Konfigurationsdaten, Statusinformationen und anderen wichtigen Informationen verwendet wird. ETCD fungiert daher als das Herzstück eines jeden Kubernetes-Clusters und ist verantwortlich für die Speicherung aller Konfigurationen.

In den gängigen K8S-Distributionen läuft ETCD als Pod auf allen Master-Nodes. Man kann den Dienst aber auch auf einem dedizierten Linux-Node installieren oder gleich ein richtiges Cluster aufsetzen. Wichtig zu wissen ist hierbei nur, dass alle ETCD-Member den gleichen Datenbestand haben und man einen Restore daher nur einmal durchführen muss. Die Daten werden dann eh synchronisiert.

Was deckt ein ETCD-Backup ab?

Enthalten sind alle wesentlichen Informationen, die zur ordnungsgemäßen Funktion und Wiederherstellung eines Kubernetes-Clusters erforderlich sind. So eine Sicherung gewährleistet die Wiederherstellung und die Kontinuität des Betriebs, selbst in Fällen von Hardwareschäden, menschlichen Fehlern oder anderen unvorhergesehenen Ereignissen.

Folgende Datentypen sind in einem solchen Snapshot enthalten:

  • Cluster-Konfiguration: Dies umfasst alle Informationen über die Konfiguration des Kubernetes-Clusters, einschließlich der API-Server-Endpunkte, Authentifizierungseinstellungen, Zertifikate und mehr. Ein Backup stellt sicher, dass diese Konfigurationen im Falle eines Ausfalls oder Datenverlusts wiederhergestellt werden können.
  • Ressourcenstatus: Ein ETCD-Backup enthält Informationen über den aktuellen Zustand aller im Cluster vorhandenen Ressourcen. Dies umfasst Details zu Pods, Services, ReplicaSets, Deployments und anderen Objekten. Durch das Wiederherstellen eines Backups können verlorene oder beschädigte Ressourcen rekonstruiert werden.
  • Service Discovery: Informationen über Dienste und deren Endpunkte werden ebenfalls gesichert. Dies ermöglicht es, die Kommunikation zwischen den Diensten im Cluster nach einem Ausfall wiederherzustellen.
  • Historische Daten: Ein Backup kann auch historische Daten und Änderungen umfassen, die im Laufe der Zeit in ETCD vorgenommen wurden. Dies ermöglicht es, auf vorherige Zustände und Konfigurationen zurückzugreifen, was besonders nützlich sein kann, wenn Probleme auftreten und eine Rückverfolgung von Änderungen erforderlich ist.

Wie gelingen Backup & Restore?

ETCD Backup & Restore

Geht es um das Thema ETCD-Backup liest und hört man immer etwas von einem Snapshot. Meistens ist damit das Build-in-Feature des Key-Value-Stores gemeint. Also das Anfertigen einer Sicherung mittels etcdctl im Terminal. Läuft ETCD allerdings in einem Pod muss man erstmal noch etwas Software auf dem Master nachinstallieren. Das geht bei Debian ganz einfach mithilfe des Paketmanagers.

Bei Red Hat hingegen muss man das Binary herunterladen und einrichten. Wirklich notwendig ist dieser Aufwand aber nicht. Schließlich besteht auch die Möglichkeit im Storage-Backend ein Backup des Volumes eines ETCD-Pods zu erstellen. Aber wann trifft man schon mal auf eine solche Konfiguration? Eher selten. Aber keine Sorge, es geht noch um ein Vielfaches simpler.

Man kann nämlich alle Daten aus dem ETCD-Speicher auslesen, indem man den Kube-API-Pod einfach danach fragt. Gerade in kleinen Clustern mit geringem Workload klappt das überraschend gut. Noch dazu ist der Restore enorm simpel, man lässt einfach die ganze Backup-Datei durch die API einlesen. Wer bei einem Managed-Cluster zudem gar keinen Zugriff auf ETCD hat, kann nur so vorgehen.

API-Abfrage & Umleitung in eine Datei:

Die einfachste Methode für ein ETCD-Backup ist es, das Kommandozeilenwerkzeug kubectl zu benutzen. Man kann nämlich alle Manifeste auf einmal ausgeben lassen. Wer den Output des Befehls direkt in eine Datei umleitet, erhält eine vollständige und noch dazu von Menschen lesbare Sicherung. Angefertigt wird die Sicherung mithilfe dieses kleinen Kommandos:

kubectl get all --all-namespaces > </Pfad/zur/Backup-Datei.yaml>

Möchte man die Anfertigung des Backups automatisieren, genügt es schon den obigen Befehl als Cronjob täglich auszuführen. Natürlich kann man auch ein kleines Skript schreiben, welches bei jedem Lauf des Befehls eine neue Backup-Datei generiert und zu alte Sicherungen automatisch entfernt. Die Arbeit brauchst du dir aber gar nicht zu machen. Ich habe selbst so ein Skript im Einsatz:

#!/bin/bash

# Verzeichnis für die Backups:
backup_dir="/backups/all-objects"

# Anzahl der zu behaltenden Dateien:
max_files=100

# Aktuelles Datum und Uhrzeit im Format Jahr-Monat-Tag-Stunde-Minute-Sekunde:
timestamp=$(date +'%Y-%m-%d-%H-%M-%S')

# Vollständiger Pfad zur Backup-Datei:
backup_file="${backup_dir}/everything-${timestamp}.yaml"

# Befehl zum Erstellen des Backups:
kubectl get all --all-namespaces -o yaml > "$backup_file"

# Liste der vorhandenen Backup-Dateien:
backup_files=("$backup_dir"/*)

# Anzahl der vorhandenen Backup-Dateien:
num_files=${#backup_files[@]}

# Wenn die Anzahl der Dateien die maximale Anzahl überschreitet, älteste Dateien löschen:
if [ "$num_files" -gt "$max_files" ]; then
  # Sortiere die Dateien nach Datum aufsteigend
  mapfile -t sorted_files < <(printf '%s\n' "${backup_files[@]}" | sort)

  # Anzahl der Dateien, die gelöscht werden müssen:
  files_to_delete=$((num_files - max_files))

  # Lösche die ältesten Dateien:
  for ((i = 0; i < files_to_delete; i++)); do
    rm "${sorted_files[$i]}"
  done
fi

Das Skript kannst du nun unter /usr/local/bin abspeichern und mittels chmod +x ausführbar machen. Ein Cronjob ist ebenfalls schnell angelegt. Der Befehl crontab -e erlaubt es dir, einen neuen Cronjob zu hinterlegen. Nutze dafür einfach die folgende Zeile: 0 5 * * * /usr/local/bin/<dein-backup-skript.sh> Damit wird jeden Morgen um 5 Uhr das Sicherungsskript ausgeführt.

Wiederherstellung der YAML-Daten:

Der Restore ist tatsächlich unkomplizierter als das Erstellen des Backups selbst. De facto ist der entsprechende Befehl dafür sogar noch viel kürzer als man denken könnte. Schließlich musst du die generierte YAML-Datei nur mit dem Kommandozeilen-Werkeug kubectl einlesen lassen und den Rest erledigt dann Kubernetes für dich. Lange Rede, kurzer Sinn hier kommt der Befehl:

kubectl apply -f </Pfad/zur/Backup-Datei.yaml>

ETCD-Snapshot mittels etcdctl erzeugen:

Die wohl bekannteste und zuverlässigste Methode, um eine Sicherung von ETCD anzufertigen, ist der Einsatz von etcdctl. Damit lässt sich binnen weniger Sekunden ein Snapshot des Key-Value-Stores anfertigen. Allerdings benötigt man dafür das Kommandozeilenwerkzeug etcdctl, welches bei Debian und seinen Derivaten im Paketmanager als etcd-client enthalten ist.

Wer hingegen auf Red Hat oder dessen freie Pendants setzt, muss selbst tätig werden und einen passenden Release von Github herunterladen und installieren. Das Paketverwaltungssystem findet bei der Suche nach ETCD nämlich rein gar nichts. Der Befehl ist gar nicht so komplex. Man muss nur die IP-Adresse des ETCD-Servers angeben und die nötigen Zertifikatsdateien hinterlegen:

ETCDCTL_API=3 etcdctl --endpoints=<https://IP-Adresse>:2379 \
  --cacert=<Trusted-Ca-Datei> --cert=<Cert-Datei> --key=<Key-Datei> \
  snapshot save </Pfad/zum/Backup-Speicherort.db>

Arbeitet man auf einem Master-Node von einem Kubeadm-Cluster ist das besonders einfach. Hier trägt man einfach localhost ein und gibt dann noch die lokal vorhandenen Cert-Files an. Die benötigten Pfade lassen sich mithilfe von kubectl describe pod etcd-<Hostname> ermitteln. In der Ausgabe braucht man dann lediglich einen genaueren Blick auf die Command-Sektion zu werfen.

Manch einer wird sich jetzt die berechtigte Frage stellen, warum diese Pfade auf dem Controlplane vorhanden sind. Schließlich wird hier nur die ETCD-Software für den Betrieb im Container konfiguriert. Das Zauberwort lautet hostPath. Wer die Volumes-Sektion genau anschaut, wird feststellen, dass die ganzen Zertifikats-Dateien im Host-Verzeichnis /etc/kubernetes/pki/etcd gespeichert werden.

Und der ETCD-Pod hängt dieses Volume genau im gleichen Pfad ein. Im Manifest gibt es nämlich noch den Bereich volumeMounts mit der Angabe von mountPath: /etc/kubernetes/pki/etcd. Warum bin ich so genau auf das Aufspüren der Cert-Files eingegangen? Die Antwort ist simpel, es gibt ja nicht nur Kubeadm-Cluster auf dem Planeten und der Snapshot-Befehl braucht die Dateien zwingend.

Einspielen des ETCD-Snapshots:

Die Restore-Prozedur ist hier ebenfalls recht simpel und funktioniert überaus zuverlässig. Nicht zuletzt, da man auch alte Snapshots recht problemlos einspielen kann. Die Wiederherstellung eines Backups aus einer anderen ETCD-Patch-Version wird nämlich ganz offiziell unterstützt. Aber genug herumgelabert. Letztendlich braucht man für das ETCD-Recovery nur einen einzigen CLI-Befehl:

ETCDCTL_API=3 etcdctl --endpoints <https://IP-Adresse>:2379 \
snapshot restore </Pfad/zum/Backup.db>

Ich gehe bei meinen Kubeadm-Clustern allerdings etwas anders vor, und zwar stelle ich die Datenbank in einen neuen Ordner auf dem Hostsystem her. Ich möchte die vorhandenen ETCD-Daten nämlich nicht unwiderruflich überschreiben. Dafür muss man den obigen Befehl etwas abändern. Entfernt wird die Angabe des API-Endpoints und neu dazu kommt der Parameter –data-dir.

ETCDCTL_API=3 etcdctl snapshot restore \ 
--data-dir </neuer/ETCD-Ordner/auf/dem/Host> </Pfad/zum/Backup.db>

Der neue Pfad muss dem ETCD-Pod aber noch bekannt gemacht werden. Dafür muss man erstmal wissen, dass es sich hier um einen statischen Pod handelt. Zumindest dann, wenn Kubeadm zum Einsatz kommt. Zu diesem Zweck suche ich erstmal den Speicherort der Manifeste für statische Pods auf. Standardmäßig befinden sich diese auf einem Master im Verzeichnis /etc/kubernetes/manifests.

Allerdings muss das nicht immer der Fall sein. Aber keine Sorge, der Speicherort lässt sich rasch ermitteln. Da statische Pods immer vom Kubelet selbst verwaltet werden, findet man die Pfadangabe natürlich in dessen Konfigurationsdatei. Bevor du nun die ganze Konfiguration durchsuchst, kannst du einfach diesen Befehl verwenden: grep staticPodPath /var/lib/kubelet/config.yaml

Im richtigen Ordner angekommen, passe ich nun die Datei etcd.yaml an. Da ist zum Glück auch gar nicht viel zu tun. Lediglich ein Eintrag unter Volumes oder genauer gesagt unter HostPath muss abgeändert werden. Die Rede ist natürlich vom Verweis auf den lokalen Ordner mit den ETCD-Daten. Hier steht natürlich noch der alte Pfad drin, welchen wir gegen den Neuen tauschen:

 - hostPath:
      path: </var/lib/neues-etcd-verzeichnis>
      type: DirectoryOrCreate
    name: etcd-data

Sobald die Datei erfolgreich editiert wurde, war es das auch schon mit den Doings. Das Kubelet bemerkt binnen weniger Sekunden, dass sich hier etwas geändert hat und rollt den Pod entsprechend neu aus. Manchmal ist es aber notwendig den Kubelet-Service neu zu starten oder den ETCD-Pod selbst noch einmal zu killen. Danach sollte aber alles wieder ordnungsgemäß funktionieren.

Von Fabian Wüst

Er ist leidenschaftlicher Open-Source-Benutzer und ein begeisterter Technologie-Enthusiast. Als kreativer Kopf hinter Homelabtopia bringt Fabian hier seine umfangreiche Erfahrung als Linux-Admin ein. Um sicherzustellen, dass du aus seinen Beiträgen den größtmöglichen Nutzen ziehen kannst, führt er ausgiebige Tests durch und errichtet dafür immense Setups.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert