← Übersicht  ·  Skripte & Dateien  ·  cloud-host · OpenShift · Middleware

Middleware-Images auf OpenShift

Das Modell steht in den Atlas-Diagrammen I1–I4. Referenz ist Concordia, eine fiktive kommerzielle WAS-Middleware – der Atlas beschreibt die vier Reibungspunkte konkret genug, um sie real durchzuspielen: eine Lizenz-Schranke im Entrypoint live provozieren, Pull-Secret und Entitlement im echten Cluster prüfen, Image-Größen real vergleichen, und eine Admin-Konsole über eine passthrough-Route mit selbstsigniertem Zertifikat erreichen. EX280-relevant.

Stand: 4. September 2026 OKD 4.x / OCP 4.x ns library

Voraussetzung: Zugang zum Namespace library. Statt eines echten IBM-WAS-Images stehen generische Red-Hat-UBI-Images stellvertretend für „die kommerzielle Middleware“ – die Kubernetes-Mechanik dahinter ist real und identisch. Schritt 2 liest nur echte Cluster-Objekte, ohne sie zu ändern.

1 Die Lizenz-Schranke live provozieren

Kommerzielle Images prüfen im Entrypoint eine Lizenz-Variable, bevor der Server startet.

Kommerzielle IBM-Images (WAS, MQ, Db2) haben im Entrypoint eine Lizenz-Schranke: ohne LICENSE=accept gibt der Container die Lizenzbedingungen aus und beendet sich mit einem Fehlercode. Auf OpenShift sieht man das als CrashLoopBackOff, in den Logs steht der Lizenztext. Der Fix ist eine einzige Environment-Variable – die Frage dahinter ist, wer die Zustimmung verantwortet.

