Was gegenüber OKD anders ist
Drei Dinge – und eine Konsequenz, die man nicht umgehen kann.
| Hetzner-OKD | CRC lokal | |
|---|---|---|
| Registry | clusterintern, über externe Route | clusterintern, über default-route-openshift-image-registry.apps-crc.testing |
| Overlay | env/okd-values.yaml | env/crc-values.yaml |
| Domäne | apps.sno.<ip>.nip.io | apps-crc.testing |
| Node | 32 GB, dedizierter Server | 6 vCPU / 16 GB in einer Hyper-V-VM |
Auf sechs vCPUs können die acht Spring-Boot-Dienste nicht gleichzeitig kalt starten.
Der Deploy legt sie deshalb mit replicas=0 an, ein zweites Skript fährt sie einzeln hoch.
Warum das keine Vorsichtsmaßnahme, sondern zwingend ist, steht in Schritt 06.
01 CRC dimensionieren
Die Vorgabewerte reichen für diesen Stack nicht. Alle drei Änderungen greifen erst beim Neustart.
| Einstellung | Vorgabe | Nötig | Grund |
|---|---|---|---|
memory | 10752 | 16384 | Bei 10,5 GB sind bereits 93 % des Node-Speichers von OpenShift selbst angefordert – es bleiben rund 700 MiB für Anwendungen |
disk-size | 31 | 45 | Jedes Image liegt doppelt: einmal in der Registry-Storage, einmal in CRI-O nach dem Pull |
cpus | 4 | 6 | Weniger macht die JVM-Kaltstarts unzumutbar langsam |
crc config set memory 16384
crc config set disk-size 45
crc config set cpus 6
crc stop; crc start # disk-size waechst beim Start, die Partition wird mitvergroessert
crc start bricht mit unable to allocate … MB of RAM ab
Hyper-V reserviert den konfigurierten memory-Wert als Startup-RAM exklusiv
für die CRC-VM. Ist auf dem Host nicht genug frei – typischerweise weil eine
andere Hyper-V-VM parallel läuft (z. B. eine minikube-VM) –
schlägt der Start mit Insufficient system resources exist to complete the requested service
fehl. Prüfen mit Get-VM | Select Name,State,MemoryStartup; nicht benötigte
VMs vorher mit Stop-VM <name> stoppen, oder memory in der Tabelle
oben reduzieren, bis es in den freien Speicher passt.
~/.crc/machines/ enthält die VM-Disk und gehört auf eine SSD.
Liegt sie auf einer klassischen Festplatte, ist der Start vollständig I/O-gebunden – gemessen:
iowait 62–74 %, crictl images braucht 22 Sekunden, und daraus entstehen
reihenweise ImageInspectError: DeadlineExceeded. Der Bundle-Cache unter
~/.crc/cache/ darf dagegen auf der langsamen Platte liegen – er wird nur beim Start gelesen.
02 Jars und Images bauen
Den Maven-Reactor einmal auf dem Host bauen statt neunmal im Container.
# 1. Alle Module bauen - erzeugt die *-exec.jar je Service
cd library-platform; mvn -B package -DskipTests; cd ..
# 2. Images aus den fertigen Jars - Dockerfile.prebuilt-jar hat KEINE Maven-Stage
for s in api-gateway catalog-service config-server discovery-server fine-service \
lending-service member-service notification-service reservation-service; do
podman build -f infra/Dockerfile.prebuilt-jar --build-arg MODULE=$s --build-arg PORT=8080 \
-t library/$s:crc library-platform
done
podman build -f library-frontend/Dockerfile -t library/library-frontend:crc library-frontend
# 1. Alle Module bauen - erzeugt die *-exec.jar je Service
cd library-platform; mvn -B package -DskipTests; cd ..
# 2. Images aus den fertigen Jars - Dockerfile.prebuilt-jar hat KEINE Maven-Stage
$services = "api-gateway","catalog-service","config-server","discovery-server","fine-service",
"lending-service","member-service","notification-service","reservation-service"
foreach ($s in $services) {
podman build -f infra/Dockerfile.prebuilt-jar --build-arg MODULE=$s --build-arg PORT=8080 `
-t library/${s}:crc library-platform
}
podman build -f library-frontend/Dockerfile -t library/library-frontend:crc library-frontend
Cannot connect to Podman nach längerer Standzeit
Setzt podman machine start voraus – ohne läuft jeder Befehl oben
gegen einen toten Socket. Reicht ein einfacher Start nicht und bleibt der Fehler
ssh: rejected: connect failed (open failed) bzw. podman info hängt, liegt es
am systemd-User-Manager innerhalb der WSL-Maschine: podman machine ssh
podman-machine-default sudo systemctl status user@1000.service zeigt dann
Failed to spawn executor: Device or resource busy. Ein bloßes
podman machine stop/start behebt das nicht – nur ein
vollständiger wsl --shutdown (beendet alle WSL-Distros, nicht nur diese)
gefolgt von podman machine start räumt den hängenden Zustand weg.
Dockerfile.prebuilt-jar
Das reguläre library-platform/Dockerfile baut Maven im Container – hermetisch und
reproduzierbar, aber bei neun Images baut es den Reactor neunmal. Auf einem Laptop ist das der Unterschied
zwischen Minuten und einer Stunde. Für CI bleibt das reguläre Dockerfile die richtige Wahl.
discovery-server wird gebaut, aber nicht deployt – in
Kubernetes übernimmt die Cluster-DNS die Rolle von Eureka. Das ist auf OKD genauso.
03 In die CRC-Registry pushen
Erst die Route der clusterinternen Registry aktivieren, dann anmelden und schieben.
oc patch configs.imageregistry.operator.openshift.io/cluster \
--patch '{"spec":{"defaultRoute":true}}' --type=merge
oc new-project library
R=default-route-openshift-image-registry.apps-crc.testing
oc login -u kubeadmin -p "$(cat ~/.crc/machines/crc/kubeadmin-password)" \
https://api.crc.testing:6443 --insecure-skip-tls-verify=true
podman login --tls-verify=false -u kubeadmin -p "$(oc whoami -t)" $R
for s in api-gateway catalog-service config-server fine-service lending-service \
member-service notification-service reservation-service library-frontend; do
podman tag library/$s:crc $R/library/$s:crc
podman push --tls-verify=false $R/library/$s:crc
done
oc, helm und podman sind
auch unter Windows native Binaries und liefen genauso in PowerShell – das Label oben bezieht sich
nicht auf die Tools, sondern auf die Shell-Syntax im Block: die Variablenzuweisung
R=…, die Command-Substitution $(cat …) und die Schleife
for … do … done sind POSIX-Bash und scheitern 1:1 in PowerShell. Wer lieber
durchgehend PowerShell nutzt, findet das Äquivalent direkt darunter.
Eigene Falle im Äquivalent: Windows PowerShell reicht ein einfach-gequotetes
'{"spec":…}' an eine native .exe wie oc weiter,
indem es dabei die inneren " entfernt – oc bekommt dann
{spec:{defaultRoute:true}} ohne Anführungszeichen und meldet
invalid character 's' looking for beginning of object key string. Abhilfe: die inneren
" mit \ escapen, wie im Block unten.
oc patch configs.imageregistry.operator.openshift.io/cluster `
--patch '{\"spec\":{\"defaultRoute\":true}}' --type=merge
oc new-project library
$R = "default-route-openshift-image-registry.apps-crc.testing"
oc login -u kubeadmin -p (Get-Content "$HOME\.crc\machines\crc\kubeadmin-password") `
https://api.crc.testing:6443 --insecure-skip-tls-verify=true
podman login --tls-verify=false -u kubeadmin -p (oc whoami -t) $R
$services = "api-gateway","catalog-service","config-server","fine-service",
"lending-service","member-service","notification-service",
"reservation-service","library-frontend"
foreach ($s in $services) {
podman tag library/${s}:crc $R/library/${s}:crc
podman push --tls-verify=false $R/library/${s}:crc
}
127.0.0.1 des Hosts nicht
Unter Windows läuft Podman in einer WSL2-Maschine. CRC veröffentlicht seine Routen auf
127.0.0.1 des Windows-Hosts – das ist aus der WSL-VM heraus eine andere Maschine,
denn WSL nutzt hier NAT und kein Mirrored-Networking. Ein /etc/hosts-Eintrag auf
127.0.0.1 zeigt dort ins Leere.
CRC bindet Port 443 aber auf allen Interfaces, nur Port 6443 bleibt auf Loopback. Der Host ist also über das WSL-Gateway erreichbar – die Gateway-Adresse steht in der Default-Route der WSL-Maschine:
wsl -d podman-machine-default -- ip route # -> default via 172.25.224.1 dev eth0
wsl -d podman-machine-default -- sudo sh -c \
'echo "172.25.224.1 default-route-openshift-image-registry.apps-crc.testing" >> /etc/hosts'
Steht dort noch ein alter Eintrag mit 127.0.0.1, muss er
entfernt werden – bei mehreren Treffern gewinnt der erste. Die Gateway-Adresse
kann sich nach einem WSL-Neustart ändern.
Der Eintrag ist nicht dauerhaft: Die Kopfzeile in
/etc/hosts selbst verrät es – „automatically generated by
WSL“. Nach jedem Neustart der podman-machine-default-Maschine (auch einem einfachen
podman machine stop/podman machine start, erst recht nach
einem wsl --shutdown) ist die Datei wieder auf den Auto-generierten Stand
zurückgesetzt und der Eintrag fehlt – erkennbar am selben Fehlerbild wie oben, nur diesmal auf
Port 80 statt 443 (dial tcp 127.0.0.1:80: connect: connection refused
beim podman push), weil ohne gültigen Host-Eintrag ein NSS-Modul (resolve)
still eine falsche Adresse liefert, bevor der Push auf Klartext-HTTP zurückfällt. Abhilfe von
überall aus – PowerShell, Git Bash, egal – da podman.exe das SSH selbst
übernimmt:
podman machine ssh podman-machine-default 'echo "172.25.224.1 default-route-openshift-image-registry.apps-crc.testing" | sudo tee -a /etc/hosts'
Wer dauerhaft nicht neu eintragen will, kann in der Maschine
/etc/wsl.conf um [network] / generateHosts = false
ergänzen – dann bleibt der einmal gesetzte Eintrag über Neustarts hinweg erhalten,
muss dann aber bei einer neuen Gateway-Adresse manuell nachgezogen werden.
04 Bitnami-Infra + Keycloak
Weitgehend identisch zur OKD-Anleitung – dieselben Charts, dieselben zwei Fallen
(restricted-v2 und die Bitnami-Paywall). Nur Keycloak braucht andere Probe-Werte.
helm repo add bitnami https://charts.bitnami.com/bitnami; helm repo update
NS=library; COMPAT='--set global.compatibility.openshift.adaptSecurityContext=auto'
helm upgrade --install postgres bitnami/postgresql -n $NS $COMPAT \
--set fullnameOverride=postgres \
--set auth.username=library --set auth.password=library --set auth.database=library \
--set auth.postgresPassword=library --set primary.persistence.enabled=false --wait
helm upgrade --install redis bitnami/redis -n $NS $COMPAT \
--set fullnameOverride=redis --set auth.enabled=false --set architecture=standalone \
--set master.persistence.enabled=false --wait
helm upgrade --install kafka bitnami/kafka -n $NS $COMPAT \
--set global.security.allowInsecureImages=true \
--set image.registry=docker.io --set image.repository=bitnamilegacy/kafka \
--set fullnameOverride=kafka --set controller.replicaCount=1 \
--set listeners.client.protocol=PLAINTEXT --set listeners.controller.protocol=PLAINTEXT \
--set controller.persistence.enabled=false --timeout 8m
# Keycloak - Probe-Werte deutlich grosszuegiger als auf OKD, siehe Falle 2
helm upgrade --install keycloak bitnami/keycloak -n $NS $COMPAT \
--set global.security.allowInsecureImages=true \
--set image.registry=docker.io --set image.repository=bitnamilegacy/keycloak \
--set fullnameOverride=keycloak --set service.ports.http=8080 \
--set auth.adminUser=admin --set auth.adminPassword=admin \
--set postgresql.enabled=false --set externalDatabase.host=postgres \
--set externalDatabase.user=library --set externalDatabase.password=library \
--set externalDatabase.database=keycloak_db \
--set production=false --set proxyHeaders=xforwarded \
--set startupProbe.enabled=true --set startupProbe.initialDelaySeconds=60 \
--set startupProbe.periodSeconds=15 --set startupProbe.failureThreshold=40 \
--set livenessProbe.initialDelaySeconds=300 --set livenessProbe.failureThreshold=10 \
--set readinessProbe.initialDelaySeconds=120 --set readinessProbe.failureThreshold=20 \
--timeout 10m
helm repo add bitnami https://charts.bitnami.com/bitnami; helm repo update
$NS = "library"; $COMPAT = @("--set","global.compatibility.openshift.adaptSecurityContext=auto")
helm upgrade --install postgres bitnami/postgresql -n $NS @COMPAT `
--set fullnameOverride=postgres `
--set auth.username=library --set auth.password=library --set auth.database=library `
--set auth.postgresPassword=library --set primary.persistence.enabled=false --wait
helm upgrade --install redis bitnami/redis -n $NS @COMPAT `
--set fullnameOverride=redis --set auth.enabled=false --set architecture=standalone `
--set master.persistence.enabled=false --wait
helm upgrade --install kafka bitnami/kafka -n $NS @COMPAT `
--set global.security.allowInsecureImages=true `
--set image.registry=docker.io --set image.repository=bitnamilegacy/kafka `
--set fullnameOverride=kafka --set controller.replicaCount=1 `
--set listeners.client.protocol=PLAINTEXT --set listeners.controller.protocol=PLAINTEXT `
--set controller.persistence.enabled=false --timeout 8m
# Keycloak - Probe-Werte deutlich grosszuegiger als auf OKD, siehe Falle 2
helm upgrade --install keycloak bitnami/keycloak -n $NS @COMPAT `
--set global.security.allowInsecureImages=true `
--set image.registry=docker.io --set image.repository=bitnamilegacy/keycloak `
--set fullnameOverride=keycloak --set service.ports.http=8080 `
--set auth.adminUser=admin --set auth.adminPassword=admin `
--set postgresql.enabled=false --set externalDatabase.host=postgres `
--set externalDatabase.user=library --set externalDatabase.password=library `
--set externalDatabase.database=keycloak_db `
--set production=false --set proxyHeaders=xforwarded `
--set startupProbe.enabled=true --set startupProbe.initialDelaySeconds=60 `
--set startupProbe.periodSeconds=15 --set startupProbe.failureThreshold=40 `
--set livenessProbe.initialDelaySeconds=300 --set livenessProbe.failureThreshold=10 `
--set readinessProbe.initialDelaySeconds=120 --set readinessProbe.failureThreshold=20 `
--timeout 10m
$COMPAT ein Array sein muss, kein String
Bash spaltet eine unquotierte Variable an Leerzeichen in mehrere Argumente auf – deshalb
funktioniert COMPAT='--set foo=bar' gefolgt von $COMPAT dort als zwei Argumente.
PowerShell tut das nicht: $COMPAT = "--set foo=bar" bleibt beim Aufruf einer
nativen .exe ein einziges Argument mit eingebettetem Leerzeichen, das
Helms Flag-Parser nicht als --set erkennt und stillschweigend falsch verarbeitet
(verifiziert – ein Python-sys.argv-Test zeigt das Argument unverändert als
einen String, nicht aufgeteilt). Der PowerShell-Weg: $COMPAT als Array
anlegen und mit dem @-Sigil statt $ „splatten“ –
@COMPAT reicht dann jedes Element als eigenes Argument durch, genau wie Bashs Wortaufspaltung.
Vier Installationen, 49 Schalter – die meisten wiederholen sich. Nach Zweck sortiert:
| Schalter | gilt für | bewirkt |
|---|---|---|
helm repo add bitnami …helm repo update |
Vorbereitung | Ein Chart-Repository ist ein Verzeichnis fertiger Installationspakete – wie eine Paketquelle für apt, nur für Kubernetes. add trägt die Adresse unter dem Kurznamen bitnami ein, update holt die Liste der verfügbaren Versionen. Erst danach versteht Helm die Schreibweise bitnami/postgresql. Beides ist einmalig pro Rechner nötig, nicht pro Cluster – und schadet nicht, wenn man es wiederholt. |
NS=library; COMPAT='--set global.compatibility…' |
Vorbereitung | Eine gewöhnliche Shell-Variable, kein Helm-Begriff. Sie steht hier nur, damit der Namensraum und die lange Wiederholung darunter einmal dastehen statt viermal – wer einen anderen Namensraum nutzt, ändert genau diese Zeile. Beim Einfügen in die eigene Konsole muss sie mitkopiert werden, sonst sind die Variablen darunter leer. |
-n $NS |
alle vier | Der Namespace – die Schublade im Cluster, in der diese Anwendung liegt. Ohne die Angabe landet alles in default. Er muss vorher existieren; Helm legt ihn nicht selbst an. |
global.compatibility.openshift.adaptSecurityContext=auto |
alle vier | OpenShift verbietet unter restricted-v2 feste UID/fsGroup – genau die setzen die Bitnami-Charts. Der Schalter lässt das Chart seine securityContext-Blöcke an die SCC anpassen. Ohne ihn startet kein einziger Pod. |
fullnameOverride=postgres usw. |
alle vier | Erzwingt den Service-Namen exakt so. Helm hängt sonst den Release-Namen an (postgres-postgresql). Die Anwendung erwartet aber postgres, kafka, redis-master, keycloak – so stehen die Adressen in den values-Dateien. |
persistence.enabled=false |
Postgres, Redis, Kafka | Kein PersistentVolume, die Daten liegen im emptyDir des Pods. Spart Platz und Zeit – kostet aber alle Daten, sobald der Pod neu erstellt wird (siehe Falle 3). |
--wait / --timeout 8m |
alle vier | --wait lässt Helm warten, bis die Pods bereit sind, statt sofort zurückzukehren. Ohne Zeitangabe bricht es nach 5 Minuten ab – auf der CRC-VM zu knapp. |
global.security.allowInsecureImages=trueimage.registry=docker.ioimage.repository=bitnamilegacy/… |
Kafka, Keycloak | Bitnami hat im August 2025 die versionsgetaggten Images aus docker.io/bitnami/* entfernt – der Chart-Pin läuft ins Leere (manifest unknown). Das eingefrorene bitnamilegacy springt ein; das Chart akzeptiert ein solches „fremdes“ Image aber nur mit dem ersten Schalter. |
listeners.client.protocol=PLAINTEXTlisteners.controller.protocol=PLAINTEXT |
Kafka | Kein TLS und kein SASL innerhalb des Clusters. Für einen lokalen Lern-Cluster angemessen – in Produktion nicht. |
controller.replicaCount=1 |
Kafka | Ein einzelner KRaft-Controller statt drei. Kafka braucht seit KRaft kein ZooKeeper mehr; einer reicht ohne Ausfallsicherheit. |
postgresql.enabled=falseexternalDatabase.host=postgres … |
Keycloak | Das Keycloak-Chart würde sonst eine zweite Postgres-Instanz mitinstallieren. Stattdessen nutzt es die vorhandene, mit eigener Datenbank keycloak_db. |
production=false |
Keycloak | Entwicklungsmodus: kein TLS-Zwang, kein erzwungener Hostname. Der Preis steht in Falle 3 – der iss-Claim folgt dem Host-Header des ersten Requests. |
proxyHeaders=xforwarded |
Keycloak | Keycloak vertraut den X-Forwarded-*-Kopfzeilen des OpenShift-Routers. Ohne das erzeugt es Weiterleitungen auf http://keycloak:8080 statt auf die externe Adresse. |
startupProbe.failureThreshold=40livenessProbe.initialDelaySeconds=300readinessProbe.initialDelaySeconds=120 |
Keycloak | Die eigentliche Rettung: Keycloak baut beim Start Quarkus neu, was auf dieser VM 5 Minuten 34 dauert. Die Standardwerte des Charts geben ihm rund zwei Minuten und töten ihn mitten im Build – endlos. Die startupProbe deckt mit 40 Versuchen à 15 s ein Zehn-Minuten-Fenster ab; solange sie läuft, sind Liveness und Readiness pausiert. |
Beim Start baut Keycloak Quarkus neu. Im Log steht am Ende
Quarkus augmentation completed in 334467ms – also 5 Minuten 34. Die
Liveness-Probe des Bitnami-Charts schlägt vorher an, tötet den Container, der Neustart baut
wieder von vorn. Eine Schleife, in der Keycloak nie fertig wird. Die Werte oben brechen sie auf.
05 Deployen – bewusst mit replicas 0
Das Skript legt Datenbanken, Realm, Secrets, Helm-Releases und Routen an – startet aber nichts.
bash infra/crc/deploy-crc.sh
Anders als der Registry-Push in Schritt 03 ist das hier kein einzelnes Kommando
mit Bash-Syntax drumherum, sondern eine eigenständige .sh-Datei mit den
Idempotenz-Checks aus dem nächsten Absatz. Die lässt sich nicht sinnvoll 1:1 nach PowerShell
übersetzen, ohne sie doppelt zu pflegen. bash <datei>.sh braucht unter
Windows entweder Git Bash (kommt mit Git for Windows, meist schon im PATH)
oder eine WSL-Bash – Hauptsache, oc, helm und
podman sind darin erreichbar.
Das Skript ist idempotent: Jeder Schritt prüft erst und handelt nur bei Bedarf –
CREATE DATABASE hinter einer Existenzprüfung, helm upgrade --install statt
helm install, oc create … --dry-run | oc apply statt oc create,
Routen nur wenn nicht vorhanden. Ein abgebrochener Lauf kann darum ohne Aufräumen wiederholt werden –
was man hier öfter braucht, als einem lieb ist.
persistence.enabled=false ist gefährlicher, als es klingt
Die Bitnami-Charts laufen ohne PVC, die Daten liegen im emptyDir. Ein
Container-Neustart ist harmlos – das Volume überlebt ihn. Sobald aber der
Pod neu erstellt wird, etwa nach einer Verdrängung durch DiskPressure,
sind alle sieben Datenbanken und der Keycloak-Realm weg. Die Dienste laufen dann weiter
gegen eine Datenbank, die unter ihnen verschwunden ist – ohne dass es sofort auffällt.
Abhilfe: deploy-crc.sh erneut laufen lassen.
oc cp
Git Bash wandelt Argumente, die wie POSIX-Pfade aussehen, in Windows-Pfade um. Aus
library/keycloak-0:/tmp/realm.json wird dabei library\keycloak-0;C, und
oc cp findet den Pod nicht. Ein doppelter Schrägstrich unterdrückt
das gezielt für dieses eine Argument: …keycloak-0://tmp/realm.json.
MSYS_NO_PATHCONV=1 wäre zu grob – es lässt auch den
KUBECONFIG-Pfad unkonvertiert und legt oc.exe lahm.
06 Gestaffelt hochfahren
Der eigentliche Kern dieser Anleitung.
bash infra/crc/rollout-staged.sh # ~20 Minuten, schreibt eine CSV mit Messwerten
Auch dieses Skript ist ein eigenständiges .sh – dieselbe
Begründung wie in Schritt 05, kein PowerShell-Äquivalent, Git Bash oder
WSL-Bash genügt.
Beim Parallelstart aller acht Spring-Dienste gemessen:
| Kennzahl | Wert |
|---|---|
Load average (bei 6 vCPU), Maximum aus crc-monitor-*.csv | 157 |
| Lauffähige Prozesse (Momentaufnahme im Vorfall) | 104 |
vmstat (dieselbe Momentaufnahme) | us=64 sy=35 id=0 wa=0 |
wa=0 ist die entscheidende Zahl: reine CPU-Sättigung, kein I/O-Problem.
Eine schnellere Platte hätte hier nichts gebracht.
Die Folge war eine Rückkopplung: Der kube-apiserver bekam keine Rechenzeit mehr
(TLS handshake timeout), die Probes liefen in Timeouts, die Liveness-Probe tötete Pods,
jeder Neustart erzeugte einen weiteren Kaltstart – und damit noch mehr Last. Der Cluster fand da
nicht mehr von allein heraus; selbst crictl stop lief in Timeouts. Er musste
hart neu gestartet und die Dienste noch vor dem Hochlauf auf replicas=0 gesetzt werden.
Nacheinander gestartet bleibt die Load im niedrigen einstelligen Bereich. Die 5,25 vor
config-server stand noch aus dem abgebrochenen Parallelversuch an, danach lag sie zwischen
1,1 und 3,6. Gemessene Startdauern (aus rollout-20260902-012327.csv,
dieselbe Datei wie Zeichnung 35 der Diagrammseite):
| Dienst | Dauer | Load davor | Load danach |
|---|---|---|---|
config-server | 121 s | 5,25 | 2,17 |
catalog-service | 144 s | 2,00 | 2,75 |
member-service | 116 s | 2,09 | 1,13 |
lending-service | 630 s * | – | – |
reservation-service | 560 s | – | 3,6 |
fine-service | 97 s | 3,23 | 2,03 |
notification-service | 117 s | 2,13 | 2,32 |
api-gateway | 116 s | 1,87 | 1,52 |
library-frontend (nginx) | 11 s | 1,36 | 1,74 |
* Die 630 s waren die Zeitüberschreitung des
Rollout-Skripts, nicht die reine Startdauer – lending-service wurde danach trotzdem
bereit. Die Load-Spalten für lending und reservation sind in der CSV nicht
einzeln festgehalten.
Median der sechs regulären JVM-Dienste: rund 117 Sekunden.
lending-service und reservation-service fielen mit 560–630 s deutlich
heraus. Das Frontend ist nginx und in elf Sekunden oben – deshalb bekommt es auch nicht das Overlay
mit den 150-Sekunden-Probes, sondern seine eigenen Werte aus library-frontend-values.yaml.
07 Prüfen
oc get pods -n library
# 13 Pods, alle 1/1: 8 Services + Frontend + postgres/redis/kafka/keycloak
oc get route -n library
# library / keycloak / library-frontend, jeweils .apps-crc.testing
curl -k https://library.apps-crc.testing/actuator/health
# {"status":"UP","groups":["liveness","readiness"]}
curl -ksI https://library-frontend.apps-crc.testing/ | head -1
curl -k https://keycloak.apps-crc.testing/realms/library | head -c 60
oc get pods -n library
# 13 Pods, alle 1/1: 8 Services + Frontend + postgres/redis/kafka/keycloak
oc get route -n library
# library / keycloak / library-frontend, jeweils .apps-crc.testing
curl.exe -k https://library.apps-crc.testing/actuator/health
# {"status":"UP","groups":["liveness","readiness"]}
curl.exe -ksI https://library-frontend.apps-crc.testing/ | Select-Object -First 1
$realm = curl.exe -k https://keycloak.apps-crc.testing/realms/library
$realm.Substring(0, [Math]::Min(60, $realm.Length))
curl ohne .exe ist in PowerShell kein curl
PowerShell bringt einen eingebauten Alias curl → Invoke-WebRequest mit
(Get-Alias curl zeigt es). curl -k … landet damit nicht beim
echten curl.exe, sondern bei Invoke-WebRequest, das -k gar nicht
kennt: Invoke-WebRequest : Es wurde kein Parameter gefunden, der dem Parameternamen „k“
entspricht. Der Alias lässt sich zwar entfernen (Remove-Item alias:curl), sicherer
ist es aber, den echten Windows-eigenen curl.exe (liegt unter
C:\Windows\System32\curl.exe) explizit mit der Endung aufzurufen – wie im
Block oben. head -1/head -c 60 gibt es in PowerShell nicht;
Select-Object -First 1 ersetzt Ersteres, für die Zeichen-Begrenzung bleibt nur
.Substring(…) auf dem eingesammelten String.
Alle drei Routen antworten mit HTTP 200, der Gateway-Health meldet
UP, und Keycloak liefert den Realm samt public_key.
Die vier teuren Fallen auf einen Blick
| Symptom | Ursache | Abhilfe |
|---|---|---|
invalid destination name …\cache\tmpBundleImage<N> bei crc setup |
CRC 2.63 lädt Bundles als OCI-Artefakt; die Bibliothek kann ihr temporäres Zielverzeichnis nicht in einem Reparse-Point (Junction) anlegen | cache vorübergehend als echtes Verzeichnis, nach dem Setup zurück auf die Junction. Lesen und Entpacken durch die Junction funktionieren einwandfrei – nur das Anlegen bricht |
Pods werden Pending, Node trägt Taint disk-pressure |
openshift-console/downloads schreibt in dieser OKD-Version rund 5 GB pro Stunde in sein emptyDir – 17 GB in 3,5 Stunden gemessen |
Console-Operator auf Unmanaged, Deployment auf 0 – sonst skaliert er zurück |
| Keycloak startet endlos neu | Quarkus-Build dauert 5,5 Minuten, die Standard-Liveness gibt ihm zwei | Probe-Werte aus Schritt 04 |
TLS handshake timeout, Cluster reagiert nicht mehr |
Acht JVM-Kaltstarts parallel auf 6 vCPU | Gestaffelt hochfahren |
oc patch console.operator.openshift.io/cluster --type=merge \
-p '{"spec":{"managementState":"Unmanaged"}}'
oc scale deploy/downloads -n openshift-console --replicas=0
oc patch console.operator.openshift.io/cluster --type=merge `
-p '{\"spec\":{\"managementState\":\"Unmanaged\"}}'
oc scale deploy/downloads -n openshift-console --replicas=0
Der downloads-Pod liefert in der Web-Konsole nur die Downloadseite für
oc und verwandte Werkzeuge. Auf einem lokalen Entwicklungscluster kostet sein Abschalten nichts.