Docker und Java: Fortgeschrittene Containerisierung von Anwendungen
\n\nDie Containerisierung von Java-Anwendungen ist in der modernen Entwicklung zum De-facto-Standard geworden. Grundkenntnisse in Docker (Erstellen eines Images aus openjdk:11 und Starten eines Containers) entsprechen jedoch nicht mehr den Anforderungen von Produktionsumgebungen. Auf fortgeschrittenem Niveau müssen die Besonderheiten der JVM-Arbeitsweise innerhalb des Containers berücksichtigt, die Image-Größe optimiert, der Speicher verwaltet und das Monitoring konfiguriert werden. In diesem Artikel behandeln wir die wichtigsten Techniken, die Ihnen helfen, effiziente und sichere Docker-Images für Java-Anwendungen zu erstellen.
1. Multi-Stage-Build: Minimierung der Image-Größe
\n\nDer klassische Ansatz „Ein Dockerfile – ein Image“ führt oft zu einer Aufblähung des endgültigen Artefakts. In das Image gelangen Build-Tools (Maven, Gradle, Compiler), die zur Laufzeit nicht benötigt werden. Die Lösung ist ein Multi-Stage-Build. Wir unterteilen den Prozess in Phasen: Build und Runtime.
\n\nBeispiel eines Dockerfiles für eine Spring Boot-Anwendung mit Maven:
\n\n# Build-Phase\nFROM maven:3.8.6-openjdk-17 AS builder\nWORKDIR /app\nCOPY pom.xml .\nRUN mvn dependency:go-offline -B\nCOPY src ./src\nRUN mvn clean package -DskipTests\n\n# Runtime-Phase\nFROM eclipse-temurin:17-jre-alpine\nWORKDIR /app\nCOPY --from=builder /app/target/*.jar app.jar\nEXPOSE 8080\nENTRYPOINT ["java", "-jar", "app.jar"]\n\nDas resultierende Image wiegt dadurch ~150 MB statt 600+ MB. Wichtig: Verwenden Sie jre-Images (nicht jdk) und minimale Distributionen (Alpine, Slim). Für Anwendungen mit Java 17+ können Sie jlink in Betracht ziehen – die Erstellung einer benutzerdefinierten JRE nur mit den erforderlichen Modulen.
2. Konfiguration der JVM für den Betrieb innerhalb eines Containers
\n\nLange Zeit konnte die JVM die für den Container festgelegten Speicher- und CPU-Limits nicht korrekt erkennen. Ab Java 8u191 und Java 10 wurde die Unterstützung für cgroups v1 (und ab Java 15 auch für cgroups v2) eingeführt. Dennoch sollte man sich nicht ausschließlich auf die Automatik verlassen.
\n\nWichtige Parameter für die Produktion:
\n- \n
-XX:+UseContainerSupport– aktiviert die Containerunterstützung (standardmäßig aktiv in Java 10+). \n -XX:MaxRAMPercentage=75.0– begrenzt die Heap-Nutzung auf 75% des verfügbaren Container-Speichers. Dadurch bleibt Speicher für Metaspace, Thread-Stacks und Systemprozesse übrig. \n -XX:ActiveProcessorCount=2– explizite Angabe der Anzahl verfügbarer Prozessoren, falls die automatische Erkennung fehlschlägt. \n
Beispiel für einen vollständigen ENTRYPOINT-Befehl:
\n\nENTRYPOINT ["java", \\\n "-XX:+UseContainerSupport", \\\n "-XX:MaxRAMPercentage=75.0", \\\n "-XX:+ExitOnOutOfMemoryError", \\\n "-jar", "app.jar"]\n\nWichtig: Vermeiden Sie feste Werte für -Xmx und -Xms in der Docker-Umgebung. Diese passen sich nicht an Änderungen der Container-Limits bei Replikation oder Skalierung an.
3. Optimierung von Layern und Build-Caching
\n\nJede Anweisung in einem Dockerfile erzeugt einen neuen Layer. Docker cached Layer, die sich nicht geändert haben. Um wiederholte Builds zu beschleunigen, ordnen Sie die Anweisungen von weniger änderungsanfällig zu änderungsanfälliger an.
\n\nRichtige Struktur eines Dockerfiles:
\n- \n
- Installation von OS-Abhängigkeiten (apk add, apt-get). \n
- Kopieren der Build-Konfigurationsdateien (pom.xml, build.gradle). \n
- Herunterladen der Abhängigkeiten (mvn dependency:go-offline). \n
- Kopieren des Quellcodes. \n
- Build und Kopieren des Artefakts. \n
Zusätzlich können Sie Docker Layer Caching in CI/CD nutzen. Speichern Sie zum Beispiel in GitHub Actions oder GitLab CI den Maven-Cache zwischen den Builds:
\n\n# Beispiel für GitLab CI\ncache:\n key: ${CI_COMMIT_REF_SLUG}\n paths:\n - .m2/repository/\n - .gradle/caches/\n\nErwägen Sie auch die Verwendung von
Blogs
Buchempfehlungen
Lesen Sie auch
Sicherheit von Java-Anwendungen: Top-10 Schwachstellen und wie man sie vermeidet
C# Chatbot erstellen: Komplette Anleitung für Anfänger
Gehälter von Java-Entwicklern in Russland: Marktüberblick (fortgeschrittenes Niveau)
Arbeit mit Datenbanken in Swift: PostgreSQL und MongoDB in der Praxis
GraphQL API mit Kotlin: Schritt-für-Schritt-Tutorial für Anfänger