Der Entrypoint prüft LICENSE, bevor irgendetwas anderes passiert Entscheidungsbaum im Entrypoint Wert von $LICENSE ? accept App-Server läuft an view druckt Bedingungen Exit 0 leer / anderes "must accept" Exit ≠ 0 der Check läuft vor jedem Server-Start – nichts wird gestartet gilt genauso für IBM MQ (LICENSE), Db2 (LICENSE), viele kommerzielle Images manche Images nutzen zusätzlich ACCEPT_EULA=Y (MS SQL) – gleiche Idee der Exit-Code ≠ 0 ist Absicht: er zwingt eine bewusste Antwort LICENSE=view lokal ausführen, um die Bedingungen zu lesen der Text nennt die IBM-Lizenz-ID (L-Nummer) und eine URL Symptom in OpenShift Pod-Status: CrashLoopBackOff $ oc logs novaris-was-xxent You must accept the license terms to use this program. Set LICENSE=accept (container exited with code 1) restartCount steigt, die Readiness-Probe wird nie grün oc get events zeigt BackOff ohne inhaltlichen Grund typischer Irrweg: man sucht einen Konfig-, Port- oder Speicherfehler die eine Log-Zeile ist die ganze Diagnose – immer zuerst oc logs tritt oft erst in Stufe 2 auf: lokal war -e LICENSE=accept im docker run-Skript und wurde beim Übersetzen ins Deployment vergessen grenzt sich ab von ImagePullBackOff (I2) – da startet das Image nicht und von OOMKilled – da steht der Speicher als Reason der Fix spec.template.spec.containers[0].env: - name: LICENSE value: accept alternativ: Build-Arg --build-arg LICENSE=accept beim abgeleiteten Image oder eine Datei, die der Entrypoint liest (/opt/ibm/.../license) oc set env deploy/novaris-was LICENSE=accept für den schnellen Test danach: Pod startet, Readiness wird grün der eigentliche Punkt die Zustimmung ist rechtlich bindend – jemand muss sie verantworten nicht der Entwickler nebenbei – Betrieb, Einkauf oder Recht die Bedingungen und die Lizenz-URL gehören ins Repo, neben das Deployment LICENSE=accept per Default in ein Basis-Image zu backen nimmt dem Betreiber die bewusste Entscheidung – besser explizit im Deployment der Wert steht sichtbar im Merge-Request und ist im Audit nachvollziehbar Merksatz: CrashLoopBackOff bei einem kommerziellen Image – zuerst oc logs, dann nach LICENSE schauen Concordia: das WAS-Image von IBM – LICENSE=accept steht im Deployment und ist im Merge-Request dokumentiert verwandt, aber anders: MQ und Db2 nutzen dasselbe LICENSE, MS SQL nutzt ACCEPT_EULA=Y, UBI-Images brauchen nichts davon OCP-Unterschied: keiner am Mechanismus – OpenShift zeigt den Crash nur klarer in Konsole und Events an
Ein Container, der läuft und sich sofort beendet. Kommerzielle IBM-Images prüfen im Entrypoint LICENSE, bevor der App-Server startet. Ohne LICENSE=accept wird der Lizenztext ausgegeben und der Container beendet sich mit Fehlercode – in Kubernetes ein CrashLoopBackOff, die Diagnose steht als eine Zeile in oc logs. Der Fix ist ein env-Eintrag im Deployment. Die Frage dahinter ist organisatorisch: die Zustimmung ist rechtlich bindend, sie gehört sichtbar ins Repo und muss von jemandem verantwortet werden.
  • kommerzielle IBM-Images (WAS, MQ, Db2) haben im Entrypoint eine Lizenz-Schranke – sie läuft vor jedem Server-Start
  • ohne LICENSE=accept gibt der Container die Lizenzbedingungen aus und beendet sich mit Fehlercode
  • LICENSE=view druckt nur die Bedingungen und beendet mit Code 0 – zum Nachlesen vor der Zustimmung
  • in Kubernetes sieht man das als CrashLoopBackOff mit steigendem restartCount, die Readiness-Probe wird nie grün
  • die ganze Diagnose ist eine Zeile in oc logs: You must accept the license terms ... Set LICENSE=accept
  • typischer Irrweg: man sucht einen Konfig-, Port- oder Speicherfehler statt zuerst in die Logs zu schauen
  • tritt oft erst bei der Übersetzung von docker run -e LICENSE=accept ins Deployment auf – die Variable wird vergessen
  • Fix: im Deployment unter env ein Eintrag name: LICENSE / value: accept
  • alternativ als Build-Argument --build-arg LICENSE=accept beim abgeleiteten Image oder über eine Datei, die der Entrypoint liest
  • abzugrenzen von ImagePullBackOff (I2) – dort startet das Image gar nicht – und von OOMKilled mit eigenem Reason
  • der eigentliche Punkt: die Zustimmung ist rechtlich bindend – Betrieb, Einkauf oder Recht muss sie verantworten, nicht der Entwickler nebenbei
  • die Lizenzbedingungen und die IBM-Lizenz-URL gehören ins Repo, neben das Deployment
  • LICENSE=accept per Default in ein Basis-Image zu backen nimmt dem Betreiber die bewusste Entscheidung – besser explizit im Deployment
  • Concordia: das WAS-Image von IBM – LICENSE=accept steht im Deployment und ist im Merge-Request dokumentiert
  • verwandt: MQ und Db2 nutzen dasselbe LICENSE, MS SQL nutzt ACCEPT_EULA=Y, UBI-Images brauchen nichts davon
Laptop · Bash
# der Entrypoint einer typischen kommerziellen Middleware, nachgebaut:
cat <<'EOF' | oc apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-concordia
  namespace: library
spec:
  replicas: 1
  selector: { matchLabels: { app: demo-concordia } }
  template:
    metadata: { labels: { app: demo-concordia } }
    spec:
      containers:
        - name: was
          image: registry.access.redhat.com/ubi9/ubi-minimal:latest
          command:
            - sh
            - -c
            - |
              if [ "$LICENSE" != "accept" ]; then
                echo "You must accept the license terms to start this container.";
                echo "Set LICENSE=accept in the environment.";
                exit 1;
              fi
              echo "Lizenz akzeptiert, Server startet"; sleep 3600
EOF

oc get pods -n library -l app=demo-concordia -w
# CrashLoopBackOff, restartCount steigt
oc logs -n library -l app=demo-concordia
# "You must accept the license terms..." - die ganze Diagnose in einer Zeile

oc set env deployment/demo-concordia -n library LICENSE=accept
oc rollout status deployment/demo-concordia -n library
oc logs -n library -l app=demo-concordia
# "Lizenz akzeptiert, Server startet"

