Kubernetes für Java-Entwickler: Der Einstieg
Zurück zum Blog

Kubernetes für Java-Entwickler: Der Einstieg

6 Min. Lesezeit
Lesen auf English

Warum Container-Orchestrierung?

Docker hat in den letzten Jahren die Art und Weise revolutioniert, wie wir Software ausliefern. Container sind leichtgewichtig, portabel und reproduzierbar. Doch wer schon einmal versucht hat, eine Handvoll Docker-Container in Produktion zu betreiben, kennt die Herausforderungen: Wie verteile ich Container auf mehrere Hosts? Was passiert, wenn ein Container abstürzt? Wie skaliere ich bei Last?

Genau hier kommt Container-Orchestrierung ins Spiel. Während Docker Compose und Docker Swarm für einfache Szenarien ausreichen, zeigt sich in der Praxis schnell, dass ein leistungsfähigeres Werkzeug benötigt wird. Kubernetes -- ursprünglich von Google entwickelt und mittlerweile ein Open-Source-Projekt der Cloud Native Computing Foundation -- hat sich als die führende Lösung etabliert.

Für Java-Teams, die ihre Spring-Boot-Anwendungen bereits in Docker-Container verpacken, ist Kubernetes der logische nächste Schritt. In diesem Artikel schauen wir uns die Grundkonzepte an und machen die ersten praktischen Schritte.

Die Kernkonzepte von Kubernetes

Kubernetes bringt eine eigene Terminologie mit, die anfangs etwas überwältigend wirken kann. Im Kern sind die Konzepte aber recht intuitiv, sobald man sie einmal verstanden hat.

Pods

Ein Pod ist die kleinste deploybare Einheit in Kubernetes. Er enthält einen oder mehrere Container, die sich Netzwerk und Speicher teilen. In den meisten Fällen läuft genau ein Container pro Pod -- zum Beispiel eine Spring-Boot-Anwendung.

Pods sind von Natur aus kurzlebig. Kubernetes kann sie jederzeit beenden und neu starten. Das klingt zunächst beunruhigend, ist aber ein zentrales Designprinzip: Anwendungen sollten zustandslos sein und jederzeit ersetzt werden können.

Deployments

Ein Deployment beschreibt den gewünschten Zustand einer Anwendung: Welches Container-Image soll laufen? Wie viele Instanzen (Replicas) sollen es sein? Kubernetes sorgt dann automatisch dafür, dass dieser Zustand eingehalten wird. Stürzt ein Pod ab, wird er neu gestartet. Wird ein neues Image deployt, erfolgt ein Rolling Update ohne Downtime.

Services

Wenn Pods kurzlebig sind und sich ihre IP-Adressen ständig ändern können, braucht man eine stabile Adresse, unter der eine Anwendung erreichbar ist. Genau das leistet ein Service. Er stellt einen festen DNS-Namen und eine feste IP bereit und verteilt eingehende Anfragen per Load Balancing auf die dahinterliegenden Pods.

ConfigMaps und Secrets

Konfigurationswerte und Zugangsdaten sollten nicht im Container-Image hart verdrahtet sein. Kubernetes bietet dafür ConfigMaps (für nicht-sensible Konfiguration) und Secrets (für Passwörter, Tokens und Ähnliches). Beide lassen sich als Umgebungsvariablen oder als Dateien in den Pod einbinden -- ideal für Spring Boots externalisierte Konfiguration über application.properties oder Umgebungsvariablen.

Wie Java-Anwendungen in Kubernetes passen

Spring Boot bringt von Haus aus viele Eigenschaften mit, die den Betrieb in Kubernetes erleichtern:

  • Embedded Server: Spring Boot liefert Tomcat gleich mit. Das Container-Image braucht keinen externen Application Server.
  • Health Endpoints: Mit Spring Boot Actuator stehen /health-Endpoints bereit, die Kubernetes als Liveness- und Readiness-Probes nutzen kann.
  • Externalisierte Konfiguration: Profile und Umgebungsvariablen machen es einfach, die gleiche Anwendung in verschiedenen Umgebungen zu betreiben.
  • Fat JARs: Ein einzelnes JAR enthält alles, was die Anwendung braucht. Das Dockerfile wird dadurch erfreulich schlank.

Ein typisches Dockerfile für eine Spring-Boot-Anwendung sieht so aus:

FROM openjdk:8-jre-alpine
COPY target/my-app-1.0.0.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]

Kubernetes-Konfiguration in der Praxis

