- Go Template 66.5%
- Shell 19.1%
- HCL 12%
- Go 1.1%
- Jinja 0.6%
- Other 0.6%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo/workflows | ||
| apps | ||
| customers | ||
| docs | ||
| inventory | ||
| playbooks | ||
| roles | ||
| scripts/backup | ||
| .gitignore | ||
| ansible.cfg | ||
| CLAUDE.md | ||
| loki-export.sh | ||
| production_run.sh | ||
| README.md | ||
| testing_run.sh | ||
| TODO.md | ||
k3s-Cluster (Testing + Production)
Ziel: Ein k3s-Cluster, das beim Booten automatisch startet, komplett als Code definiert ist (kein manuelles Klicken/Ausführen von Shell-Scripts) und schrittweise um Services erweiterbar ist – alles aus diesem Ordner heraus. Derselbe Code deployt sowohl eine lokale Testing- als auch eine Remote-Production-Umgebung, siehe Umgebungen: Testing vs. Production.
Umgebungen: Testing vs. Production
| Testing | Production | |
|---|---|---|
| Inventory | inventory/testing.yml |
inventory/production.yml |
| Zielrechner | localhost |
srv202603.sunah.de |
| kubectl-Context | k3s-testing |
k3s-production |
| Forgejo-Domain | git.local |
git.gme24.de |
| ArgoCD-Domain | argocd.local |
argocd.gme24.de |
| Zitadel-Domain | id.local |
id.gme24.de |
| TLS | lokale Self-Signed-CA | Let's Encrypt (HTTP-01) |
| Kustomize-Overlay | apps/{forgejo,argocd,root}/overlays/testing |
apps/{forgejo,argocd,root}/overlays/production |
/etc/hosts-Eintrag |
automatisch (Ansible, für beide Domains) | nicht automatisiert (öffentliches DNS) |
| kube-apiserver | direkt lokal (127.0.0.1:6443) |
nicht öffentlich erreichbar, nur per SSH-Tunnel |
Der SSH-/kubectl-User wird nicht hardcoded, sondern in beiden
Inventories über ansible_user: "{{ lookup('env', 'USER') }}" automatisch
aus dem lokalen Shell-User abgeleitet, der ansible-playbook aufruft – kein
manuelles Eintragen nötig.
Deploy/Update für Production: ./production_run.sh (Pendant zu
testing_run.sh, siehe dortige Kommentare für Details).
Kubeconfig liegt repo-lokal unter .kube/config (gitignored, Default
aus k3s_local_kubeconfig_path in roles/k3s_server/tasks/main.yml:
playbook_dir + '/../.kube/config') – nicht ~/.kube/config. Gilt für
beide Umgebungen gleichermaßen; die Ansible-Rolle legt/aktualisiert dort bei
jedem Lauf beide Kontexte (k3s-testing, k3s-production), unabhängig
davon, welches Inventory gerade läuft. testing_run.sh/production_run.sh
nutzen denselben Pfad (KUBECONFIG_PATH="${KUBECONFIG_PATH:-.kube/config}").
Ein separates ~/.kube/config (z.B. aus einem älteren manuellen Setup) hat
davon nichts und enthält k3s-production insbesondere nie, weil der
Kontext ohnehin nur über den Tunnel unten erreichbar ist.
kube-apiserver ist in production bewusst nicht öffentlich erreichbar
(höchster Angriffswert im Cluster, volle Cluster-Admin-Rechte) – anders als
Forgejo/ArgoCD/Zitadel, die über Traefik+OIDC laufen. production_run.sh
baut dafür selbst einen SSH-Tunnel auf (lokaler Port 16443, da 6443
durch das lokale testing-k3s dauerhaft belegt ist) und räumt ihn danach
wieder auf. Für kubectl-Zugriff außerhalb des Skripts denselben Tunnel
manuell öffnen:
ssh -N -L 16443:127.0.0.1:6443 "$USER@srv202603.sunah.de" &
kubectl --kubeconfig=.kube/config --context=k3s-production get nodes
Testing braucht keinen Tunnel (kube-apiserver läuft lokal auf
127.0.0.1:6443), aber denselben expliziten Kubeconfig-Pfad:
kubectl --kubeconfig=.kube/config --context=k3s-testing get nodes
Offene Punkte für Production:
- Es wird angenommen, dass der aufrufende lokale User (z.B.
falk) aufsrv202603.sunah.debereits als SSH-User mit Sudo-Rechten existiert. - Ob der Hoster zusätzlich eine vorgelagerte Cloud-Firewall/Security-Group
einsetzt, die Port 80/443 (Let's-Encrypt-HTTP-01-Challenge, siehe
apps/cert-manager/overlays/production/) blockieren könnte, ist von hier aus nicht prüfbar.
Anmelden bei Forgejo
- Admin-Zugangsdaten abrufen (Secret wird vom Bootstrap-Job selbst generiert,
liegt nie im Repo):
(Für Production:kubectl --context=k3s-testing -n forgejo get secret forgejo-admin -o jsonpath='{.data.username}' | base64 -d; echo kubectl --context=k3s-testing -n forgejo get secret forgejo-admin -o jsonpath='{.data.password}' | base64 -d; echo--context=k3s-production.) https://git.localim Browser aufrufen und mit Username + Passwort einloggen (Ingress leitet Port 80 automatisch auf 443 um). Für Production entsprechendhttps://git.prod.local.- Falls der Browser das Zertifikat noch als nicht vertrauenswürdig anzeigt: Root-CA manuell importieren (siehe Abschnitt "Root-CA im Browser vertrauen" weiter unten) – betrifft v.a. Edge/Chrome unter Linux, die eine eigene NSS-Zertifikatsdatenbank statt (nur) den System-Trust-Store nutzen.
- Die Organisation
localund das Repolocal/k3s(privat) wurden vom Bootstrap-Job bereits automatisch angelegt – dieses Repo hier ist bereits dorthin gepusht (Remoteforgejo, siehe Abschnitt "Dieses Repo pushen").
Anmelden bei ArgoCD
- Initial-Admin-Passwort abrufen (Secret wird von ArgoCD selbst beim ersten
Start erzeugt, liegt nie im Repo):
(Für Production:kubectl --context=k3s-testing -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d; echo--context=k3s-production.) Username istadmin. https://argocd.localim Browser aufrufen und einloggen (Ingress leitet Port 80 automatisch auf 443 um, TLS wie bei Forgejo über die lokale Root-CA). Für Production entsprechendhttps://argocd.prod.local.- ArgoCD läuft mit
server.insecure: "true"(TLS-Termination übernimmt Traefik am Ingress, nichtargocd-serverselbst) – dadurch ist kein separates Vertrauen der lokalen CA imargocd-repo-servernötig, da dieser Forgejo ohnehin nur über die interne, unverschlüsselte Cluster-URL anspricht (http://forgejo.forgejo.svc.cluster.local:3000). - Die beiden Applications
forgejoundargocd(App-of-Apps unterapps/root/) sollten im ZustandSynced/Healthyerscheinen – ArgoCD verwaltet damit sowohl Forgejo als auch sich selbst aus Git heraus. - Nur UI/Ingress-Zugriff ist eingerichtet; CLI-Login (
argocd login) bräuchte client-seitig--grpc-web.
Root-CA im Browser vertrauen
update-ca-certificates (Teil der local_ca-Ansible-Rolle) aktualisiert nur
den System-Trust-Store. Chromium-basierte Browser (Edge, Chrome) nutzen
unter Linux zusätzlich eine eigene NSS-Zertifikatsdatenbank
(~/.pki/nssdb), die davon unabhängig ist. Import per certutil
(Paket libnss3-tools, unabhängig davon ob die Edge-UI unter
"Zertifikate verwalten" überhaupt etwas anzeigt – das kann durch eine
Unternehmensrichtlinie deaktiviert sein):
certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "Development Root CA" -i /etc/ssl/local-ca/ca.crt
Danach Browser komplett neu starten (alle Fenster schließen).
Dieses Repo pushen
Remote ist bereits eingerichtet (forgejo, SSH: ssh://git@git.local:30022/local/k3s.git):
git push forgejo main
SSH statt HTTPS, obwohl ~/.ssh/id_rsa nur 2048 Bit hat (Forgejos Default
wäre mindestens 3071 Bit) — apps/forgejo/base/deployment.yaml senkt das
bewusst ab. testing_run.sh richtet SSH-Zugriff bei jedem Lauf automatisch
ein, bevor gepusht wird (neuer Host-Key nach Fresh-Rebuild, Public Key wird
idempotent per Forgejo-API registriert). Anderer Key: SSH_PUBKEY_PATH
vor dem Skriptaufruf setzen.
Für production (production_run.sh) fehlt der Remote noch und muss einmalig
angelegt werden:
git remote add forgejo-production ssh://git@git.gme24.de:30022/local/k3s.git
Nach dem Push: ArgoCD liest den Repo-Inhalt ausschließlich vom
forgejo-Remote, nicht vom lokalen Arbeitsverzeichnis — ungepushte
Änderungen zeigen bei der betroffenen Application Unknown/
ComparisonError: ... app path does not exist, kein Bug. Nach dem Push
reconciled ArgoCD automatisch, kein manueller Sync nötig.
Terraform manuell anstoßen (wenn CI nicht geht)
Alle Terraform-Roots im Repo (apps/zitadel/terraform/, sowie pro Kunde
customers/<environment>/<kunde>/terraform/ und
customers/<environment>/<kunde>/<produkt>/terraform/) laufen normalerweise
automatisiert per CI-Job (.forgejo/workflows/{zitadel,customer-org,customer-product}-terraform.yml)
mit der In-Cluster-ServiceAccount zitadel-terraform-ci. Für einen manuellen
Lauf vom eigenen Rechner aus (z. B. Debugging, State-Reparatur) müssen
zwei unabhängige Stellen explizit umkonfiguriert werden — beide haben
sonst unterschiedliche, stillschweigende Fallbacks, die live schon zu einem
kaputten State geführt haben (siehe docs/troubleshooting.md):
- Backend (State-Speicherort) — kein eigener Terraform-Variable dafür,
muss per
-backend-configbeiterraform initgesetzt werden. - Provider (tatsächliche kubectl-Verbindung für
kubernetes_*- Ressourcen) —config_path/config_contextkommen aus den Variablenkubeconfig_path/kubeconfig_context(Defaultnull= In-Cluster- ServiceAccount bzw. deren client-go-Fallback). Beide immer explizit auf die repo-lokale.kube/configsetzen, nie auf eine eventuell vorhandene~/.kube/configverlassen (siehe oben, "Kubeconfig liegt repo-lokal").
Beispiel für apps/zitadel/terraform/ in testing (Pfade für die
Kunden-Roots entsprechend anpassen, PAT bleibt gleich):
cd apps/zitadel/terraform
PAT=$(kubectl --kubeconfig=../../../.kube/config --context=k3s-testing \
-n zitadel get secret iam-admin-pat -o jsonpath='{.data.pat}' | base64 -d | tr -d '\n\r')
terraform init -input=false \
-backend-config="in_cluster_config=false" \
-backend-config="config_path=../../../.kube/config" \
-backend-config="config_context=k3s-testing"
terraform plan -input=false \
-var-file="testing.tfvars" \
-var "zitadel_access_token=$PAT" \
-var "zitadel_domain=id.local" -var "zitadel_port=443" -var "zitadel_insecure=false" \
-var "kubeconfig_path=../../../.kube/config" -var "kubeconfig_context=k3s-testing"
zitadel_domain=id.local/zitadel_port=443/zitadel_insecure=false
biegen den Zitadel-Provider auf den öffentlichen Ingress um (die Defaults
in provider.tf sind die In-Cluster-Werte, die von außerhalb des Clusters
nicht auflösen). Erst nach einem sauberen plan apply statt plan
verwenden — nie direkt -auto-approve bei einem manuellen Lauf.
Architektur-Entscheidungen
| Ebene | Werkzeug | Warum |
|---|---|---|
| k3s-Installation (System) | Ansible (eigene Rolle roles/k3s_server) |
Idempotent, deklarativ, kein Blackbox-Shell-Script (get.k3s.io) |
| k3s-Konfiguration | /etc/rancher/k3s/config.yaml, per Ansible-Template gerendert |
Versioniert in Git statt manuell auf der Maschine gepflegt |
| k3s-Autostart | systemd-Unit, per Ansible-Template selbst erzeugt | Kein Verlass auf das Install-Script, volle Kontrolle über Unit-Datei |
| Services/Workloads | GitOps (ArgoCD) | Deklarativ, Cluster reconciled sich selbst aus Git |
| Git-Hosting | Forgejo, im Cluster selbst gehostet | Bleibt lokal, kein externer Anbieter |
| CI (optional, später) | Forgejo Actions | Falls eigene Container-Images gebaut werden sollen |
| TLS für Forgejo/ArgoCD | Eigene lokale Root-CA (Ansible-Rolle roles/local_ca, community.crypto) |
Echtes vertrauenswürdiges Zertifikat für https://git.local/https://argocd.local, ohne externe CA/Domain |
| ArgoCD-Repo-Zugriff | Interne Cluster-URL (http://forgejo.forgejo.svc.cluster.local:3000) statt https://git.local |
Vermeidet, dass argocd-repo-server der lokalen selbstsignierten CA vertrauen muss |
Aktueller Stand
- k3s system-weit per Ansible installiert (
roles/k3s_server, Binary + Template-Config + systemd-Unit, keinget.k3s.io-Script), Kubeconfig automatisch nach.kube/configgemergt (repo-lokal, nicht~/.kube/config— bestehende globale Kontexte bleiben unangetastet) - Lokale Root-CA + Sub-CA (
roles/local_ca), Sub-CA als Secret für cert-managersClusterIssuer local-ca-issuer— Ansible bootstrapped nur noch die CA, App-Zertifikate erzeugt cert-manager selbst - Forgejo (
apps/forgejo/) mit Bootstrap-Job (Admin-User, Orglocal, Repolocal/k3s), Postgres statt sqlite3, eigener Actions-Infra-Runner - ArgoCD (
apps/argocd/, vendorter Install-Manifest) + App-of-Apps (apps/root/) — verwaltet sich selbst und alle Apps aus Git - cert-manager, Sealed Secrets, CloudNativePG +
service-db+reflector(geteilte Postgres-Instanz für Root-/Plattform-Services) - Zitadel + OIDC-Login für Forgejo und ArgoCD (inkl. RBAC)
- HowTo für manuelles Terraform-Anstoßen dokumentiert (siehe unten)
- Erste eigene Services unter
apps/definieren (über Forgejo/ArgoCD hinaus), Production-Deploy, kube-apiserver-OIDC, Backup/Recovery
Details, Begründungen und gelöste Stolpersteine zu jedem Punkt:
CLAUDE.md (Konventionen + aktueller Stand) und docs/troubleshooting.md
(volle Debugging-Chroniken).
Wichtige Pfade auf dem Cluster-Node
/var/lib/rancher/k3s/– containerd-Daten, Kubelet-State, eingebettete DB (SQLite unterserver/db/), Zertifikate (server/tls/)/var/lib/rancher/k3s/server/manifests/– Manifeste hier werden von k3s automatisch beim Start angewendet (nutzt k3s intern z.B. für Traefik)/etc/rancher/k3s/k3s.yaml– Kubeconfig (Admin-Zugriff); Ansible merged daraus automatisch in die repo-lokale.kube/config, kein manuelles Kopieren nötig (siehe "Reproduzieren")/etc/ssl/local-ca/– lokale Root-CA + Leaf-Zertifikate fürgit.localundargocd.local(Key-Dateien 0600, root-only; nicht im Git-Repo)- Logs:
journalctl -u k3s(kein eigenes Logfile, systemd-Unit istType=notifyohne File-Logging)
Reproduzieren / erneutes Ausrollen
Ansible verbindet sich per SSH auf localhost (nicht connection: local) –
Grund: Bei connection: local läuft sudo/become über eine gepufferte
Pipe ohne echtes TTY, was gelegentlich zu
"timeout waiting for privilege escalation password prompt" mitten im Lauf
führte (Timing-Race beim Puffer-Flush, unabhängig vom Sudo-Auth-Cache). Über
SSH bekommt become ein echtes Pseudo-TTY, das Problem tritt nicht mehr auf.
Dafür einmalig pro Sitzung den privaten Key im Agenten entsperren:
ssh-add ~/.ssh/id_rsa
# k3s installieren/konfigurieren (Ansible), Testing-Umgebung (Default-Inventory)
cd ${path_to_folder}/k3s
ansible-playbook playbooks/site.yml -K # -K fragt sudo-Passwort ab
# nur prüfen, ohne etwas zu verändern
ansible-playbook playbooks/site.yml --check --diff
# Production-Umgebung (Remote-Server)
ansible-playbook playbooks/site.yml -K -i inventory/production.yml
Ein manuelles Kopieren der Kubeconfig ist nicht nötig – das Playbook merged
den passenden Kontext (k3s-testing/k3s-production) automatisch in die
repo-lokale .kube/config auf diesem Rechner (siehe "Aktueller Stand").
Danach: export KUBECONFIG=$(pwd)/.kube/config oder --kubeconfig=.kube/config
an jedem kubectl-Aufruf.
Bootstrap-Ablauf für Forgejo + GitOps
Grund für die Reihenfolge: Forgejo hängt an Postgres, aber Postgres/
cert-manager/reflector können nicht über GitOps kommen — ArgoCD selbst
braucht ja erst ein erreichbares Forgejo-Repo. Deshalb Schritte 2–5 wie
Forgejo/ArgoCD per direktem kubectl apply gebootstrapped; sobald ArgoCD
(Schritt 10) hochkommt, übernimmt es dieselben Ressourcen nahtlos in die
GitOps-Verwaltung. --server-side bei 2/3/9: einzelne CRDs überschreiten
die 262144-Byte-Grenze für client-side Apply. Details/Stolpersteine:
docs/troubleshooting.md.
- ✅
ansible-playbook playbooks/site.yml -K(testing) – k3s installieren, Kubeconfig lokal mergen, Root-CA + Sub-CA erzeugen. - ✅
kubectl apply -k apps/cert-manager/overlays/testing --server-side– cert-manager +ClusterIssuer local-ca-issuer. Muss vor Forgejo/ArgoCD (Schritt 6/9) und vor dem cnpg-operator-Schritt direkt danach laufen – dessen testing-Overlay installiert auch das Barman-Cloud-Plugin, dessenCertificate/Issuer-Ressourcen die cert-manager-CRDs brauchen (live beim ersten kompletten Restore-Test so aufgetreten, siehedocs/troubleshooting.md). - ✅
kubectl apply -k apps/cnpg-operator/overlays/testing --server-side– CloudNativePG-Operator. - ✅
kubectl apply -k apps/reflector/overlays/testing– reflector. - ✅
kubectl apply -k apps/service-db/overlays/testing–Clusterservice-db,bootstrap.initdberzeugt Datenbank+Rolleforgejo. - ✅
kubectl apply -k apps/forgejo/overlays/testing– Deployment/Service/ Ingress/Certificate, Bootstrap-Job legt Admin-Userfalk, Orglocal, Repolocal/k3san. - ✅
https://git.localim Browser erreichbar (Root-CA importiert,forgejo-tlsvon cert-manager ausgestellt). - ✅ Dieses Repo zu Forgejo gepusht (
git push forgejo main). - ✅
kubectl apply -k apps/argocd/overlays/testing --server-side– installiert ArgoCD selbst, derrepo-creds-jobmintet ein Forgejo-Zugriffstoken und legt das ArgoCD-Repository-Secret an.https://argocd.localerreichbar, Login mitadmin+ Passwort ausargocd-initial-admin-secret. - ✅
kubectl apply -k apps/root/overlays/testing– registriert die App-of-Apps (forgejo,argocd,rootselbst,cnpg-operator,cert-manager,reflector,service-db,sealed-secrets), ab hier verwaltet ArgoCD alle acht selbst aus Git (inkl. sich selbst über dieroot-Application). - Ab jetzt:
git pushzu Forgejo → ArgoCD reconciled automatisch sowohl Änderungen an bestehenden Apps als auch neueApplication-Manifeste unterapps/root/base/– auch das manuellekubectl apply -k apps/root/overlays/testingaus Schritt 10 entfällt für künftige Änderungen. - ⏳ Production:
ansible-playbook playbooks/site.yml -K -i inventory/production.ymlgegensrv202603.sunah.de, danach dieselben Schritte 2–6/9/10 mitoverlays/production --context=k3s-production– noch nicht durchgeführt, da SSH-Zugang zum Server noch nicht eingerichtet ist.
Alle Schritte 1–10 (testing) sind auch gebündelt in ./testing_run.sh
enthalten – das Skript erkennt pro Schritt, ob er schon erledigt ist, und
überspringt ihn (postet aber den Befehl zum manuellen Erzwingen mit
--force).
Kompletter Restore-Test (testing)
⏳ Ablauf noch nicht live durchgeführt (Stand siehe
docs/projekt/plan.md Roadmap-Punkt 6) – diese Anleitung ist der geplante
Weg, keine bereits verifizierte Prozedur. Bei Abweichungen im echten Lauf
hier korrigieren.
Ziel: kompletter Cluster-Verlust simulieren (Teardown wie beim normalen
Fresh-Rebuild-Test oben) und danach Forgejo/Postgres/Secrets aus dem
Backup statt aus Git/Ansible wiederherstellen. Die einzelnen
Restore-Befehle je Komponente (Postgres/Forgejo/Secrets) sind bereits
dokumentiert und live verifiziert – siehe docs/backup-recovery.md
Abschnitte "Backup-Verfahren"/"Restore-Verfahren" – und werden hier nicht
noch einmal wiederholt.
Sonderfall Garage + restic-Passwörter: Garage speichert die Backups
selbst als Pod im Testing-Cluster (local-path-PVC unter
/var/lib/rancher/k3s/storage/). playbooks/uninstall.yml löscht genau
dieses Verzeichnis – ein normaler Teardown würde also die Backups vor
ihrer eigentlichen Verwendung mit wegwerfen. Dieselbe Zirkularität gilt
für die beiden restic-Repository-Passwörter (Forgejo/Secrets, siehe
docs/backup-recovery.md Abschnitt "Zugangsdaten") – ohne sie sind die
verschlüsselten Snapshots in Garage nach einem kompletten Cluster-Verlust
zwar noch da, aber nicht mehr entschlüsselbar (restic: wrong password or no key found; live so aufgetreten, siehe docs/troubleshooting.md).
Deshalb sichert Schritt 1 unten zusätzlich Garages Rohdaten + die vier
Zugangs-Secrets + die beiden restic-Passwörter, die alle außerhalb des in
docs/backup-recovery.md dokumentierten Backup-Scope liegen, nach
backup/testing/ (lokal, per .gitignore ausgeschlossen).
- Frische Backups anstoßen, Garage-Rohdaten + Zugangs-/restic-Secrets
lokal nach
backup/testing/sichern und danach den Teardown ausführen – alles in einem Skript, mit Bestätigungsabfrage vor dem eigentlichen Uninstall:./scripts/backup/restore-drill-teardown.sh - Leeres Fresh-Rebuild (wie im normalen Reproduzierbarkeits-Test):
./testing_run.sh - Alten Garage-Zustand zurückspielen, bevor die Restore-Skripte aus
Schritt 4 laufen – sonst zeigen die auf die neue, leere Garage-Instanz
statt auf die alten Backups. Der
garage-bootstrap-PostSync-Hook aus Schritt 2 hat zwischenzeitlich schon ein frisches, leeres Layout + neue Keys angelegt; die zugehörigen k8s-Secrets werden hier bewusst überschrieben. Pausiert dafür automatisch ArgoCD komplett (sonst wird die Deployment-Skalierung sofort wieder zurückgedreht, siehedocs/troubleshooting.md) und reaktiviert es danach garantiert wieder, auch bei einem Fehler mittendrin:./scripts/backup/restore-drill-garage-restore.sh - Postgres/Forgejo/Secrets aus dem (jetzt wieder erreichbaren) Backup
restaurieren – Live-Restore, destruktiv, nur in testing:
./scripts/backup/backup-restore.sh testing postgres --live ./scripts/backup/backup-restore.sh testing forgejo --live ./scripts/backup/backup-restore.sh testing secrets --live - Verifizieren: Forgejo-Repos/Issues aus dem alten Stand vorhanden
(Login siehe "Anmelden bei Forgejo" oben), ArgoCD-Applications wieder
Synced/Healthy(die kommen aus Git, nicht aus dem Backup – prüft nur, dass GitOps nach dem Restore weiterläuft), Zitadel-OIDC-Login für Forgejo/ArgoCD funktioniert weiterhin.
Verworfene Alternativen (und warum)
- Rootless k3s: Netzwerk-Einschränkungen (privilegierte Ports, CNI-Features), weniger ausgereift getestet als der normale Modus.
- k3d: würde eine Docker/Podman-Rootless-Engine voraussetzen; verworfen, da System-weite k3s-Installation bevorzugt wurde.
xanmanning.k3s-Ansible-Galaxy-Rolle: zu umfangreich für den tatsächlichen Bedarf (nur Binary + Symlinks + Unit nötig).- Rohes
install.shvonget.k3s.io: kein "echtes" IaC, keine Ansible-native Idempotenz-Prüfung. - GitOps gegen GitHub: explizit nicht gewünscht – alles soll lokal bleiben, daher Forgejo im Cluster statt externem Hosting.
- Git-Hook (
post-commit→kubectl apply) statt GitOps-Controller: ursprünglich als einfachere Alternative vorgeschlagen, dann zugunsten von Forgejo + Flux/ArgoCD verworfen (bessere Reconciliation/Drift-Erkennung).