2 Punkte von GN⁺ 2025-05-06 | 1 Kommentare | Auf WhatsApp teilen
  • Auf persönlichen Servern und kleinen VPS ist die deklarative Automatisierung von Kubernetes zwar attraktiv, aber CPU- und Speicherlast sowie die betriebliche Komplexität können den tatsächlichen Nutzen übersteigen
  • Kubernetes automatisiert Dinge wie die Abstimmung von Pods und die Erneuerung von TLS-Zertifikaten, indem es fortlaufend den gewünschten Zustand herstellt, hält dafür aber ständig eine recht große Runtime am Laufen
  • Bei Experimenten mit Azure Kubernetes Service, Microk8s, K3S und Raspberry Pi waren Leerlauf-Ressourcenverbrauch, Hitzeentwicklung und Lüftergeräusche wiederholt ein Problem
  • Podman kann Container in systemd-Dienste verwandeln und mit io.containers.autoupdate sowie podman auto-update neue Images erkennen und ersetzen
  • Die Kombination aus Podman, systemd und User-Lingering bietet den Großteil der gewünschten Kubernetes-Automatisierung deutlich einfacher, allerdings verlagert sich die systemd-Integration von Podman in Richtung Quadlet

Warum die Kubernetes-Automatisierung für persönliche Server zu schwergewichtig war

  • Kubernetes besteht aus mehreren Komponenten, Web-Services, Sidecars und Webhooks, aber das Kerngeschehen ähnelt eher einer Schleife, die fortlaufend Ist-Zustand und Soll-Zustand vergleicht und Unterschiede anwendet
    • Wenn ein Pod vorhanden sein sollte, aber fehlt, wird er erstellt
    • Wenn es 3 Replikate geben sollte, aber 4 existieren, wird eines entfernt
  • Dieses Modell war besonders bei Erweiterungen wie cert-manager nützlich
    • Es wird deklariert, dass für eine bestimmte Domain ein gültiges TLS-Zertifikat vorhanden sein muss
    • Wenn festgelegt ist, wie das Zertifikat angefordert wird, beschafft das System ein neues Zertifikat und installiert es im Webserver, sobald keines vorhanden ist oder das Ablaufdatum naht
  • Für persönliche Experimente war das interessant und lehrreich, für den tatsächlichen Betrieb aber eher ein überdimensioniertes Werkzeug
  • Die Ressourcenlast zeigte sich in mehreren Umgebungen immer wieder
    • Auf einem NUC lief der Rechner ständig weiter, wurde heiß und erzeugte Lüftergeräusche, sodass Schlafen schwierig war
    • In Azure Kubernetes Service belegte die Kubernetes-Implementierung viel RAM und nutzte auf Worker-Nodes im Leerlauf etwa 7–10 % CPU
    • Eine einzelne Microk8s-Instanz auf einem x86_64-VPS mit 2 vCPU lag im Leerlauf bei etwa 12 % CPU
    • K3S auf einer Ampere-A1-Maschine mit 2 vCPU gilt zwar als leichter, verbrauchte aber dennoch rund 6 % CPU dauerhaft
    • Auch auf dem Raspberry Pi wurde keine Implementierung gefunden, die ohne Hitze- und Lüfterprobleme genug CPU für die Workloads übrig ließ