Kubernetes-Ressourcen werden in YAML-Dateien beschrieben. Hier ein vollständiges Beispiel für ein Deployment und einen Service einer Spring-Boot-Anwendung:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: my-spring-app
  labels:
    app: my-spring-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-spring-app
  template:
    metadata:
      labels:
        app: my-spring-app
    spec:
      containers:
        - name: my-spring-app
          image: registry.example.com/my-spring-app:1.0.0
          ports:
            - containerPort: 8080
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 5
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: "production"
---
apiVersion: v1
kind: Service
metadata:
  name: my-spring-app
spec:
  type: ClusterIP
  selector:
    app: my-spring-app
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP

Einige wichtige Details in dieser Konfiguration:

  • Replicas: Zwei Instanzen sorgen für grundlegende Hochverfügbarkeit.
  • Liveness Probe: Kubernetes prüft regelmäßig, ob die Anwendung noch reagiert. Der initialDelaySeconds-Wert gibt der JVM genügend Zeit zum Starten.
  • Readiness Probe: Erst wenn dieser Check erfolgreich ist, leitet der Service Traffic an den Pod weiter.
  • Resource Limits: Besonders bei Java-Anwendungen wichtig, da die JVM gerne Speicher beansprucht. Die Limits schützen das Cluster vor einem einzelnen Pod, der alle Ressourcen verbraucht.

Erste Schritte mit Minikube

Für den lokalen Einstieg ist Minikube das Mittel der Wahl. Es erstellt eine virtuelle Maschine mit einem Single-Node-Kubernetes-Cluster auf dem eigenen Rechner.

Die Einrichtung ist unkompliziert:

# Minikube starten
minikube start

# Cluster-Status prüfen
kubectl cluster-info

# Deployment anlegen
kubectl apply -f deployment.yaml

# Pods anzeigen
kubectl get pods

# Logs eines Pods ansehen
kubectl logs my-spring-app-abc123

# Service im Browser öffnen
minikube service my-spring-app

Mit kubectl hat man ein mächtiges Kommandozeilen-Werkzeug an der Hand, das alle Interaktionen mit dem Cluster ermöglicht. Der Befehl kubectl get pods -w ist dabei besonders nützlich -- er zeigt in Echtzeit, wie Pods erstellt, gestartet und in den Ready-Zustand übergehen.

Worauf man achten sollte

Der Einstieg in Kubernetes ist einfacher als oft angenommen, aber es gibt einige Punkte, die Java-Entwickler im Blick behalten sollten:

  • JVM-Speicher und Container-Limits: Die JVM erkennt Container-Speicherlimits nicht automatisch korrekt. Es empfiehlt sich, -Xmx explizit zu setzen und dabei Platz für den Off-Heap-Speicher der JVM zu lassen.
  • Startup-Zeit: Java-Anwendungen brauchen länger zum Starten als etwa Go- oder Node.js-Services. Die initialDelaySeconds bei den Probes sollten entsprechend großzügig konfiguriert werden.
  • Logging: Kubernetes sammelt alles, was auf stdout/stderr geschrieben wird. Spring Boot schreibt per Default auf die Konsole -- das passt bereits.
  • Graceful Shutdown: Bei einem Rolling Update sollte die Anwendung laufende Requests noch abarbeiten, bevor sie beendet wird. Spring Boot unterstützt dies, es muss aber korrekt konfiguriert werden.

Fazit

Kubernetes mag auf den ersten Blick komplex erscheinen, doch die Grundkonzepte sind klar strukturiert und für Java-Entwickler gut zugänglich. Wer bereits Docker nutzt, hat die schwierigste Hürde schon genommen. Kubernetes baut darauf auf und löst die Probleme, die in Produktion unweigerlich auftreten: Skalierung, Ausfallsicherheit und Zero-Downtime-Deployments.

Unser Rat: Installiert Minikube, nehmt eine bestehende Spring-Boot-Anwendung und deployt sie ins lokale Cluster. Experimentiert mit Skalierung (kubectl scale deployment my-spring-app --replicas=5) und simuliert Ausfälle (einfach einen Pod löschen und beobachten, wie Kubernetes ihn ersetzt). Das praktische Erleben vermittelt mehr als jede Dokumentation.

In kommenden Artikeln werden wir tiefer in fortgeschrittene Themen eintauchen: Ingress-Konfiguration, persistenter Speicher, Helm Charts und CI/CD-Pipelines mit Kubernetes.

Teilen

Patrick Hütter

Geschrieben von

Patrick Hütter

Gründer & Software-Architekt

Softwarearchitekt, Ingenieur und Unternehmer. Patrick baut seit über einem Jahrzehnt Produkte und Plattformen — von Enterprise-Backends über Cloud-Native-Infrastruktur bis hin zu KI-gestützten Anwendungen. Als Gründer von encircle360 verbindet er technische Tiefe mit unternehmerischem Denken und treibt Open-Source-Projekte voran, die echten Impact haben.