01 Dimensionierung
Die Vorgabewerte reichen für einen leeren Cluster. Sobald etwas darauf laufen soll, nicht mehr.
| Einstellung | Vorgabe | Praxiswert | Woran man merkt, dass es zu wenig ist |
|---|---|---|---|
memory | 10752 | 16384 | Bei 10,5 GB sind 93 % des Node-Speichers schon von OpenShift selbst angefordert. Neue Pods bleiben Pending mit Insufficient memory. |
disk-size | 31 | 45+ | Node bekommt den Taint disk-pressure, laufende Pods werden verdrängt. Der leere Cluster belegt allein schon rund 23 GB. |
cpus | 4 | 6 | JVM-Kaltstarts dauern Minuten; siehe Diagnose. |
crc config set memory 16384
crc config set disk-size 45
crc config set cpus 6
crc config view
# Alle drei greifen erst beim naechsten Start:
crc stop; crc start
disk-size lässt sich jederzeit erhöhen – CRC vergrößert die VHDX
und die Partition beim nächsten Start automatisch. Zurück geht es nicht ohne
crc delete. Der Wert ist zugleich das Minimum 31; kleiner lässt CRC
nicht zu, weil das Bundle-Image selbst schon so groß ist.
02 Was auf die SSD gehört
Nicht alles unter ~/.crc ist gleich empfindlich.
| Verzeichnis | Größe | Zugriff | Wohin |
|---|---|---|---|
~/.crc/machines/ | ~30 GB | ständig, wahlfrei – hier liegt die VM-Disk | SSD |
~/.crc/cache/ | ~35 GB | nur beim Start und beim Entpacken, sequentiell | HDD reicht |
~/.crc/bin/ | < 1 GB | selten | egal |
Gemessen mit der VHDX auf einer 2,5-Zoll-HDD:
| VM-Disk auf HDD | VM-Disk auf NVMe | |
|---|---|---|
| iowait beim Start | 62–74 % | 0–9 % |
| Load (6 vCPU) | bis 25 | Spitze 20, dann ~2 |
crictl images | 22 s | < 1 s |
Start bis Running | ~45 Min, mit Handeingriff | ~6 Min |
Auf der HDD meldet crc start zuverlässig cluster operators are still not stable
after 11m, und die Pods hängen reihenweise in
ImageInspectError: DeadlineExceeded. Das sieht nach einem kaputten Cluster aus, ist aber nur
CRI-O, das seine Zeitfenster nicht einhält, weil die Platte nicht hinterherkommt.
03 Cache auslagern – und die Junction-Falle
Wenn die SSD zu klein ist, kann der Cache auf die große Platte. Aber nicht bedingungslos.
CRC kennt keine Konfiguration für einen abweichenden Cache-Pfad. Der übliche Weg ist deshalb eine Junction – ein Verzeichnis-Verweis auf NTFS-Ebene, den CRC gar nicht bemerkt:
# Cache auf die grosse Platte verlagern, machines/ bleibt auf der SSD
robocopy "%USERPROFILE%\.crc\cache" "D:\OpenShift-Local\.crc\cache" /E /MOVE
mklink /J "%USERPROFILE%\.crc\cache" "D:\OpenShift-Local\.crc\cache"
Seit CRC 2.63 werden Bundles als OCI-Artefakt geladen, nicht mehr per HTTP. Die dahinterliegende Bibliothek legt ein temporäres Zielverzeichnis im Cache an – und das schlägt in einem Reparse-Point fehl:
level=error msg="invalid destination name C:\Users\...\.crc\cache\tmpBundleImage3964368163: The system cannot find the path specified."
Lesen und Entpacken durch die Junction funktionieren einwandfrei – nur das Anlegen bricht. Der laufende Betrieb ist also nicht betroffen, sondern ausschließlich der einmalige Bundle-Download. Vorgehen bei einem Versions-Update:
- Junction entfernen –
rmdirohne/slöst nur den Verweis, die Daten auf der anderen Platte bleiben - Echtes
cache-Verzeichnis auf der SSD anlegen (braucht ~35 GB frei) crc setuplaufen lassen- Cache zurück auf die große Platte kopieren, verifizieren, Quelle löschen
- Junction neu setzen
crc cleanup, nie den Uninstaller
Beide arbeiten auf Deinstallations-Ebene und können ~/.crc mitsamt Junction entfernen.
Ein anschließendes crc setup legt dann ein echtes Verzeichnis auf der
Systemplatte an und zieht rund 35 GB dorthin. crc delete ist dagegen unbedenklich –
es läuft durch die Junction und lässt sie stehen. Der MSI-Installer eines Versions-Updates
fasst das Verzeichnis ebenfalls nicht an.
04 Der Pod, der die Platte frisst
Ein Defekt in dieser OKD-Version, der reproduzierbar den ganzen Cluster lahmlegt.
openshift-console/downloads schreibt fortlaufend Temporärdateien in sein
emptyDir und räumt sie nie auf. Gemessen: 17 GB in 3,5 Stunden,
verteilt auf mehrere Dateien zu je 3,1 GB – also rund 5 GB pro Stunde. Irgendwann greift
DiskPressure, der Node bekommt einen NoSchedule-Taint, und laufende Pods werden
verdrängt.
Das Symptom sieht völlig anders aus als die Ursache: Pods, die eben noch liefen, stehen plötzlich
auf Pending mit node(s) had untolerated taint(s).
# Groesstes emptyDir in der VM aufspueren
ssh -i ~/.crc/machines/crc/id_ed25519 -p 2222 core@127.0.0.1 \
"sudo du -xh --max-depth=1 /var/lib/kubelet/pods | sort -rh | head -5"
# Pod-UID aus dem Pfad einem Namen zuordnen
oc get pods -A -o jsonpath='{range .items[*]}{.metadata.uid}{" "}{.metadata.namespace}{"/"}{.metadata.name}{"\n"}{end}' \
| grep <uid>
# Ohne Unmanaged skaliert der Console-Operator das Deployment sofort zurueck
oc patch console.operator.openshift.io/cluster --type=merge \
-p '{"spec":{"managementState":"Unmanaged"}}'
oc scale deploy/downloads -n openshift-console --replicas=0
Der Pod liefert in der Web-Konsole nur die Downloadseite für oc, kubectl
und verwandte Werkzeuge. Auf einem lokalen Entwicklungscluster kostet sein Abschalten nichts – und
es gibt sofort 17 GB zurück. Die Web-Konsole selbst läuft weiter.
05 Diagnose: Load richtig lesen
Dieselbe Kennzahl, zwei völlig verschiedene Ursachen.
Der Load Average unter Linux zählt Prozesse, die laufen, auf einen Kern warten – oder in ununterbrechbarem Warten stecken, praktisch immer auf Platten-I/O. Genau deshalb sagt die Zahl allein noch nicht, woran es klemmt. Der Bezugspunkt ist die Kernzahl: Bei 6 vCPUs bedeutet Load 6 volle Auslastung ohne Warteschlange.
# CRC 2.6x hat kein "crc ssh" - direkt ueber den Bundle-Schluessel
ssh -i ~/.crc/machines/crc/id_ed25519 -p 2222 \
-o StrictHostKeyChecking=no core@127.0.0.1 \
"cat /proc/loadavg; vmstat 1 2 | tail -1; df -h /var | tail -1"
Entscheidend sind die letzten Spalten von vmstat:
| Beobachtung | Load | vmstat | Ursache | Was hilft |
|---|---|---|---|---|
| Start dauert ewig, VM-Disk auf HDD | 20+ | wa=74 us=6 |
Platten-I/O | VM-Disk auf SSD |
| Viele JVMs starten gleichzeitig | 157 | wa=0 us=64 sy=35 id=0 |
CPU-Sättigung | gestaffelt starten |
Bei anhaltender CPU-Sättigung verhungert der kube-apiserver
(TLS handshake timeout). Dann schlagen die Probes fehl, Liveness tötet Pods, jeder
Neustart erzeugt einen weiteren Kaltstart – eine Rückkopplung, die sich selbst nährt.
In diesem Zustand läuft sogar crictl stop in Timeouts.
Ausweg: crc stop, crc start, und während der Cluster
hochfährt die Anwendungs-Deployments auf 0 skalieren, sobald die API antwortet. Danach einzeln
hochfahren.
06 Auslastung protokollieren
Weil die interessanten Zustände meist dann auftreten, wenn man nicht hinschaut.
Nützlich sind Host- und VM-Werte nebeneinander – und in der VM ausdrücklich
iowait, weil erst das die beiden Fälle aus Abschnitt 05 trennt.
Ein PowerShell-Skript, das im Minutentakt eine CSV schreibt, reicht dafür völlig.
| Spalte | Woher | Wozu |
|---|---|---|
| Host-CPU, Host-RAM | WMI | Merkt man, wenn Windows selbst knapp wird |
| freier Platz je Laufwerk | Get-PSDrive | VHDX-Wachstum im Blick behalten |
| VM-CPU, zugewiesenes RAM | Get-VM | Was Hyper-V der VM tatsächlich gibt |
Load, us/sy/id/wa, /var | SSH in die VM | Der eigentliche Diagnosewert |
| fehlerhafte Pods und Operatoren | oc | Wirkung auf den Cluster |
vmstat darf man nicht über feste Spaltenpositionen auswerten: Neuere
procps-ng-Versionen hängen hinter st noch eine Spalte gu an,
wodurch alle Indizes verrutschen. Die Spalten über die Kopfzeile auflösen.
Und der Hyper-V-Speicher der VM taucht auf dem Host nicht als Prozess auf – ihn liefert
nur Get-VM, nicht Get-Process.