Genau das Symptom aus dem Atlas: CrashLoopBackOff mit steigendem restartCount, die Readiness-Probe wird nie grün, und die ganze Diagnose steht in einer Zeile oc logs. Der typische Irrweg ist, zuerst einen Konfig- oder Portfehler zu suchen, statt zuerst in die Logs zu schauen.

Wer entscheidetDie Zustimmung ist rechtlich bindend – sie gehört explizit ins Deployment und ins Repo, nicht in ein Basis-Image gebacken. Betrieb, Einkauf oder Recht verantworten sie, nicht der Entwickler nebenbei.

2 Pull-Secret und Entitlement prüfen

Volle RHEL-Pakete brauchen zwei getrennte Dinge – ein Pull-Secret und ein Entitlement. UBI braucht keins von beidem.

Ein Build, der volle RHEL-Pakete braucht (nicht nur UBI), muss sich bei Red Hat ausweisen. Das Basis-Image aus registry.redhat.io braucht ein Pull-Secret mit Zugangsdaten von console.redhat.com, und ein dnf install aus den vollen RHEL-Repos braucht ein Entitlement-Zertifikat unter /etc/pki/entitlement. UBI-Images brauchen keins von beidem.

Braucht der Build nur UBI oder volle RHEL-Pakete? Was zieht der Build in FROM und dnf install? UBI – Universal Base Image Images unter registry.access.redhat.com/ubi9/... sind frei ziehbar – kein Login die dnf-Repos ubi, ubi-baseos, ubi-appstream brauchen keine Zugangsdaten deckt die meisten Java-, Python- und Node-Workloads ab kein Pull-Secret, kein Entitlement, keine Subskription der Standardfall – wenn UBI reicht, nimm UBI ubi-minimal nutzt microdnf und ist ~40 MB – guter Runtime-Layer (C3, I3) Red Hat baut die eigenen Sprach-Images (ubi9/openjdk-17) darauf auf Lizenz erlaubt Weitergabe der fertigen Images ohne RHEL-Abo Grenze: Pakete, die nur im vollen BaseOS oder in CodeReady liegen volle RHEL-Pakete – zwei getrennte Dinge 1 · Pull-Secret für registry.redhat.io das Cluster-Pull-Secret openshift-config/pull-secret braucht einen Eintrag für registry.redhat.io mit einem Service-Account-Token von console.redhat.com fehlt der Eintrag → schon der FROM-Pull scheitert: unauthorized: authentication required 2 · Entitlement für dnf der Cluster stellt die Zertifikate als etc-pki-entitlement in openshift-config-managed bereit die BuildConfig mountet sie nach /etc/pki/entitlement in den Build-Container dann findet dnf die vollen RHEL-Repos (rhel-9-for-x86_64-baseos-rpms) fehlt der Mount → der dnf install-Schritt scheitert: Repository '...-baseos-rpms' is not available Symptom → Ursache unauthorized: authentication required beim FROM → Pull-Secret-Eintrag für registry.redhat.io fehlt (B10) Repository ... is not available / Unable to find a match beim dnf → Entitlement-Mount fehlt manifest unknown → Tag falsch geschrieben, nicht Auth  ·  no space left on device → Node-Disk, gehört nicht hierher (I3) Concordia: das WAS-Liberty-Image liegt in icr.io mit eigenem Entitlement-Key – eigener Pull-Secret-Eintrag, die eigenen Layer bleiben auf UBI OCP-Unterschied: der Entitlement-Weg ist OpenShift-spezifisch – auf reinem Kubernetes legt man die Zertifikate selbst als Secret an
Zwei Ausweise, nicht einer. Das Basis-Image aus registry.redhat.io braucht einen registry.redhat.io-Eintrag im Cluster-Pull-Secret (Token von console.redhat.com) – sonst scheitert schon der FROM-Pull mit unauthorized. Ein dnf install aus den vollen RHEL-Repos braucht zusätzlich das Entitlement-Zertifikat unter /etc/pki/entitlement, das die BuildConfig in den Build-Container mountet – sonst Repository ... is not available. UBI-Images brauchen keins von beidem. Bei Concordia liegt das WAS-Image in icr.io mit eigenem Key, die eigenen Layer bleiben auf UBI.
  • ein Build, der nur UBI-Inhalte braucht, kommt ohne alles aus – Images unter registry.access.redhat.com sind frei ziehbar
  • die dnf-Repos ubi, ubi-baseos, ubi-appstream brauchen keine Zugangsdaten – das deckt die meisten Java-/Python-Workloads ab
  • wenn UBI reicht, nimm UBIubi-minimal mit microdnf ist ~40 MB und ein guter Runtime-Layer (C3, I3)
  • volle RHEL-Pakete brauchen zwei getrennte Dinge: ein Pull-Secret und ein Entitlement
  • 1 · Pull-Secret: das Cluster-Secret openshift-config/pull-secret braucht einen Eintrag für registry.redhat.io
  • die Zugangsdaten sind ein Service-Account-Token von console.redhat.com – fehlt der Eintrag, scheitert schon der FROM-Pull
  • 2 · Entitlement: der Cluster stellt die Zertifikate als etc-pki-entitlement in openshift-config-managed bereit
  • die BuildConfig mountet sie nach /etc/pki/entitlementdann findet dnf die vollen RHEL-Repos
  • Symptom unauthorized: authentication required beim FROM → Pull-Secret-Eintrag für registry.redhat.io fehlt (B10)
  • Symptom Repository ... is not available oder Unable to find a match beim dnf-Schritt → Entitlement-Mount fehlt
  • abzugrenzen: manifest unknown ist ein falscher Tag, nicht Auth – no space left ist Node-Disk (I3)
  • Concordia: das WAS-Liberty-Image liegt in icr.io mit eigenem Entitlement-Key – eigener Pull-Secret-Eintrag
  • die eigenen Build-Layer von Concordia bleiben auf UBI und brauchen kein RHEL-Entitlement
  • OCP-Unterschied: der Entitlement-Weg ist OpenShift-spezifisch – auf reinem Kubernetes legt man die Zertifikate selbst als Secret an