Der geänderte Automatisierungsansatz mit Podman und systemd

  • Der wichtigste Grund, Kubernetes weiter einzusetzen, war die Automatisierung von Deployments
    • Mit GitOps und Flux ließen sich Änderungen einfach umsetzen
    • Mit der Container-Image-Automatisierung und Webhooks in Flux v2 wurde nach dem Push eines neuen Images innerhalb weniger Sekunden das neue Image auf den Server gezogen und die Produktionsanwendung gestartet
  • Frühere Alternativen außerhalb von Kubernetes waren nicht zufriedenstellend
    • Container neu zu erstellen, indem alle ursprünglichen Kommandozeilenargumente gemerkt werden mussten, bedeutete hohen Verwaltungsaufwand
    • Werkzeuge, die vollständige Kontrolle über docker.sock verlangen, waren ebenfalls unerwünscht
  • Podman auto-updating kam den benötigten Funktionen am nächsten
    • Podman kann als Alternative zur Docker CLI gesehen werden
    • Nach dem Erstellen eines Containers lässt sich eine systemd-Service-Datei erzeugen
    • Beim Start des Dienstes wird der Container erstellt oder ersetzt, beim Stoppen des Dienstes wird der Container entfernt
  • Automatische Updates funktionieren über das Tag io.containers.autoupdate
    • Sie laufen einmal täglich per Timer oder durch direktes Ausführen von podman auto-update
    • Wenn ein neues Image vorhanden ist, wird der Container mit diesem Image neu erstellt
  • Der Fedora-Magazine-Artikel Auto-updating Podman containers with systemd lieferte den Großteil der Umsetzung, zusätzlich waren noch zwei Einstellungen nötig
    • Mit systemctl --user enable mycontainer.service wird der Container beim Login automatisch gestartet
    • Mit loginctl enable-linger wird sichergestellt, dass die Benutzersitzung beim Serverstart läuft
  • Mit der Kombination aus Podman, systemd und User-Lingering werden etwa 99 % der Vorteile erreicht, die zuvor mit Kubernetes erzielt wurden, bei deutlich geringerer Komplexität sowie niedrigerer CPU- und Speicherlast
  • Der gesamte Dienst wurde vom bisherigen VPS auf einen neuen VPS mit halb so vielen vCPU und halb so viel RAM umgezogen; über mehrere Stunden Laufzeit hinweg war er leichter, schneller und verursachte geringere Compute-Kosten
  • Allerdings scheint die systemd-Integration von Podman bereits veraltet zu sein, und bei Container-Definitionen geht die Diskussion inzwischen in Richtung Quadlet

