Es funktioniert... Aber es fühlt sich falsch an - Der richtige Weg, einen Java-Monolithen auf Kubernetes zu betreiben, ohne sich den Kopf zu zerbrechen
„Es funktioniert… Aber es fühlt sich falsch an“ — Der richtige Weg, einen Java-Monolithen auf Kubernetes zu betreiben, ohne sich den Kopf zu zerbrechen
Sie haben:
- Ubuntu-Host
- Apache2, das SSL übernimmt
kind-Cluster- Moqui + OpenSearch in Pods
- MySQL auf dem Host
- Service-Typ: NodePort
- Apache als Reverse-Proxy zu
172.x.x.x:30083
Und es funktioniert.
Aber es fühlt sich zusammengeklebt an.
Dieser Instinkt? Er ist richtig.
Räumen wir das ordentlich auf — im Produktionsstil, selbstverwaltet, keine Cloud-Load-Balancer, keine Magie.
1️⃣ NodePort + Apache-Reverse-Proxy — Ist das schlecht?
Kurze Antwort:
Für lokale Entwicklung? In Ordnung.
Für Produktion? Fragil.
Aktuell proxyt Apache zu einer internen kind-IP.
Das ist brüchig, weil:
kindnicht für Produktion gedacht ist- Control-Plane-IPs keine stabilen Design-Grenzen sind
- Sie das vorgesehene Ingress-Modell von Kubernetes umgehen
Sie sagten, Sie wollen keine cloud-verwalteten Load Balancer und betreiben alles auf Ihrer eigenen Maschine. Das ist völlig in Ordnung.
Das saubere, produktionsartige Setup auf Bare Metal sieht so aus:
Empfohlene Architektur (selbstverwaltet)
Internet
↓
Apache (or Nginx) — TLS termination
↓
Kubernetes Ingress Controller
↓
Service (ClusterIP)
↓
Pods
Statt NodePort installieren Sie einen Ingress-Controller wie:
- NGINX (nginx-ingress)
- Traefik
Dann:
- Apache proxyt zum Ingress-Controller (nicht zu NodePort)
- Oder noch besser: Apache ganz entfernen und TLS über den Ingress mit cert-manager handhaben lassen
Wenn Sie vollständig selbstverwaltet auf einer einzigen Maschine bleiben, ist die einfachste saubere Version:
- nginx-ingress installieren
hostNetworkoder MetalLB nutzen, falls Sie später auf mehrere Nodes skalieren- TLS am Ingress terminieren
NodePort ist nicht „falsch“. Es ist nur Low-Level-Verrohrung. Ingress existiert, damit Sie nicht für immer manuell Ports verdrahten müssen.
2️⃣ Autoscaling eines Java-Monolithen — Die harte Wahrheit
Sie sehen:
400–500 MB RAM pro Pod
Skalierung von 1 → 3 Replicas = ~1,5 GB Arbeitsspeicher
Ja.
So funktioniert horizontales Skalieren.
Kubernetes teilt den JVM-Heap nicht magisch zwischen Replicas.
Jede Replica = vollständige JVM.
Das ist kein Kubernetes-Problem. Das ist Java-Architektur.
Aber hier können Sie optimieren.
🧠 JVM-Tuning
Wenn Sie nicht explizit setzen:
-XX:MaxRAMPercentage
-Xms
-Xmx
Kann die JVM überallozieren.
Setzen Sie Speicherlimits in Kubernetes:
resources:
requests:
memory: "512Mi"
limits:
memory: "512Mi"
Stimmen Sie dann die JVM darauf ab, den Container-Speicher zu respektieren.
Sonst nutzt Ihr Pod mehr Speicher, als Sie denken.
⚙ HPA-Strategie
HPA funktioniert besser mit CPU als mit Speicher.
Speicher-Skalierung ist träge. CPU-Skalierung reagiert schneller.
Ihre Idee eines Ziels von 90 % CPU-Auslastung ist vernünftig.
Aber die eigentliche Optimierungsfrage lautet:
Wie viele gleichzeitige Requests kann ein Moqui-Pod handhaben?
Messen Sie das. Das ist Ihre Skalierungseinheit.
🚫 Die größere Frage
Brauchen Sie 3 Replicas für Verfügbarkeit — in Ordnung.
Brauchen Sie 3 Replicas für Traffic — in Ordnung.
Aber skalieren Sie, weil der Speicher wegen Session-Storage wächst?
Das ist Architektur-Smell.
Das bringt uns zu Ihrem eigentlichen Schmerzpunkt.
3️⃣ Sessions, Logs, Locks und PVC-Engpässe
Sie erwähnten:
logs/txlogs/sessions/- Mehrere Replicas
- RWX-PVC
- LOCK-Probleme
Das ist der eigentliche Blocker.
Und es ist nicht die Schuld von Kubernetes.
Es ist die Semantik gemeinsam genutzter Dateisysteme.
Mehrere Java-Pods, die auf dasselbe RWX-Volume schreiben, verursachen definitiv Lock-Contention. Besonders bei Sessions.
So lässt sich sauber darüber nachdenken:
🔥 Sessions sollten NICHT auf der Disk liegen
Disk-basierte Sessions skalieren nicht horizontal.
Richtiges Produktions-Setup:
Nutzen Sie Redis.
- Externes Redis
- Oder Redis in Kubernetes
- Oder Redis-Cluster
Der Session-Store wird zu einem zentralisierten In-Memory-Key-Value-Store.
Kein File-Locking.
Keine RWX-Probleme.
Kein Nutzer-Logout beim Pod-Wechsel.
Das ist der Industriestandard für die Skalierung von Java-Monolithen.
📁 Logs sollten NICHT auf gemeinsam genutztem PVC liegen
Logs gehen an stdout.
Dann nutzen Sie:
- Fluent Bit
- Loki
- ELK-Stack
Das Kubernetes-Logging-Modell erwartet Logs auf stdout, nicht in gemeinsam genutzten Dateien.
Wenn Sie Transaktions-Tracing brauchen, senden Sie Logs an zentralisiertes Logging — nicht auf gemeinsam genutzte Disk.
📦 Statische Dateien / Uploads
Jetzt trennen wir die Zuständigkeiten:
- Sessions → Redis
- Logs → zentralisiertes Logging
- Uploads → Object Storage
Object Storage ist ideal, weil:
- Es replica-sicher ist
- Kein Locking
- Keine Datei-Korruption
- Unabhängig skaliert
Wenn Sie vollständig self-hosted sind, betreiben Sie:
- MinIO (S3-kompatibel)
- Oder NFS (aber NFS ist im Vergleich zu Object Storage Legacy)
RWX-PVC ist hier die am wenigsten skalierbare Option.
4️⃣ Sollte MySQL innerhalb von Kubernetes laufen?
Sie betreiben MySQL derzeit auf dem Host.
Das ist eigentlich in Ordnung.
Datenbanken innerhalb von Kubernetes zu betreiben ist möglich (StatefulSet), bringt aber zusätzliche Komplexität:
- Volume-Management
- Backup-Orchestrierung
- HA-Komplexität
Wenn Sie auf einer einzelnen Bare-Metal-Maschine sind, ist es völlig vernünftig, MySQL außerhalb von Kubernetes zu behalten.
Gehen Sie zu Multi-Node über, überdenken Sie das.
5️⃣ Kubernetes über eine UI verwalten
Ja.
Sie können nutzen:
- Lens
- Kubernetes Dashboard
- Portainer
Lens bietet mit Abstand das sauberste Erlebnis für die Remote-Cluster-Verwaltung.
Sie laden einfach die kubeconfig herunter und verbinden sich.
Kein Grund, sich auf fragile NodePort-Freigabe zu verlassen.
Die echte Produktionsform für Ihren Fall
Da Sie self-hosted auf lokaler Hardware sind und volle Kontrolle wollen:
Saubere Architektur
- Bare-Metal-Server
- Richtige Kubernetes-Distribution (nicht kind) RKE2, Talos, k3s usw.
- nginx-ingress installiert
- TLS am Ingress gehandhabt
- Moqui-Pods (kein gemeinsam genutztes Dateisystem für Sessions)
- Redis für Sessions
- Object Storage (MinIO) für Uploads
- MySQL entweder außerhalb von k8s oder als StatefulSet
- Zentralisiertes Logging
Kein NodePort.
Keine Control-Plane-IP-Abhängigkeiten.
Keine RWX-Locks.
Die harte, aber ehrliche Einschätzung
Sie stecken nicht wegen Kubernetes fest.
Sie stecken fest, weil Monolithen gemeinsam genutzte Disk erwarten.
Kubernetes erwartet zustandslose Pods.
Sobald Sie verschieben:
- Sessions → Redis
- Logs → stdout
- Uploads → Object Storage
Wird Skalieren langweilig.
Und langweilig ist gut.
Aktuell funktioniert Ihre Architektur.
Aber sie ist eng an ein Single-Machine-Denkmodell gekoppelt.
Kubernetes glänzt, wenn Sie die Dateisystem-Kopplung entfernen.
Das ist der Schlüssel.