Laptop · Bash
# 1 - Pull-Secret: steht ein Eintrag fuer registry.redhat.io drin?
oc get secret pull-secret -n openshift-config \
  -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d \
  | grep -o '"registry.redhat.io"' || echo "kein Eintrag fuer registry.redhat.io"

# 2 - Entitlement: liegen die Zertifikate im Cluster bereit?
oc get secret etc-pki-entitlement -n openshift-config-managed --ignore-not-found
oc get secret etc-pki-entitlement -n openshift-config-managed \
  -o jsonpath='{.data}' 2>/dev/null | grep -o '"[^"]*\.pem"' || echo "kein Entitlement-Secret gefunden"

# zum Vergleich: ein reiner UBI-Pull braucht nichts davon
oc run demo-ubi-pull -n library --image=registry.access.redhat.com/ubi9/ubi-minimal:latest \
  --restart=Never -- true
oc get pod demo-ubi-pull -n library
# Completed, ganz ohne Pull-Secret-Eintrag fuer registry.redhat.io

Fehlt der Pull-Secret-Eintrag, scheitert schon der FROM-Pull mit unauthorized: authentication required. Fehlt der Entitlement-Mount, scheitert erst der dnf-Schritt mit Repository ... is not available – der Build kommt also weiter, bevor er bricht. Beide Fehler abzugrenzen von manifest unknown (ein falscher Tag) und no space left (Node-Disk, Schritt 3).

3 Image-Größen real vergleichen

Ein WAS-traditional-Image ist 1,5 bis 2,5 GB, Open Liberty 200 bis 400 MB, ein UBI-Fat-Jar rund 150 MB – ohne eigene Builds real nachprüfbar.

Ein WAS-traditional-Image ist 1,5 bis 2,5 GB. Open Liberty liegt bei 200 bis 400 MB, ein Spring-Boot-Fat-Jar auf UBI bei etwa 150 MB. Die Größe kostet an vier Stellen: langsamer Pull, Disk-Druck auf den Nodes, jeder Rollout schiebt Gigabytes pro Node, die Registry läuft voll. Der größte Hebel ist die Layer-Reihenfolge – das Stabile nach unten, den Code nach oben.

