Das Prinzip
OKD (OpenShift Kubernetes Distribution) ist das Community-OpenShift. Single-Node (SNO) packt
Control-Plane und Workload auf einen Server: API (Application Programming Interface) auf 6443, Web-Console + Router auf
443, interne Registry, alle Cluster-Operatoren. Dein oc auf dem Laptop
(Superset von kubectl) spricht ueber HTTPS (Hypertext Transfer Protocol Secure) mit dem API-Server. Die
Installation selbst laeuft in WSL2 – openshift-install
gibt es nur fuer Linux.
steuert nur, rechnet nicht
alle Pods laufen hier
OKD-Nodes brauchen SCOS (CentOS Stream CoreOS) – ein unveraenderliches Container-OS, das der Machine Config Operator selbst verwaltet. Ein normales Ubuntu/CentOS taugt nicht.
Hetzner Cloud hat keinen SCOS-Katalogeintrag und erlaubt keinen
Image-Upload. Loesung: vom Rescue-System per kexec in die
SCOS-Live-Umgebung springen (Ignition als /config.ign ins initramfs eingebettet).
Die Live-Umgebung fuehrt dann bootstrap-in-place aus: temporaere
Control-Plane → coreos-installer schreibt SCOS + fertige Ignition auf
/dev/sda → Reboot in den fertigen Node.
Das macht alles scripts/sno.ps1 – du musst den Ablauf nicht
von Hand fahren (Abschnitt 7 zeigt ihn trotzdem).
Die fruehere MicroShift-Fassung ist raus: ihr Installations-Repo
(@redhat-et/microshift-COPR) liefert fuer CentOS Stream 9 nur noch einen
Build von 2022 mit unaufloesbaren Abhaengigkeiten. MicroShift wird heute praktisch nur noch
auf RHEL (Red Hat Enterprise Linux) 9.4+ mit Subscription unterstuetzt – und Hetzner hat kein
RHEL-Image. OKD SNO (Single-Node OpenShift) deckt denselben Zweck ab (voller oc-Workflow, Web-Console)
und wird gepflegt.
01 Voraussetzungen
Der einmalige Teil steht in Server-Basis:
Hetzner-Konto + API-Token, hcloud CLI mit Kontext, SSH-Key bei Hetzner
hinterlegt, oc/kubectl im PATH (Programmsuchpfad).
zusaetzlich WSL2
openshift-install laeuft nur unter Linux. Einmalig eine WSL2-Distribution
installieren (Neustart noetig):
wsl --install -d Ubuntu
wsl -l -v # muss die Distro mit VERSION 2 zeigen
sno.ps1holtopenshift-install+ocselbst in die WSL (nach~/okd-sno/bin) – nichts weiter einzurichten.- Der WSL-Traffic geht ueber dieselbe oeffentliche IP (Internet Protocol) wie der Laptop – die Firewall-Regel fuer 6443 passt also automatisch.
- Server-Groesse: das offizielle SNO-Minimum ist 8 vCPU / 16 GB
(
cpx42) – das reicht fuer OKD leer (OKD belegt selbst schon ~11 GB RAM, CPU ist nie das Thema).sno.ps1nimmt darum standardmaessigcpx52(12 vCPU / 24 GB), damit auch eigene Workloads Platz haben.ccx33(8 dediziert / 32 GB) laeuft am ruhigsten. Konkrete Auslastung mit einem ganzen Projekt-Stack: Bibliothek-Enterprise per Jenkins auf OKD.
02 Installation mit sno.ps1
Ein Befehl. Danach ~30–45 min warten (die ersten ~5 min mit Rueckfragen bei
neuem SSH-Host-Key, der Rest laeuft durch). Server, Rescue, Ignition, kexec,
wait-for – alles automatisch.
.\scripts\sno.ps1 install
# Vorgabe: -Type cpx52 (12 vCPU / 24 GB)
# optional: -Type cpx42 (16 GB, nur OKD leer) | -Type ccx33 (dediziert, ruhiger)
# -Location fsn1 (anderer Standort)
Was das Skript der Reihe nach tut:
- neueste
okd-scos-Version ermitteln,openshift-install+ocin die WSL holen - Firewall
okd-sno-fw(22+6443 nur von deiner IP, 80+443 offen) und eine reservierte Primary-IPv4okd-sno-ipanlegen – die feste IP ist wichtig, weil die API-Zertifikate und dienip.io-Namen an ihr haengen - Server
okd-sno(cpx52, Ubuntu als Rescue-Traeger) erstellen, Rescue aktivieren, Hard-Reset (ein Soft-Reboot geht nicht in den Rescue) - in der WSL die
install-config.yaml+single-node-ignition-configerzeugen (mit der festen IP:baseDomain: <ip>.nip.io, Pull-Secret ist ein Dummy – OKD braucht kein echtes) - Ignition +
sno-kexec.shins Rescue hochladen, dort SCOS-Live-Kernel/initramfs holen, Ignition als/config.ign(xz-cpio) anhaengen,kexec - vom Laptop aus
openshift-install wait-for install-completegegenapi.sno.<ip>.nip.io:6443 - kubeconfig nach
~\.kube\okd-sno-hetzner.yamlkopieren,KUBECONFIGsetzen, Console-URL + kubeadmin-Passwort ausgeben
Der haeufigste Punkt ist zu wenig RAM (auf cpx42) waehrend der
Operator-Konvergenz. oc get clusterversion / oc get co zeigen den
Stand. Oft laeuft es mit ein paar Minuten mehr doch durch. Sonst
.\scripts\sno.ps1 destroy und mit dem groesseren Vorgabe-Typ
(cpx52) oder -Type ccx33 neu.
Bricht install direkt nach „kexec … ausloesen“ ab,
laeuft der Server trotzdem schon los. Pruefen: ssh core@<ip>
– zeigt es „CentOS Stream CoreOS“, dann nicht neu installieren, sondern nur
weiterwarten:
wsl -e bash scripts/sno-wsl.sh wait und danach
… sno-wsl.sh finish /mnt/c/Users/<du>/.kube/okd-sno-hetzner.yaml.
03 Was dabei passiert (zum Mitlesen)
Waehrend wait-for laeuft, kannst du dem Node zusehen –
.\scripts\sno.ps1 ssh (User core).
| Phase | ca. | woran erkennbar |
|---|---|---|
SCOS-Live bootet, node-image-pull + release-image | ~5–10 min | ssh core@… geht, OS (Betriebssystem) = „CentOS Stream CoreOS“ |
bootkube – temporaere Control-Plane, rendert die Master-Ignition | ~6 min | journalctl -b -u bootkube -f |
install-to-disk – coreos-installer schreibt /dev/sda, dann Reboot | ~1 min | SSH (Secure Shell) bricht ab |
Reboot in den Node, bootstrap-in-place-post-reboot: CSRs, alle Operatoren, CVO | ~20–30 min | API 6443 antwortet, oc get co |
Der Node rebootet dabei ein- bis zweimal selbst (Machine Config Operator). Das ist normal.
04 Cluster pruefen
$env:KUBECONFIG = "$env:USERPROFILE\.kube\okd-sno-hetzner.yaml"
oc whoami # -> system:admin
oc get clusterversion # AVAILABLE=True, "Cluster version is 4.x.x-okd-scos.y"
oc get co # alle 34 auf AVAILABLE=True
oc get nodes -o wide # 1 Node, ROLES control-plane,master,worker, cri-o
.\scripts\sno.ps1 console oeffnet
https://console-openshift-console.apps.sno.<ip>.nip.io
und zeigt Login kubeadmin + Passwort (aus ~/okd-sno/cfg/auth/kubeadmin-password
in der WSL). Zertifikat ist selbstsigniert – im Browser einmal bestaetigen.
KUBECONFIG dauerhaft: hat install schon per
SetEnvironmentVariable(…,'User') gesetzt – in neuen Terminals aktiv.
Zuruecksetzen macht sno.ps1 down / destroy.
05 Erstes Projekt, App & Route
Normaler OpenShift-Workflow – wie im grossen Cluster.
oc new-project demo
oc new-app --image=docker.io/library/nginx --name=web
oc expose service/web # -> Route auf *.apps.sno.<ip>.nip.io
oc get route
# -> http://web-demo.apps.sno.<ip>.nip.io (Port 80/443 sind fuer alle offen)
- S2I-Builds,
BuildConfig,DeploymentConfig, SCCs (Security Context Constraints),oc adm– alles da, es ist ein echtes OpenShift. - Auto-Deploy: Manifeste in Git + Argo CD (Continuous Delivery)/Flux (OLM/OperatorHub ist installiert).
- Ganzes Projekt per CI (Continuous Integration): Bibliothek-Enterprise per Jenkins auf OKD – Jenkins baut die Images, pusht in die interne Registry, deployt per Helm (jede Falle dokumentiert).
- Privat statt Route:
oc port-forward svc/web 8080:80.
06 Kosten steuern: down / up
Preise sind Richtwerte. Hetzner rechnet stundenweise ab, solange der Server existiert – herunterfahren spart nichts, nur loeschen. Aktuelle Preise + Rechner: Kosten & Budget.
.\scripts\sno.ps1 down # kubelet stoppen -> Snapshot -> Server loeschen (IP + Snapshot bleiben)
.\scripts\sno.ps1 up # Server aus dem Snapshot, gleiche feste IP -> Cluster kommt zurueck
.\scripts\sno.ps1 status # laeuft er? seit wann? ~Kosten? API erreichbar?
.\scripts\sno.ps1 destroy # alles weg: Server, Snapshot, reservierte IP, Firewall
| Posten | €/Std | €/Tag | €/Monat |
|---|---|---|---|
Server CPX52 (12 vCPU / 24 GB, Vorgabe), waehrend er laeuft | ~0,138 | ~3,30 | ~100 |
Server CPX42 (16 GB, nur OKD leer), waehrend er laeuft | ~0,095 | ~2,28 | ~69 |
reservierte Primary-IP (bleibt bei down) | ~0,0008 | ~0,02 | ~0,60 |
Snapshot des konvergierten Node (~10 GB, bleibt bei down) | – | ~0,005 | ~0,15 |
Nach down laufen nur IP + Snapshot weiter (~0,75 €/Monat). Eine 3-h-Session
auf cpx52 ist ~40 Cent. Warum die feste IP: API-Zertifikate
und alle nip.io-Namen sind auf die IP ausgestellt – mit einer neuen IP muesste
man neu installieren.
.\scripts\create-shortcuts.ps1 -For sno legt Desktop-Icons
OKD-SNO-up/-down/-status an. .\scripts\install-autodown.ps1 -For sno
loescht den Node jede Nacht um 03:00 (Windows-Aufgabenplanung). Der wichtigste Kostenschutz,
weil Hetzner kein Ausgabenlimit hat. Voller Kill-Switch-Ueberblick:
Server-Snapshot & Restore.
+ Manuell (ohne sno.ps1)
Falls du den Ablauf verstehen oder anpassen willst. Die drei Bausteine im Repo:
scripts/sno.ps1 (Orchestrierung), scripts/sno-wsl.sh (Linux-Teil),
scripts/sno-kexec.sh (Rescue-Teil).
1 WSL: Werkzeuge + Ignition
W=~/okd-sno; mkdir -p $W/bin; cd $W
OKD=4.22.0-okd-scos.8 # neueste: github.com/okd-project/okd/releases/latest
curl -L https://github.com/okd-project/okd-scos/releases/download/$OKD/openshift-install-linux-$OKD.tar.gz | tar xz -C bin
curl -L https://github.com/okd-project/okd-scos/releases/download/$OKD/openshift-client-linux-$OKD.tar.gz | tar xz -C bin oc kubectl
apiVersion: v1
baseDomain: <ip>.nip.io
metadata:
name: sno
compute:
- { name: worker, replicas: 0 }
controlPlane:
name: master
replicas: 1
networking:
networkType: OVNKubernetes
machineNetwork:
- cidr: <ip>/32
platform:
none: {}
bootstrapInPlace:
installationDisk: /dev/sda
pullSecret: '{"auths":{"fake":{"auth":"aWQ6cGFzcwo="}}}'
sshKey: |
<dein id_ed25519.pub>
bin/openshift-install --dir cfg create single-node-ignition-config
# -> cfg/bootstrap-in-place-for-live-iso.ign (+ cfg/auth/kubeconfig, kubeadmin-password)
bin/openshift-install coreos print-stream-json | grep -oE 'https://[^"]+live-(kernel|initramfs|rootfs)[^"]*'
2 Hetzner: Server + Rescue
hcloud primary-ip create --type ipv4 --name okd-sno-ip --location hel1 --assignee-type server
hcloud server create --name okd-sno --type cpx52 --image ubuntu-24.04 `
--ssh-key laptop --firewall okd-sno-fw --location hel1 --primary-ipv4 okd-sno-ip
hcloud server enable-rescue okd-sno --ssh-key laptop
hcloud server reset okd-sno # HARD reset - Soft-Reboot geht nicht in den Rescue
Vier Aufrufe, und der letzte klingt harmloser als er ist:
| Schalter | gehoert zu | bewirkt |
|---|---|---|
hcloud primary-ip create --type ipv4 |
zuerst | Reserviert eine oeffentliche IPv4-Adresse als eigenstaendiges Objekt, unabhaengig vom Server. Normalerweise gehoert die Adresse zum Server und ist mit ihm weg; hier bleibt sie bestehen. Das ist der Grund, warum der Cluster spaeter geloescht und aus einem Snapshot neu aufgebaut werden kann, ohne dass DNS und Zertifikate nachgezogen werden muessen. Kostet rund 0,50 EUR im Monat, auch waehrend kein Server daran haengt. |
--assignee-type server |
primary-ip | Legt fest, woran die Adresse spaeter haengen darf. Aktuell gibt es nur diese eine Art – die Angabe ist trotzdem Pflicht, sonst lehnt die API den Aufruf ab. |
--location hel1 (zweimal) |
primary-ip, server | Muss an beiden Stellen identisch sein. Eine reservierte Adresse ist an ihren Standort gebunden und laesst sich nicht an einen Server in einem anderen Rechenzentrum haengen – der server create scheitert dann, nachdem die Adresse bereits angelegt (und berechnet) ist. |
--primary-ipv4 okd-sno-ip |
server create | Haengt die eben reservierte Adresse an den neuen Server, statt eine beliebige neue zu ziehen. Der Name aus dem ersten Aufruf, keine Zahl. |
--firewall okd-sno-fw |
server create | Haengt die Firewall schon bei der Erstellung an, damit der Server nicht erst ein paar Minuten ungeschuetzt im Netz steht. Auffaellig ist, was fehlt: kein --user-data-from-file. Das Ubuntu-Image ist hier nur ein Vehikel, um in den Rescue-Modus zu kommen – das eigentliche Betriebssystem wird gleich darueber installiert. |
enable-rescue --ssh-key laptop |
Rescue | Schaltet fuer den naechsten Start ein Notfall-Linux ein, das aus dem Netz gebootet wird und die Platte unangetastet laesst. Genau das braucht der coreos-installer spaeter: er ueberschreibt /dev/sda, was aus einem laufenden System heraus nicht ginge. Ohne --ssh-key vergibt Hetzner ein Einmal-Passwort und zeigt es nur einmal an. |
hcloud server reset |
Rescue | Ein harter Reset – das Gegenstueck zum Ausschalten am Netzschalter, ohne dass das laufende System etwas davon mitbekommt. Noetig, weil ein sauberes reboot das bestehende System wieder von der Platte startet und den Rescue-Modus schlicht ueberspringt. Auf einem Server mit Daten waere derselbe Befehl gefaehrlich; hier ist die Platte im naechsten Schritt ohnehin dran. Der verwandte Befehl hcloud server reset-password tut etwas voellig anderes – nicht verwechseln. |
3 Rescue: kexec in SCOS-Live
Ignition + scripts/sno-kexec.sh ins Rescue kopieren, dann:
KERNEL_URL=… INITRD_URL=… ROOTFS_URL=… IGN=/root/sno.ign bash /root/sno-kexec.sh
Das Skript installiert kexec-tools, laedt Kernel+initramfs, haengt die Ignition
als /config.ign (xz-cpio) an das initramfs (genau das macht
coreos-installer pxe customize intern – der Container-Weg scheitert im Rescue an
fehlendem BPF) und loest systemctl kexec aus. Ab hier laeuft alles selbst.
4 Warten & kubeconfig
bin/openshift-install --dir cfg wait-for install-complete
cp cfg/auth/kubeconfig /mnt/c/Users/<du>/.kube/okd-sno-hetzner.yaml
cat cfg/auth/kubeadmin-password
+ Absicherung
- API + SSH nur ueber Tailscale –
okd-sno-fwlässt nur noch 80/443 offen (App-Routes). 22 und 6443 sind zu.oc/helm/SSH laufen übers Tailnet (siehe Kasten). Klassisch mit Firewall-Regeln von deiner IP geht auch – dann$TailscaleHost = ''insno.ps1.
Ausnahme: der Hetzner-Jenkins (Jenkins → OKD) ist nicht im Tailnet –jenkins.ps1stanzt daher beimupeine eigene 6443-Regel inokd-sno-fwund raeumt sie beimdownwieder weg. Ein lokal betriebener Jenkins braucht das nicht. - kubeadmin ist Voll-Admin – nach dem Einrichten eines eigenen Identity-Providers (htpasswd/OIDC) abschalten. Die kubeadmin-kubeconfig nicht weitergeben.
- SCOS updatet sich ueber den Cluster (
oc adm upgrade) – keindnfauf dem Node. - SSH zum Node nur als
coremit Key.
curl -fsSL https://tailscale.com/install.sh | sh scheitert auf
dem Node: SCOS ist immutable, kein dnf, keine Paketquellen
(Error: There are no enabled repositories). Der CoreOS-Weg – statische
Binärdateien + systemd-Unit:
VER=$(curl -fsSL 'https://pkgs.tailscale.com/stable/?mode=json' | grep -oE '"Version":"[0-9.]+"' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
curl -fsSL -o /tmp/ts.tgz "https://pkgs.tailscale.com/stable/tailscale_${VER}_amd64.tgz"
sudo tar xzf /tmp/ts.tgz -C /tmp
cd /tmp/tailscale_${VER}_amd64
sudo install -m 0755 tailscale tailscaled /usr/local/bin/
sudo sed 's#/usr/sbin/tailscaled#/usr/local/bin/tailscaled#' systemd/tailscaled.service | sudo tee /etc/systemd/system/tailscaled.service >/dev/null
sudo cp systemd/tailscaled.defaults /etc/default/tailscaled
# sudo hat /usr/local/bin nicht im PATH -> ergaenzen (SCOS-minimal):
echo 'Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"' | sudo tee /etc/sudoers.d/10-localpath >/dev/null
sudo systemctl daemon-reload && sudo systemctl enable --now tailscaled
sudo /usr/local/bin/tailscale up --ssh --hostname sno # druckt Login-URL
Danach:
- Im Admin-Panel bei
okd-sno→ Disable key expiry (sonst faellt der Node nach 180 Tagen raus). - hosts-Eintrag auf dem Laptop (Admin-PowerShell), damit der API-Zert-SAN
passt – die kubeconfig bleibt unverändert:
Add-Content $env:SystemRoot\System32\drivers\etc\hosts "<tailscale-ip>`tapi.sno.<ip>.nip.io" $TailscaleHostinsno.ps1auf den MagicDNS-Namen setzen (<cluster>.<tailnet>.ts.net) →ssh/down/status/uplaufen dann über Tailscale.okd-sno-fwauf nur 80/443 setzen:hcloud firewall replace-rules okd-sno-fw --rules-file <json mit 80+443>.- SELinux-Warnung
Tailscale SSH may not work: das--ssh-Feature (Login ohne Key) braucht ein SELinux-Modul, das SCOS nicht hat. Klassischesssh core@<cluster>.<tailnet>.ts.net(key-basiert) geht trotzdem – und reicht fuers „kein offener Port“-Ziel.
Alternativen (rpm-ostree, Container) +
ACLs/MagicDNS/Exit-Node:
Tailscale-Anleitung.
Grundausstattung-Checkliste – 2FA aufs Hetzner-Konto, Tailscale, Backup ausser Haus, Uptime-Monitoring, Log-Limits.
+ Grenzen & andere Wege
- Keine HA (Hochverfügbarkeit). Ein Node = ein Ausfallpunkt. Fuer „echt betreiben“ der mehrknotige OKD-Cluster (Stufe 4).
- RAM statt CPU (Central Processing Unit) ist der Engpass. OKD selbst belegt ~11 GB (CPU
< 1 Kern im Leerlauf).
cpx42(16 GB) reicht nur fuer OKD leer, Vorgabe ist darumcpx52(24 GB). Mit einem ganzen Projekt-Stack (Beispiel: ~15,6 GB gesamt) istcpx52knapp bequem,ccx33/cpx62entspannt. - Nur Kubernetes, ohne OpenShift-Schicht: die k3s-Anleitung – leichter, schneller, ~512 MB (Megabyte) Overhead.
- Nur
ocueben, kostenlos: die Developer Sandbox (Stufe 0) – gehostetes OpenShift von Red Hat, kein Server. - nip.io hatte schon laengere Ausfaelle. Fuer mehr als ein Lab eine echte
Wildcard-Domain (
*.apps.sno.deine-domain) auf die feste IP zeigen lassen.
| Option | Art | Bemerkung |
|---|---|---|
| ROSA (Red Hat OpenShift Service on AWS) / ARO (Azure Red Hat OpenShift) | managed | OpenShift auf AWS (Amazon Web Services) bzw. Azure, mit Console/OperatorHub. ~$0,17/h Control-Plane + Worker. |
| OpenShift Dedicated | managed | Von Red Hat gehostet auf AWS/GCP (Google Cloud Platform). Ab ~$1k/Monat. |
| CRC / OpenShift Local | lokal | SNO auf dem Laptop (~4 vCPU / 9 GB). Zum Entwickeln, nicht „remote“. |
| Assisted Installer | self-host | Web-UI erzeugt eine Discovery-ISO. Braucht ISO-Boot – bei Hetzner Cloud nicht moeglich, daher hier der kexec-Weg. |