k3sbase

GitOps-verwalteter k3s-/k3d-Cluster mit Flux Operator, Cilium, cert-manager und external-dns/Cloudflare. Ein Repo, zwei Cluster-Ziele — lokaler k3d-Dev-Cluster und ein physischer Turing Pi 2-Cluster (IPv6-only, Split-Horizon-Zugriff).

Flux Operator Cilium / Gateway API cert-manager external-dns SOPS + age mise

ÜberblickWorum es geht

k3sbase ist ein Baukasten für GitOps-verwaltete Kubernetes-Cluster: ein Repo, zwei Cluster-Ziele, dieselben Infrastruktur-Manifeste. Nach dem einmaligen Bootstrap übernimmt Flux vollständig — jede weitere Änderung läuft über Commit und Push, kein manuelles kubectl apply oder helm install mehr.

  • local — ein k3d-Cluster für schnelle Entwicklung, in Minuten aus einem frischen Checkout aufgebaut.
  • pi — ein physischer Turing Pi 2-Cluster (1 Server + 3 Agents) auf k3s, IPv6-only, mit Cilium Gateway API und Split-Horizon-Zugriff (extern via Cloudflare Tunnel, intern via statischer IPv6-ULA).

Weitere Cluster-Targets sind als eigene Verzeichnisse unter clusters/ vorgesehen; die infrastructure/-Manifeste in clusters/base/ sind bewusst target-übergreifend wiederverwendbar, target-spezifische Abweichungen bleiben in der Bootstrap-/Cluster-Schicht.

LoslegenGetting Started

Der schnellste Weg zu einem laufenden Cluster ist der lokale k3d-Cluster:

  1. Tools installieren
    mise install   # kubectl, helm, flux2, age, sops, k3d, cilium-cli
  2. Secrets & Konfiguration einmalig einrichten
    mise run setup -- local
    Fragt Cloudflare-API-Token ab, generiert den age-Key und verschlüsselt alles per SOPS.
  3. Cluster erstellen, Cilium installieren, Flux bootstrappen
    mise run cluster-up    -- local
    mise run cilium-up     -- local
    mise run flux-bootstrap -- local
Wenn cluster-up die Server-IP in clusters/local/infrastructure/cilium.yaml patcht: vor flux-bootstrap committen und pushen — Flux reconciliert origin/main, nicht den lokalen Checkout.

Für den physischen Pi-Cluster (Turing Pi 2, ab einem BMC ohne OS) gibt es einen eigenen, ausführlichen Weg vom Flashen der Compute-Module bis zur Übergabe an dieselbe Bootstrap-Kette — siehe Turing-Pi-2-Fresh-Setup-How-To und das vollständige README.

Was drin istFeatures

Networking (Cilium)

kube-proxy-Replacement, Gateway API (Cilium-Controller + Envoy) statt Ingress-Controller. local: Overlay-Routing. pi: IPv6-only Native Routing, HA-Operator.

Split-Horizon-Zugriff (nur pi)

Jeder Hostname über zwei Pfade erreichbar: extern via Cloudflare Tunnel, intern via statischer IPv6-ULA (Cilium LB-IPAM-Pinning, kein L2-Announcement nötig) — große Uploads müssen nicht durch den Tunnel.

TLS & DNS

cert-manager + Let's-Encrypt-ClusterIssuer via DNS-01/Cloudflare (Wildcard-Zertifikate). external-dns legt DNS-Einträge inkl. Tunnel-CNAME automatisch an.

Secrets

SOPS + age — nur data/stringData verschlüsselt, kind/apiVersion bleiben für kustomize build lesbar.

GitOps (Flux Operator)

FluxInstance statt manuellem flux bootstrap. Kustomization-Kette mit dependsOn/healthChecks, zentrale ConfigMaps als einzige Quelle der Wahrheit für Flux und mise-Tasks.

BMC-/Hardware-Bootstrap (Turing Pi 2)

