- Authors

- Name
- Phillip Pham
- @ddppham
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 /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.
| Flag | Wert | Bedeutung |
|---|---|---|
-XX:MaxRAMPercentage | 75.0 | Max. 75% des Container-RAM für Heap |
-XX:InitialRAMPercentage | 50.0 | Startwert 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
Image Pull Policies und Private Registry einrichten
Kubernetes Image Pull Policies verstehen und private Registries mit imagePullSecrets konfigurieren – von Always bis Never und Harbor-Integration.
Legacy-Modernisierung: Von VMs zu Kubernetes
Legacy-Anwendungen von VMs zu Kubernetes migrieren: Die 6 Rs der Modernisierung, Containerisierung einer 3-Tier-App und Datenbank-Strategien.
Node.js auf Kubernetes: Express-App deployen
Express-Apps auf Kubernetes deployen mit Multi-Stage Dockerfile, Graceful Shutdown, Resource Limits und HPA für stabile Production-Cluster.
ArgoCD ApplicationSets: Multi-App Deployment Patterns
ArgoCD ApplicationSets automatisieren Multi-App und Multi-Cluster Deployments mit Generatoren. Praxis-Patterns für Git, Cluster und Matrix.
.NET-Anwendungen auf Kubernetes containerisieren
.NET- und ASP.NET-Core-Anwendungen für Kubernetes containerisieren: Multi-Stage Dockerfile, Health Checks, Kestrel-Konfiguration und komplette Manifeste.