Größe kostet vierfach – und die Layer-Reihenfolge ist der größte Hebel Größenvergleich WAS traditional   1,5–2,5 GB WAS Liberty (voll)   400–700 MB Open Liberty (gezielte Features)   200–400 MB Spring Boot Fat Jar / ubi9-openjdk   ~150 MB ubi-minimal + JRE + App   ~40 MB Basis die 2 GB sind fast alle App-Server – die eigene App sind meist < 30 MB komprimiert in der Registry etwas kleiner, entpackt auf der Node voll oc image info / skopeo inspect zeigt die Layer-Größen die vier Folgen 1 Pull – neuer Node oder Cache-Miss zieht das ganze Image, bevor der Pod startet verlängert Rollout und Scale-out spürbar – ein 2-GB-Pull dauert Minuten 2 Disk-Druck – viele große Images füllen den Image-Store der Node ab der Schwelle: Kubelet-GC, im Extremfall Evicted wegen DiskPressure 3 Rollout-Kosten – jede Version schiebt die geänderten Layer an jeden Ziel-Node bei einem 2-GB-Monolith-Layer ist das jedes Mal fast das ganze Image 4 Registry füllt sich – jeder Build ein neues Manifest ohne Aufräum-Policy wächst der Storage-Bedarf linear mit der Build-Zahl (C5) alle vier verschwinden nicht durch mehr Hardware – nur durch kleinere Images Messgröße: Zeit von Scheduled bis Running bei einem kalten Node Layer-Reihenfolge: Stabiles nach unten, Code nach oben WAR / Fat Jar – jeder Commit Abhängigkeiten – selten server.xml / Feature-Liste Liberty-Runtime + JDK Basis-OS (UBI) ein Code-Commit trifft nur den obersten kleinen Layer alle darunter bleiben im Build- und im Node-Cache Dependencies vor dem App-Code kopieren (pom.xml vor src) so wird der 200-MB-Runtime-Layer einmal gezogen und nie wieder imagePullPolicy: IfNotPresent lässt den Node-Cache wirken (statt Always) unveränderliche Tags (Digest / :git-sha) machen IfNotPresent sicher Gegenmaßnahmen • Layer-Reihenfolge fixen (der billigste Gewinn) • auf Open Liberty statt WAS traditional wechseln • in Liberty nur genutzte Features aktivieren statt javaee-8 komplett • chained build: ubi-minimal-Runtime-Stage mit nur JRE + App (C3) • kritische Nodes vor-pullen (DaemonSet oder Node-Image-Preload) • alte Tags mit skopeo delete / Registry-Prune räumen (C5) .dockerignore – keine Build-Caches oder .git ins Image Merksatz: die App sind 30 MB – der Rest ist der App-Server. Kleiner wird es nur durch einen kleineren Server oder eine schlanke Runtime-Stage Concordia: Liberty-Image, Feature-Liste auf jaxrs-2.1, cdi-2.0, jpa-2.2 reduziert, die App-WAR ist der oberste Layer und wenige MB OCP-Unterschied: keiner an den Mechanismen – die integrierte Registry und der Image-Pruner sind OpenShift-Komfort (C5)
Zwei Gigabyte, von denen 30 Megabyte deine App sind. Der Rest ist der Application Server. Die Größe kostet an vier Stellen: langsamer Pull vor jedem Pod-Start, DiskPressure-Eviction auf vollen Nodes, jeder Rollout schiebt fast das ganze Image an jeden Ziel-Node, die Registry wächst mit jeder Build-Nummer. Der größte Hebel ist die Layer-Reihenfolge: Basis-OS und Runtime nach unten, die WAR nach oben, dann trifft ein Commit nur den obersten kleinen Layer. Danach: Open Liberty statt WAS traditional, gezielte Feature-Liste, eine ubi-minimal-Runtime-Stage (C3), alte Tags räumen.
  • ein WAS-traditional-Image ist 1,5 bis 2,5 GB – Open Liberty 200 bis 400 MB, ein Spring-Boot-Fat-Jar auf UBI etwa 150 MB
  • die eigene App sind meist unter 30 MB – der Rest ist der Application Server
  • Folge 1 – langsamer Pull: ein neuer Node oder Cache-Miss zieht das ganze Image, bevor der Pod startet, das verlängert Rollout und Scale-out
  • Folge 2 – Disk-Druck: viele große Images füllen den Image-Store, ab der Schwelle Kubelet-GC und im Extremfall Evicted wegen DiskPressure
  • Folge 3 – Rollout-Kosten: jede Version schiebt die geänderten Layer an jeden Ziel-Node – bei einem Monolith-Layer fast das ganze Image
  • Folge 4 – Registry füllt sich: jeder Build ein neues Manifest, ohne Aufräum-Policy wächst der Storage linear mit der Build-Zahl (C5)
  • größter Hebel – Layer-Reihenfolge: Stabiles (OS, Runtime, JDK) nach unten, den Code (WAR, Fat Jar) nach oben
  • Dependencies vor dem App-Code kopieren (pom.xml zuerst, dann mvn, dann src) – dann trifft ein Commit nur den obersten Layer
  • imagePullPolicy: IfNotPresent statt Always lässt den Node-Cache wirken – sicher mit unveränderlichen Tags (Digest oder :git-sha)
  • auf Open Liberty wechseln und in Liberty nur die genutzten Features aktivieren statt javaee-8 komplett
  • chained build: eine ubi-minimal-Runtime-Stage mit nur JRE plus App – die Build-Werkzeuge bleiben draußen (C3)
  • kritische Nodes vor-pullen (DaemonSet oder Node-Image-Preload), damit der erste Pod nicht auf 2 GB wartet
  • alte Tags räumen mit skopeo delete oder dem Registry-Pruner, .dockerignore hält .git und Build-Caches aus dem Image (C5)
  • Concordia: Liberty-Image, Feature-Liste auf jaxrs-2.1, cdi-2.0, jpa-2.2 reduziert, die App-WAR ist der oberste Layer und wenige MB
  • OCP-Unterschied: keiner an den Mechanismen – die integrierte Registry und der Image-Pruner sind OpenShift-Komfort
