Dieser Blogpost der K8S-Reihe hat lange auf sich warten lassen. Das hat nicht nur damit zu tun, dass ich mit anderen Dingen beschäftigt war, sondern auch damit, dass Persistenz im Kubernetes-Cluster kein leichtes Thema ist. Seit ich begonnen habe, mich näher mit Kubernetes zu befassen, habe ich mich auch mit diesem Thema beschäftigt und immer wieder verschiedene Lösungen ausprobiert, ohne eine zu finden, mit der ich vollständig zufrieden gewesen wäre.

Einen systematischen Vergleich habe ich nie vorgenommen und auch selbst keine Performancebenchmarks durchgeführt. Allen, die sich eine erste Orientierung verschaffen möchten, kann ich den Blogpost “Storage on Kuberentes: OpenEBS vs. Rook (Ceph) vs Rancher Longhorn vs StorageOS vs Robin vs Portworx vs Linstor” von Vito Botta empfehlen.

Ursprünglich hatte ich geplant, auf Rook (Ceph) (https://rook.io) zu setzen und in einem Blogpost darauf einzugehen. Mit Rook habe ich das ein oder andere Testcluster aufgesetzt. Die ersten Versuche liegen inzwischen mehrere Jahre zurück und auch wenn Rook über die Zeit immer leichter zu verwenden wurde, ist es doch jedesmal wieder eine Herausforderung geblieben, ein Ceph-Cluster mit Rook zum Laufen zu bringen. Beruflich hatte ich dann viel mit Longhorn - eine der jüngsten Lösungen im Bereich der Kubernetes Storage-Lösungen - zu tun. Nachdem ich anfangs eher skeptisch war, hat mich die Einfachheit von Longhorn mittlerweile überzeugt, auch wenn der Funktionsumfang nicht mit Rook (Ceph) mithalten kann.

Im weiteren Blogpost werde ich nun hauptsächlich beschreiben, wie Longhorn aufgesetzt werden kann. Grundlage ist dabei ein Kubernetes-Cluster, dass so aufgesetzt wurde, wie ich es in den Blogposts der K8S-Reihe bisher beschrieben habe. Ich werde nicht im Detail auf Kuberentes Volumes, PersistenVolumeClaims, StorageClasses etc. eingehen. Diese grundlegenden Kubernetes-Resourcen und die Konzepte sind im Kapitel Storage der Kubernetes-Dokumentation (https://kubernetes.io/docs/concepts/storage/) zu finden.

Ich gehe nun zunächst auf die manuelle Installation von Longhorn per Helm ein, bevor ich beschreibe, wie das Setup mit GitOps aussieht.

Longhorn-Installation per Helm

Longhorn kann per Helm installiert werden.


Hetzner Volumes

Für Hetzner empfehle ich, Volumes zu erstellen und diese auf den Nodes zu mounten.

Meiner Erfahrung nach ist es nicht zu empfehlen, die Volumes unter /var/lib/longhorn zu mounten, sondern z.B. unter /export/longhorn.


# Add longhorn helm-repository
helm repo add longhorn https://charts.longhorn.io

# Update helm repository 
helm repo update

# Create namespace for longhorn
kubectl create namespace longhorn-system

# Install longhorn
helm install longhorn longhorn/longhorn --namespace longhorn-system

GitOps / flux

Zunächst erzeugen wir in unserem Flux GitOps repository einen Ordner für Longhorn.

# Create directory longhorn
mkdir infrastructure/longhorn

Danach fügen wir im kustomizations Manifest im Verzeichnis infrastructure unter resources die Zeile - ./longhorn ein.

Im infrastructure/sources-Verzeichnis legen wir die Datei longhorn.yaml mit dem HelmRepository-Kubernetes-Manifest an. Die Datei sollte folgenden Inhalt enthalten.

apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: HelmRepository
metadata:
  name: longhorn
spec:
  interval: 30m
  url: https://charts.longhorn.io

Nun legen wir die Dateien infrastructure/longhorn/namespace.yaml und infrastructure/longhorn/release.yaml an.

apiVersion: v1
kind: Namespace
metadata:
  name: longhorn
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: longhorn
spec:
  releaseName: longhorn
  chart:
    spec:
      chart: longhorn
      sourceRef:
        kind: HelmRepository
        name: longhorn
        namespace: flux-system
      version: v1.2.0
  interval: 1h0m0s
  install:
    remediation:
      retries: 3
  values:

Jetzt benötigen wir noch die Datei kustomization.yaml, die namespace.yaml und release.yaml referenziert.

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: longhorn
resources:
- namespace.yaml
- release.yaml

Konfiguration der Disks

Wie bereits oben im Infoblock zu Hetzner erwähnt, empfehle ich nicht die standardmäßig konfigurierten Longhorn-Disks mit dem Pfad /var/lib/longhorn zu verwenden.

Am Besten sollte für Longhorn eine seperate Festplatte/Dateisystem eingebunden werden z.B. unter dem Pfad /export/longhorn

Nach der Installation von Longhorn kann dann per Longhorn UI (siehe unten) pro Node eine neue Longhorn-Disk mit dem entsprechenden Pfad angelegt werden.

Infos dazu gibt es in der Longhorn-Dokumentation.

Longhorn Grundlagen

Im Folgenden dokumentiere ich nun noch einige Hinweise zur Nutzung der Longhorn-UI und zu Backups.

Longhorn UI

Die einfachste Möglichkeit, um auf das Longhorn UI zuzugreifen, ist ein Port-Forwarding per kubectl port-forward --namespace longhorn-system svc/longhorn-frontend 8080:80.

Longhorn Backup

Die Stärke von Longhorn ist die (z.B. im Vergleich zu Rook) einfachere Handhabung. Diese erreicht Longhorn zum Großteil dadurch, dass die Entwickler sich darauf konzentriert haben, die Kernfeatures möglichst einfach umzusetzen und sich auf wenige Optionen zu beschränken.

Longhorn unterstützt z.B. lediglich S3 und NFS als Backupspeicher.

Longhorn Dokumentation

Die Longhorn Dokumentation ist auch aufgrund des aus meiner Sicht bisher geglückten Versuchs, Longhorn so einfach wie möglich zu halten, relativ übersichtlich.

Wichtige Informationen z.B. zum Upgrade einer Installation, zur Verschlüsselung von Volumes, zu Backups und zum Monitoring sind schnell zu finden und die jeweils erforderlichen Schritte sind nachvollziehbar beschrieben.

Die Dokumenation befindet sich unter https://longhorn.io/docs/.

Im nächsten Blogpost werde ich das Thema Datenbanken im Kubernetes-Cluster zum Thema machen. Gemäß des Fahrplans, den ich im April 2020 aufgestellt hatte, war geplant in diesem Zusammenhang auf KubeDB einzugehen. Leider hat AppsCode den Funktionsumfang der Community-Edition stark eingeschränkt.

Ich habe mich daher entschieden, nicht weiter auf KubeDB zu setzen, und werde den nächsten Blogpost vermutlich eher kurz halten und mich auf einen Ansatz mit Helm für PostgreSQL beschränken.