tpi-CLI flasht alle vier Compute-Module inkl. Cloud-Init, automatische Node-Discovery per Hostname-Scan, Ansible konvergiert SSH-Härtung und statische ULAs idempotent.

Interaktiver Setup-Assistent

Ein Befehl (mise run setup) für age-Key, SOPS-Recipient, Node-Konfiguration und alle Secrets — mehrfach ausführbar, fragt nur fehlende Werte ab.

Lokale Validierung (flux-local)

Kustomizations/HelmReleases gegen den Git-Stand geprüft, ganz ohne laufenden Cluster — als Pre-Push-Gate über pre-commit einbindbar.

Beispiel-Workload & Testplan

echo-a/echo-b testen die komplette Kette: Gateway, HTTPRoute-Matching, Zertifikat, beide Zugriffspfade.

Versionierung

Alle Chart-/Tool-Versionen gepinnt, keine latest-Floating-Tags — zentral je Cluster in cluster-versions.yaml.

Warum soDesignentscheidungen

EntscheidungBegründung
Alles im Repo, nichts nur in Shell Jeder Schritt ist ein idempotentes Skript oder Manifest, aufrufbar via mise-Task. Frischer Checkout + mise-Tasks in Reihenfolge muss denselben Cluster ergeben.
GitOps zuerst Nach dem Flux-Bootstrap kein manuelles helm install/kubectl apply mehr. Einzige Ausnahmen: zwangsläufig imperative Bootstrap-Schritte (CNI vor Flux, Flux selbst), klar kommentiert.
Minimaler Overhead Keine Komponente ohne konkretes Problem.
IPv6-only auf dem Pi-Cluster Bewusste Vereinfachung statt Dual-Stack. Native Routing statt VXLAN, weil Ciliums Tunnel-Modus in 1.16 einen IPv4-Underlay voraussetzen würde.
Split-Horizon statt nur Tunnel Große Uploads (z.B. Nextcloud) sollen nicht über den Cloudflare-Tunnel-Umweg laufen müssen — LAN-Traffic geht direkt über die statische Gateway-ULA.
LB-IPAM-Pinning statt L2-Announcement Cilium ignoriert Gateway.spec.addresses; die statische Adresse kommt über einen LB-IPAM-Pool + Pinning-Annotation und verhält sich im Datapath identisch zu externalIPs.
SOPS verschlüsselt nur data/stringData kind/apiVersion/metadata bleiben lesbar — sonst schlägt jeder kustomize build fehl.
Helm-Release-Adoption statt Doppel-Install Cilium wird vor Flux imperativ installiert (Henne-Ei-Problem), Flux übernimmt danach dieselbe Release über gleichen Namen/Namespace statt einen zweiten Install zu versuchen.
ClusterIssuer/Gateway in apps/, nicht infrastructure/ Der kustomize-controller kann CRs unbekannter Kinds nicht applien, bevor deren CRDs (aus den Helm-Releases) existieren. dependsOn + healthChecks entkoppeln das sauber.
Alle Versionen gepinnt Keine latest-Floating-Tags. Eine zentrale ConfigMap pro Cluster ist Quelle für Flux (postBuild.substituteFrom) und mise-Tasks gleichermaßen.

WeiterlesenReferenz

Im Repo

Tools & Dokumentation

  • Flux GitOps-Toolkit, hier via Flux Operator
  • Cilium CNI, kube-proxy-Replacement, Gateway API
  • Gateway API Ingress-Nachfolger, standard-channel CRDs
  • cert-manager Zertifikate via Let's Encrypt/DNS-01
  • external-dns DNS-Records aus Cluster-Ressourcen
  • SOPS + age Secrets-Verschlüsselung im Repo
  • k3d / k3s lokaler Dev-Cluster / Pi-Cluster
  • tpi BMC-CLI für den Turing Pi 2
  • mise Tool- und Task-Runner für alle Bootstrap-Schritte