1 Kommentare

 
GN⁺ 2025-05-06
Meinungen auf Hacker News
  • Ich kann die Gefühle des Autors des Originals vollkommen nachvollziehen. Im Job verwalten wir relativ problemlos mehrere Kubernetes-Cluster mit Dutzenden Microservices, aber bei Hobbyprojekten ohne Einnahmen ist das Budget so klein, dass ich Kubernetes selbst dann nicht nutzen kann, wenn ich es möchte.
    Auf einem VPS für 10 US-Dollar im Monat mit 1 gemeinsam genutzten vCPU und 2 GB RAM ist Kubernetes viel zu schwergewichtig. Statt Deployments führe ich per SSH manuell docker compose up/down aus, statt Ingress verlasse ich mich auf die Container-Erkennung von Traefik, und weil ich keine CronJobs nutzen kann, habe ich sogar ein kleines Skript geschrieben, das crontab idempotent verwaltet.
    Was ich wirklich will, ist eine leichtgewichtige Alternative, die eine Kubernetes-kompatible API bietet und auch auf günstigen VPS gut läuft. Die Lücke zwischen Container-Orchestrierung auf Enterprise-Niveau und billigem Hosting für Hobbyprojekte ist immer noch viel zu groß.

    • Je nachdem, welchen Umfang der Kube API du brauchst, kann Podman diese Rolle übernehmen. Es kann aus Kubernetes-Manifests Container und Pods erzeugen und verhält sich damit wie docker compose für Kubernetes-Manifests.
      Es lässt sich auch wie im Artikel beschrieben zusammen mit systemd-Units verwenden. Podman unterstützt außerdem die Docker API größtenteils oder vollständig, sodass auch docker compose funktioniert; man kann sich auch per SSH mit einem entfernten Socket verbinden und damit arbeiten.
      https://docs.podman.io/en/latest/markdown/podman-kube-play.1...
      https://docs.podman.io/en/latest/markdown/podman-systemd.uni...
    • Ich frage mich, ob du dir k0s oder k3s angesehen hast. Es gibt ziemlich viele Beispiele, in denen sie in kleinem Maßstab gut genutzt werden: https://news.ycombinator.com/item?id=43593269
    • Ich will nicht wie Oracle-Werbung klingen, aber der Oracle Cloud Free Tier ist überwältigend großzügig. Man kann damit ziemlich viel betreiben, darunter einen kleinen k8s-Cluster, und auch der k8s-Control-Plane-Service ist kostenlos.
      Kostenlos gibt es 4 ARM64-Kerne und 24 GB RAM, die man je nach gewünschter Konfiguration auf 1 bis 4 Nodes aufteilen kann.
      https://www.oracle.com/cloud/free/
    • Man kann einfach etwas wie Ansible nach alter Schule verwenden. Ich verwalte ein paar dedizierte Server vollständig mit Ansible, und man kann es als eine Art aufgebohrtes docker compose nutzen.
      Mit Traefik und Labels lassen sich Reverse Proxy und TLS-Zertifikate verallgemeinern, und Authelia kann man als einfachen Authentifizierungsanbieter davorschalten. Auf GitHub gibt es viele Beispielprojekte, und wenn man ein Wochenende in die Einrichtung steckt, bekommt man ein ziemlich gut wartbares System.
    • Da du bereits gesagt hast, dass die Kosten für Kubernetes zu hoch sind, kann eine selbst gebaute Lösung je nachdem, auf welcher Achse man Effizienz misst, sogar effizienter sein.
  • systemd bekommt viel Kritik ab, löst aber wirklich viele Probleme und sollte daher nicht vorschnell abgetan werden. Als es in Distributionen standardmäßig Einzug hielt, mochten viele Leute vor allem nicht, dass sie sich umstellen mussten.
    Es gibt Container, machinectl, nspawn als leistungsfähigeres chroot, vmspawn für den Fall, dass vollständige Virtualisierung nötig ist, importctl zum Herunterladen, Importieren und Exportieren von Maschinen sowie homed/homectl, was Verschlüsselung und Rechteverwaltung für Home-Verzeichnisse erleichtert.
    Statt fstab behandelt man Mounts als Units, steuert Boot-Reihenfolge sowie Start und Stopp von Diensten und bekommt auch Timer, die mächtiger sind als cron. Beispielsweise kann man erkennen, welche Jobs nicht ausgeführt wurden, weil die Maschine ausgeschaltet war, oder Jobs nach dem Booten unter bestimmten Bedingungen verzögert ausführen.
    Service-Units erlauben eine feingranulare Kontrolle von Aufgaben und eine Einschränkung von Berechtigungen; mit systemctl edit kann man Override-Konfigurationen anlegen, ohne die Originalkonfiguration anzufassen. Der Einstieg ist etwas lästig, aber wenn man komplexe Dinge tun will, gibt es ohnehin kein Werkzeug, bei dem man nie in die Dokumentation schauen muss.

    • Dass systemd in den ersten Jahren, vielleicht fast ein Jahrzehnt lang, so viel Kritik bekam, lag nicht daran, dass das Projekt selbst schlecht gewesen wäre. Im Gegenteil: Es war gut genug, um trotz vieler Probleme erfolgreich zu sein.
      Das Problem war die Haltung der Maintainer. Dinge, die gut funktionierten, wurden scheinbar unbekümmert kaputtgemacht, ohne angemessene Korrekturen bereitzustellen. Wenn man durch systemd keine Schmerzen gespürt hat, ist man vielleicht spät dazugestoßen, oder die eigenen Bedürfnisse überschnitten sich zufällig mit denen der Kern-Maintainer.
    • Das Einzige, was mir an systemd nicht gefällt, ist, dass ein Binary aus einer unüberschaubar großen Codebasis die Rolle von PID 1 übernimmt und beim Upgrade noch komplexeres Verhalten zeigt, weil es versucht, sich an Ort und Stelle selbst per exec zu ersetzen.
      Ein Programm, das zu 100 % exakt das tut, was PID 1 tun muss, und sonst nichts, kann viel kleiner sein. Wenn man darauf systemd startet, führt ein Problem in systemd nicht sofort zu einer Kernel Panic.
      Quelle: https://ewontfix.com/14/
    • Die Forderung, cron zu vergessen, ist schwer nachvollziehbar. Es hat 50 Jahre lang gut funktioniert, und plötzlich soll es furchtbar falsch sein und selbstverständlich durch systemd ersetzt werden.
    • Wenn man fstab vergisst und systemd-Mounts verwendet, können die Automount-Regeln komplex sein, und selbst wenn man sie 1:1 an die Dokumentation anpasst, funktionieren sie manchmal einfach nicht, sodass Dateisysteme nicht rechtzeitig gemountet werden.
    • systemd ist großartig für moderne Desktop- oder Server-Linux-Systeme oder ähnliche Einsatzzwecke. Wenn man aber etwas tun will, das außerhalb der vom Projekt vorgesehenen Arbeitsweise liegt, stößt man auf Spott und Widerstand. Fragt einfach das musl-Team.
      Für Distributionen oder Upstreams macht es das Leben sehr viel leichter, fügt dafür aber in der untersten Systemschicht immer mehr Komplexität hinzu. Je nach Sichtweise kann man Dinge wie journalctl, timedatectl, die dbus-Abhängigkeit oder entsprechende Alternativen als nicht vereinbar mit der Unix-Philosophie ansehen.
      Wenn es lediglich darum geht, Prozesse zu koordinieren, sie in der richtigen Reihenfolge auszuführen und automatische Aktivierung sicherzustellen, halte ich es für ein angemesseneres Werkzeug als k8s oder Docker.
  • Ich betreibe mein Homelab seit einiger Zeit mit podman-systemd, also Quadlet, und jedes Mal, wenn ich mir eine neue k8s-Variante angesehen habe, war sie den zusätzlichen Aufwand nicht wert. Es reicht völlig, in den alten Ansible-Playbooks Images vorab zu pullen und Unit-Dateien an die richtige Stelle zu legen.
    Auch den gesamten Stack meines Voron-3D-Druckers lasse ich mit podman-systemd laufen, sodass ich alle Komponenten auf einmal aktualisieren und zurückrollen kann. Allerdings prüfe ich inzwischen auch einen Ansatz, bei dem mit mkosi und systemd-sysupdate komplette Disk-Images auf einmal aktualisiert und zurückgerollt werden.
    Das Hauptproblem ist, dass die Leute meist nur docker-compose-Dateien ausliefern, die man dann in systemd-Units umwandeln muss, und dass manche Docker-Images bei Benutzer- und Rechtekonfigurationen eine für Podman unnötige Komplexität mitbringen. Besonders wenn ein Container die Ausführung als root verweigert oder auf einen anderen Benutzer wechselt, braucht man mitunter lästige userns-ID-Mappings.
    Insgesamt ist es aber deutlich weniger komplex als jede k8s- oder k8s-Varianten-Konfiguration. Mir gefällt auch, dass alles sowohl in systemd als auch in journald integriert ist und nicht auf zwei Stellen aufgeteilt wird.

    • Ich nutze seit Jahren einen ähnlichen Ansatz: https://github.com/Mati365/hetzner-podman-bunjs-deploy. Er basiert auf Podman und systemd, und in der ganzen Zeit ist nichts kaputtgegangen.
      Man legt einfach nur die Units ab; das ist sehr stabil und simpel.
    • Für die Umwandlung von compose-Dateien in Quadlet-Dateien kann man podlet verwenden: https://github.com/containers/podlet
    • Am Ende ist das wohl ein einzelner Node oder eine Sammlung unabhängiger Nodes. Podman/systemd/Quadlet kann ein Implementierungsdetail dafür sein, wie ein k8s-Node Container ausführt, also gewissermaßen etwas CRI-Ähnliches.
      Es ersetzt aber nicht die Orchestrierungs- und Scheduling-Abstraktion über mehrere Nodes hinweg, die k8s bietet. Es fehlt der Teil: „Hier sind Maschinen, die Podman-systemd-Dateien ausführen können, und hier ist die Spezifikation, die ich ausführen möchte; bitte platziere sie passend.“
    • Ähnliche Erfahrung. Der Workflow ist: einen Container mit einem podman run-Befehl starten, prüfen, ob er richtig läuft, dann mit podlet eine grundlegende Container-Datei erzeugen und diese anpassen, indem Volumes und Netzwerke in andere Quadlet-Dateien ausgelagert werden – fertig.
      Das Projekt podman-compose scheint ebenfalls weiterhin aktiv gepflegt zu werden und kann eine gute Alternative zu docker-compose sein. Die Integration von Podman und systemd ist aber so zufriedenstellend.
  • Der nächste Schritt, um das noch weiter zu vereinfachen, ist, Container innerhalb von systemd mit Quadlet zu verwalten. Details gibt es unter https://www.redhat.com/en/blog/quadlet-podman.

    • Quadlet ist genau der richtige Weg. Es ist wirklich eine gute Art, Container auszuführen, und fühlt sich nach „einmal einrichten und vergessen“ an. Unter Fedora oder Rocky Linux muss man zumindest nicht einmal ein zusätzliches Paket installieren.
    • Das wurde am Ende des Artikels behandelt, aber der Autor hatte es sich noch nicht angesehen. Da die systemd-Integration von Podman bereits wie deprecated wirkt und nun davon die Rede ist, Container über „Quadlet“-Dateien zu definieren, hat er es offenbar als etwas aufgeschoben, das man später lernen muss.
    • Ich bin in die Kommentare gekommen, um zu sehen, ob jemand Quadlet erwähnt hat. Letzte Woche habe ich meinen Homeserver von docker compose auf rootless Podman Quadlet umgestellt; die Migration war mühsam, aber mit dem Ergebnis bin ich sehr zufrieden.
    • Auch dieser Beitrag hat sehr geholfen, das Homelab reibungslos auf Quadlet umzustellen: https://news.ycombinator.com/item?id=43456934
  • Ich habe skate gebaut: https://github.com/skateco/skate. Im Grunde ist es genau für diesen Zweck gedacht, unterstützt aber mehrere Hosts und auch k8s-Manifeste. Intern nutzt es Podman und systemd.

    • Dieser Ansatz gefällt mir sehr. Es ist wirklich frustrierend, dass es keinen einfachen Weg gibt, Docker/Podman über mehrere Hosts hinweg zu betreiben. Docker Swarm ist leider seit 2019 faktisch liegen geblieben.
      Allerdings finde ich, dass k8s bei API und User Experience furchtbar ist. Die Docker-Compose-Spezifikation ist deutlich nutzerfreundlicher, daher experimentiere ich derzeit mit docker-compose für mehrere Hosts: https://github.com/psviderski/uncloud
  • Ich bin wieder dazu zurückgekehrt, alles als deb-Pakete zu bündeln und direkt mit systemd auf EC2-Instanzen auszuführen; Container nutze ich nicht mehr. Die Instanzen laufen in einer Auto Scaling Group mit angebundenem ALB, und beim Booten installiert ein einfaches ansible-pull das deb.
    Das ist ziemlich roh, aber ich bin die endlosen HCL-Blöcke mit YAML in JSON leid. Inzwischen möchte ich nur noch ungefähr Ansible-YAML anfassen.

    • Das Gute an diesem Ansatz ist: Wenn es einen Bug in einer gemeinsamen Library gibt, führt man apt full-upgrade aus und startet nur die laufenden Prozesse neu – dann ist man geschützt.
      Man muss nichts neu bauen und auch nicht herausfinden, wie man eine Library aktualisiert, die tief in einem Container vergraben ist, den man vielleicht selbst gebaut hat oder vielleicht auch nicht.
    • Bei einer sehr einfachen Anwendung habe ich denselben Weg gewählt. systemd war überraschend angenehm, und es war ziemlich gut, den Dienst mit einem vom System zugewiesenen Benutzerkonto und minimalen Rechten auszuführen.
      Dank cgroup-Unterstützung eignet es sich auch gut, mehrere Dienste auf einem einzigen VPS laufen zu lassen.
    • Wenn man an die Lebenszeit denkt, die Menschen im Problemfeld „YAML-Verwaltung im großen Stil“ verschwendet haben, wird einem schwindlig.
  • Der Beitrag ist über ein Jahr alt; inzwischen gibt es mit ParticleOS sogar eine offizielle OS-Distribution von systemd für unveränderliche Workflows.
    https://github.com/systemd/particleos
    https://news.ycombinator.com/item?id=43649088

    • Der nächste logische Schritt wäre wohl, den Linux-Kernel durch systemd-kernel zu ersetzen, dann wäre es komplett.
  • Beim Lesen wirkt es so, als ließe sich all das durch den Befehl docker compose und etwas wie Caddy, das automatisch Zertifikate holt, ersetzen.
    Wenn man nur eine compose.yaml hat, reicht im Grunde eine Zeile: docker compose up -d --pull always. Für die CI-Konfiguration genügt auch scp compose.yaml user@remote-host:~/ gefolgt von ssh user@remote-host 'docker compose up -d --pull always'.
    Der Vorteil ist, dass es einfach ist und auch auf der Entwicklungsmaschine funktioniert. Wenn das Nebenziel natürlich ist, etwas Interessantes auszuprobieren und dabei zu lernen, sind Quadlet, k8s und systemd ebenfalls gute Optionen.

    • Man muss nur einmal docker context create --docker 'host=ssh://user@remote-host' remote-host ausführen.
      Danach kann man docker -c remote-host compose -f compose.yaml up -d --pull always verwenden und muss die Datei nicht kopieren. Wenn man außerdem die Benutzerinformationen in ~/.ssh/config hinterlegt, muss man bei SSH-Aufrufen kein user@ mehr angeben, was es im Team einfacher macht, Dokumentation oder Befehle zu kopieren und zu verwenden.
    • Man kann es wie im Geschwisterkommentar machen oder die Umgebungsvariable DOCKER_HOST setzen. Allerdings sollte man beachten, dass für die Interpolation in der compose-Datei lokale Umgebungsvariablen verwendet werden.
  • Weil ich finde, dass Deployments auf einen einzelnen Server nicht so kompliziert sein sollten, habe ich ein Tool gebaut, das so deployt, wie ich es möchte: https://harbormaster.readthedocs.io/
    Harbormaster findet Repositories anhand einer YAML-Datei, klont und aktualisiert sie regelmäßig und führt dann die darin enthaltenen Docker-Compose-Dateien aus. Der gesamte Zustand liegt ebenfalls in einem einzigen Verzeichnis, was Backups einfach macht.
    Wenn man nur einen einzelnen Server braucht, ist es unter den Container-Orchestrierungs-Tools, die ich bisher gesehen habe, das einfachste und beste. Mir gefällt, dass die gesamte Konfiguration im Repository deklariert ist, der gesamte Zustand in einem Verzeichnis liegt und alles einfach Compose-Dateien sind.

  • Beim Lesen der Kommentare hier fühle ich mich alt. Nutzt inzwischen niemand mehr einfach nur ssh und nginx?
    Alles in eine Box stopfen, diese Box aggressiv sichern, fertig. Für den Heimgebrauch braucht man wirklich kein Microservice-Management.

    • Ich nutze nur nginx und ein paar Custom Services, aber das geht, weil die Anforderungen sehr klein sind. Sobald es nur etwas komplexer wird oder man mehrere Nodes betreibt und Redundanz braucht, ergeben Container auf einmal enorm viel Sinn.