Laptop · Bash
# reale, oeffentlich ziehbare Images vergleichen (kein Pull noetig, nur Metadaten):
for img in \
  registry.access.redhat.com/ubi9/ubi-minimal:latest \
  registry.access.redhat.com/ubi9/openjdk-17:latest \
  icr.io/appcafe/open-liberty:full-java17-openj9-ubi
do
  echo "== $img =="
  oc image info "$img" 2>/dev/null | grep -E '^(Digest|Size)' \
    || skopeo inspect "docker://$img" 2>/dev/null | grep -E '"Digest"|"Size"' \
    || echo "  (Registry nicht erreichbar von hier - Werte laut Hersteller-Doku pruefen)"
done

Der größte Hebel bleibt die Layer-Reihenfolge: Stabiles (OS, Runtime, JDK) nach unten, den Code (WAR, Fat Jar) nach oben, Dependencies vor dem App-Code kopieren – dann trifft ein Commit nur den obersten Layer. Ein chained build mit einer ubi-minimal-Runtime-Stage (build-kette-openshift.html) lässt die Build-Werkzeuge draußen. Alte Tags räumt skopeo delete oder der Registry-Pruner auf.

4 Die Admin-Konsole per passthrough erreichen

Eine Admin-Konsole mit selbstsigniertem Zertifikat bricht an einer edge-Route – passthrough reicht TLS unverändert durch.

Die WAS-Admin-Konsole läuft auf 9043/9443 mit einem selbstsignierten Zertifikat. Man kann sie über eine passthrough-Route erreichbar machen – aber die Konsole schreibt Konfiguration in den laufenden Server, skaliert nicht und ist nicht reproduzierbar. Der richtige Weg ist wsadmin-Scripting zur Build- oder Init-Zeit: Konfiguration als Code.

