Veröffentlicht am

Java Spring Boot auf Kubernetes containerisieren

Teilen:
Authors

TL;DR

Spring Boot Anwendungen lassen sich mit einem Multi-Stage Dockerfile effizient containerisieren. Wichtig sind JVM-Flags wie -XX:MaxRAMPercentage=75.0 für korrekte Speichernutzung im Container, Spring Actuator für Liveness- und Readiness-Probes sowie passende Resource Limits im Kubernetes Deployment. Als Alternative zum klassischen Dockerfile bietet Google Jib einen Build ohne Docker-Daemon.

Multi-Stage Dockerfile für Spring Boot

Ein Multi-Stage Build trennt Build- und Runtime-Umgebung. Das reduziert die Image-Größe erheblich.

# Build Stage
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
COPY gradle/ gradle/
COPY gradlew build.gradle.kts settings.gradle.kts ./
RUN ./gradlew dependencies --no-daemon
COPY src/ src/
RUN ./gradlew bootJar --no-daemon -x test

# Runtime Stage
FROM eclipse-temurin:21-jre-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
USER appuser
EXPOSE 8080

ENTRYPOINT ["java", \
  "-XX:MaxRAMPercentage=75.0", \
  "-XX:InitialRAMPercentage=50.0", \
  "-XX:+UseG1GC", \
  "-jar", "app.jar"]

Das Builder-Image nutzt das volle JDK, das Runtime-Image nur die JRE. Alpine als Basis hält das Image unter 200 MB.

JVM Memory in Containern richtig konfigurieren

Die JVM erkennt seit Java 10+ Container-Grenzen automatisch. Trotzdem sollten Sie die Speichernutzung explizit steuern.

FlagWertBedeutung
-XX:MaxRAMPercentage75.0Max. 75% des Container-RAM für Heap
-XX:InitialRAMPercentage50.0Startwert für Heap-Größe
-XX:+UseG1GC-G1 Garbage Collector für Container

Warum nicht 100%? Die JVM braucht Off-Heap-Speicher für Metaspace, Thread-Stacks und Native Memory. Bei 512 Mi Container-Limit und 75% bleiben ~128 Mi für diese Bereiche.


Health Checks mit Spring Actuator

Spring Actuator liefert fertige Endpunkte für Kubernetes Probes. Fügen Sie die Dependency hinzu:

{/* pom.xml */}
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Konfiguration in application.yml:

management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus
  endpoint:
    health:
      probes:
        enabled: true
      show-details: always
  health:
    livenessState:
      enabled: true
    readinessState:
      enabled: true

Spring stellt dann automatisch bereit:

  • /actuator/health/liveness – Ist die Anwendung alive?
  • /actuator/health/readiness – Kann sie Traffic empfangen?

Kubernetes Deployment und Service

Das vollständige Deployment mit Probes, Resource Limits und Rolling Update:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-app
  labels:
    app: spring-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: spring-app
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  template:
    metadata:
      labels:
        app: spring-app
    spec:
      containers:
        - name: spring-app
          image: registry.example.com/spring-app:1.0.0
          ports:
            - containerPort: 8080
          resources:
            requests:
              memory: "512Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "1000m"
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 5
          startupProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 5
            failureThreshold: 12
          env:
            - name: JAVA_TOOL_OPTIONS
              value: "-XX:MaxRAMPercentage=75.0"
---
apiVersion: v1
kind: Service
metadata:
  name: spring-app
spec:
  selector:
    app: spring-app
  ports:
    - port: 80
      targetPort: 8080
  type: ClusterIP

Die startupProbe gibt Spring Boot bis zu 60 Sekunden zum Hochfahren, bevor die livenessProbe greift. Das verhindert Neustarts bei langsamen Starts.

Jib: Container ohne Dockerfile bauen

Google Jib baut Container-Images direkt aus Maven oder Gradle – ohne Docker-Daemon und ohne Dockerfile.

// build.gradle.kts
plugins {
    id("com.google.cloud.tools.jib") version "3.4.4"
}

jib {
    from {
        image = "eclipse-temurin:21-jre-alpine"
    }
    to {
        image = "registry.example.com/spring-app"
        tags = setOf("latest", project.version.toString())
    }
    container {
        jvmFlags = listOf(
            "-XX:MaxRAMPercentage=75.0",
            "-XX:+UseG1GC"
        )
        ports = listOf("8080")
        user = "1000"
    }
}

Build und Push in einem Schritt:

./gradlew jibDockerBuild    # Lokales Docker-Image
./gradlew jib               # Direkt in Registry pushen

Vorteile von Jib: Reproduzierbare Builds, Layer-Caching für schnelle Rebuilds, kein Docker nötig in der CI/CD-Pipeline. Nachteil: Weniger Flexibilität als ein manuelles Dockerfile bei komplexen Build-Schritten.


FAQ

Welche Java-Version sollte ich für Kubernetes verwenden?

Java 17 oder 21 (LTS). Beide erkennen Container-Limits korrekt und bieten optimierte Garbage Collectors für Container-Umgebungen.

Wie groß sollte das Memory-Limit für Spring Boot sein?

Minimum 256 Mi, empfohlen 512 Mi für typische Microservices. Beobachten Sie den tatsächlichen Verbrauch mit Prometheus und passen Sie entsprechend an.

Warum brauche ich eine startupProbe zusätzlich zur livenessProbe?

Spring Boot benötigt Zeit zum Starten (Dependency Injection, DB-Connections). Ohne startupProbe würde die livenessProbe den Pod killen, bevor die Anwendung bereit ist.

Soll ich Jib oder ein Dockerfile verwenden?

Jib für Standard-Spring-Boot-Apps, Dockerfile wenn Sie native Libraries, spezielle OS-Pakete oder komplexe Build-Schritte benötigen.

Legacy zu Kubernetes migrieren?

Wir begleiten Ihre Migration von VMs zu Containern – ohne Produktionsausfall. Erfahrung aus 50+ Migrationsprojekten.

Kubernetes-Beratung gesucht?

Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.

📖 Verwandte Artikel

Weitere interessante Beiträge zu ähnlichen Themen