Ü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:
-
Tools installieren
mise install # kubectl, helm, flux2, age, sops, k3d, cilium-cli
-
Secrets & Konfiguration einmalig einrichten
mise run setup -- local
Fragt Cloudflare-API-Token ab, generiert den age-Key und verschlüsselt alles per SOPS. -
Cluster erstellen, Cilium installieren, Flux bootstrappen
mise run cluster-up -- local mise run cilium-up -- local mise run flux-bootstrap -- local
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
| Entscheidung | Begrü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
- README Vollständige Bootstrap-Reihenfolge, Secrets, Cluster-Targets
- Turing-Pi-2-Fresh-Setup-How-To Vom BMC ohne OS bis zur Übergabe an die Bootstrap-Kette
- TESTPLAN.md End-to-End-Tests für Split-Horizon-Routing
- CLAUDE.md Grundprinzipien, Commit-Konvention, Repo-Struktur
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