Konsole = Break-Glass. Konfiguration = Code (wsadmin / server.xml) der passthrough-Weg (nur Notfall-Zugang) Browser Admin Router reicht TLS durch WAS-Konsole 9043/9443, self-signed spec.tls.termination: passthrough spec.port.targetPort: 9443 # kein Pfad-Routing - passthrough kennt nur den Host (SNI) # edge/reencrypt bricht am self-signed Cert der Konsole warum das kein Betriebsmodell ist • schreibt in die Config eines laufenden Servers • bei mehreren Replicas trifft man einen zufälligen Pod, die anderen sehen nichts (H3) • die Änderung ist beim nächsten Pod-Neustart weg – Dateisystem nicht persistent (H2) • keine Historie, kein Review, kein Rollback • zwei Admins, die gleichzeitig speichern, überschreiben sich • die Konsole selbst hält JVM-State – sie skaliert nicht legitim: einmaliger Blick auf Laufzeit-MBeans, Thread-Dump, akute Analyse – mit NetworkPolicy einsperren der richtige Weg: Konfiguration als Code WAS traditional: wsadmin-Skripte Jython (oder Jacl) macht dieselben Config-Änderungen wie die Konsole läuft als RUN-Schritt beim Image-Bau, oder als init-Job vor dem Rollout wsadmin -lang jython -f /scripts/configure-ds.py Ergebnis: jeder frische Pod hat exakt dieselbe Konfiguration Liberty: noch direkter server.xml + configDropins/overrides sind schon die Konfiguration als Code es gibt keine Konsole, die man braucht – Werte kommen aus ConfigMap und Secret (H5) optional das Admin-Center-Feature für Lesezugriff, nie als Änderungsweg was beide gemeinsam haben die Skripte und XML-Dateien liegen im Git-Repo jede Änderung ist ein Merge-Request mit Review, Historie und Rollback (E3) die Umgebung ist reproduzierbar – Dev, Test und Prod aus derselben Quelle Merksatz: was du in der Konsole klickst, weiß nur ein Pod und nur bis zum nächsten Neustart. Skripte im Repo wissen es für immer passthrough braucht man auch für echte Ende-zu-Ende-TLS-Fälle (Client-Zertifikate, mTLS zum Pod) – nicht nur für die Konsole (B4) Concordia: Konsole nur als passthrough-Route mit NetworkPolicy für den Notfall, die Konfiguration ist ein Satz Jython-Skripte im init-Job Liberty-Ziel: server.xml plus configDropins, ganz ohne Konsole OCP-Unterschied: passthrough ist ein OpenShift-Route-Begriff – auf Ingress heißt es ssl-passthrough am NGINX-Controller
Die Konsole ist ein Notausgang, kein Betriebsweg. Erreichbar wird sie nur über eine passthrough-Route – der Router reicht die TLS-Verbindung unverändert an den Pod durch, weil edge am selbstsignierten Konsolen-Zertifikat bricht und passthrough kein Pfad-Routing kann. Aber was du in der Konsole klickst, kennt nur ein Pod und nur bis zum nächsten Neustart (H2). Der richtige Weg ist wsadmin-Scripting zur Build- oder Init-Zeit, in Liberty die server.xml plus configDropins (H5) – im Git-Repo, mit Review und Historie (E3).
  • die WAS-Admin-Konsole läuft auf 9043/9443 mit einem selbstsignierten Zertifikat, das der Server selbst terminiert
  • eine edge- oder reencrypt-Route bricht am self-signed Cert und an der Client-Cert-Erwartung der Konsole
  • also braucht es eine passthrough-Route: termination: passthrough, targetPort: 9443, der Router reicht TLS unverändert durch
  • passthrough kann kein Pfad-Routing – es kennt nur den Host aus der SNI, bevor entschlüsselt wird
  • Nachteil: die Konsole schreibt in die Config eines laufenden Servers – bei mehreren Replicas trifft man einen zufälligen Pod (H3)
  • Nachteil: die Änderung ist beim nächsten Pod-Neustart weg, weil das Dateisystem nicht persistent ist (H2)
  • Nachteil: keine Historie, kein Review, kein Rollback – und zwei Admins, die gleichzeitig speichern, überschreiben sich
  • Nachteil: die Konsole hält selbst JVM-State – sie skaliert nicht und ist nicht reproduzierbar
  • legitim: ein einmaliger Blick auf Laufzeit-MBeans, ein Thread-Dump, akute Analyse – mit NetworkPolicy einsperren
  • richtiger Weg – wsadmin: Jython- oder Jacl-Skripte machen dieselben Config-Änderungen wie die Konsole
  • die Skripte laufen als RUN-Schritt beim Image-Bau oder als init-Job vor dem Rollout – jeder frische Pod ist gleich konfiguriert
  • in Liberty noch direkter: server.xml plus configDropins/overrides sind schon die Konfiguration als Code, Werte aus ConfigMap und Secret (H5)
  • beiden gemeinsam: die Dateien liegen im Git-Repo, jede Änderung ist ein Merge-Request mit Review und Historie (E3)
  • Concordia: Konsole nur als passthrough-Route mit NetworkPolicy für den Notfall, die Konfiguration ist ein Satz Jython-Skripte im init-Job
  • OCP-Unterschied: passthrough ist ein OpenShift-Route-Begriff – auf Ingress-NGINX heißt es ssl-passthrough
Laptop · Bash
# ein selbstsignierter HTTPS-Testserver, steht fuer die WAS-Admin-Konsole:
cat <<'EOF' | oc apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-console
  namespace: library
spec:
  replicas: 1
  selector: { matchLabels: { app: demo-console } }
  template:
    metadata: { labels: { app: demo-console } }
    spec:
      containers:
        - name: tls-testserver
          image: registry.access.redhat.com/ubi9/ubi-minimal:latest
          command:
            - sh
            - -c
            - |
              microdnf install -y openssl >/dev/null 2>&1
              openssl req -x509 -newkey rsa:2048 -keyout /tmp/key.pem -out /tmp/cert.pem \
                -days 1 -nodes -subj "/CN=demo-console" 2>/dev/null
              openssl s_server -key /tmp/key.pem -cert /tmp/cert.pem -accept 9443 -www
          ports:
            - containerPort: 9443
EOF
oc expose deployment/demo-console -n library --port=9443 --target-port=9443

cat <<'EOF' | oc apply -f -
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: demo-console
  namespace: library
spec:
  to: { kind: Service, name: demo-console }
  port: { targetPort: 9443 }
  tls: { termination: passthrough }
EOF

HOST=$(oc get route demo-console -n library -o jsonpath='{.spec.host}')
curl -sk "https://$HOST/" | head -5
# openssl s_server antwortet mit dem eigenen, selbstsignierten Zertifikat -
# genau das, was eine edge-Route nicht durchlassen wuerde

passthrough kennt nur den Host aus der SNI, bevor entschlüsselt wird – deshalb kann es kein Pfad-Routing. Der Nachteil bleibt derselbe wie bei jeder Admin-Konsole mit mehreren Replicas: sie schreibt in einen laufenden Pod, die Änderung ist beim nächsten Neustart weg (H2/H3), keine Historie, kein Review. Legitim nur für einen einmaligen Blick – mit einer NetworkPolicy eingesperrt.

5 Typische Fallen

Symptommeist
CrashLoopBackOff ohne offensichtlichen Grundzuerst oc logs prüfen – oft steht die Ursache in der ersten Zeile (Lizenz, fehlende Env)
unauthorized: authentication required beim FROMPull-Secret-Eintrag für registry.redhat.io fehlt
Repository ... is not available beim dnf-SchrittEntitlement-Mount fehlt, nicht das Pull-Secret
Route liefert einen Zertifikatsfehler für die Admin-Konsoleedge/reencrypt statt passthrough gewählt
Konsolen-Änderung nach Rollout wieder wegerwartet – die Konsole schreibt in einen nicht-persistenten Pod, kein Ersatz für Konfiguration als Code

6 Wie das Projekt es macht

Die Bibliothek nutzt keine kommerzielle Middleware – alle Images bauen auf UBI, brauchen weder Lizenz-Variable noch Entitlement (build-kette-openshift.html). Diese Anleitung zeigt das Muster für den Fall, dass doch einmal ein kommerzielles Image dazukommt: LICENSE=accept explizit im Deployment, dokumentiert im Merge-Request – die Concordia-Referenz im Atlas macht genau das. Die eigenen Build-Layer bleiben dabei immer auf UBI, nur das Basis-Image der Middleware selbst braucht das Entitlement.

+ Die Kurzfassung

Lizenzeine Env-Variable, aber rechtlich bindend – explizit im Deployment, nicht im Basis-Image gebacken
EntitlementPull-Secret für den FROM-Pull, Entitlement-Zertifikat für volle RHEL-Repos – zwei getrennte Dinge
GrößeLayer-Reihenfolge ist der größte Hebel – Stabiles unten, Code oben
Konsolepassthrough für den Notfall, wsadmin/Konfiguration-als-Code für den Alltag
⌂ Cockpit