Docker und Kotlin: Containerisierung von Anwendungen auf fortgeschrittenem Niveau
\n\nDie Containerisierung ist zu einem festen Bestandteil der modernen Softwareentwicklung geworden, und Kotlin bildet hier keine Ausnahme. Wenn Sie bereits grundlegende Dockerfiles und das Ausführen einfacher JAR-Dateien beherrschen, ist es an der Zeit, auf die nächste Stufe zu wechseln. In diesem Artikel betrachten wir fortgeschrittene Techniken zur Containerisierung von Kotlin-Anwendungen: Multi-Stage-Builds, Optimierung der Image-Größe, Konfiguration für die Frameworks Ktor und Spring Boot sowie die Arbeit mit Docker Compose in einer Produktionsumgebung.
\n\n1. Multi-Stage-Builds von Kotlin-Anwendungen
\n\nEines der Hauptprobleme bei der Containerisierung von JVM-Anwendungen ist die Größe des endgültigen Images. Ein Standard-OpenJDK-Image kann über 400 MB wiegen. Multi-Stage-Builds ermöglichen es, die Build-Umgebung von der Laufzeitumgebung zu trennen, indem ein minimales Runtime-Image verwendet wird.
\n\nBeispiel für ein Gradle-Projekt in Kotlin
\n\n# Erste Stufe: Build\nFROM gradle:8.5-jdk21 AS builder\nWORKDIR /app\nCOPY build.gradle.kts settings.gradle.kts ./\nCOPY src ./src\nRUN gradle build --no-daemon -x test\n\n# Zweite Stufe: Ausführung\nFROM eclipse-temurin:21-jre-alpine\nWORKDIR /app\nCOPY --from=builder /app/build/libs/app.jar app.jar\nEXPOSE 8080\nENTRYPOINT ["java", "-jar", "app.jar"]\n\nDie Verwendung von eclipse-temurin:21-jre-alpine anstelle eines vollständigen JDK-Images reduziert die Größe von über 400 MB auf etwa 120 MB. Für Kotlin-Anwendungen mit Ktor oder Spring Boot ist dies mehr als ausreichend.
Optimierung des Layer-Cachings
\n\nUm wiederholte Builds zu beschleunigen, kopieren Sie Abhängigkeiten getrennt vom Quellcode:
\n\nFROM gradle:8.5-jdk21 AS builder\nWORKDIR /app\n# Zuerst Abhängigkeiten (wird gecached, wenn unverändert)\nCOPY build.gradle.kts settings.gradle.kts ./\nRUN gradle dependencies --no-daemon\n# Dann Quellcode\nCOPY src ./src\nRUN gradle build --no-daemon -x test\n\nDiese Technik ist besonders in CI/CD-Umgebungen nützlich, wo jede Sekunde zählt.
\n\n2. Optimierung von Images für Kotlin-Anwendungen
\n\nNeben der Wahl des Basis-Images gibt es weitere Techniken zur Größenreduzierung und Verbesserung der Sicherheit.
\n\nVerwendung von JLink zur Erstellung einer benutzerdefinierten JRE
\n\nWenn Ihre Anwendung nur einen Teil der JDK-Module verwendet, können Sie eine minimale JRE erstellen:
\n\n# In der Builder-Stufe\nRUN jlink --module-path /opt/java/openjdk/jmods \\\n --add-modules java.base,java.logging,java.naming,java.sql \\\n --output /custom-jre \\\n --strip-debug --no-man-pages --no-header-files\n\n# In der Runtime-Stufe\nFROM alpine:3.19\nCOPY --from=builder /custom-jre /opt/jre\nCOPY --from=builder /app/build/libs/app.jar /app/app.jar\nENV PATH="/opt/jre/bin:${PATH}"\nEXPOSE 8080\nCMD ["java", "-jar", "/app/app.jar"]\n\nDie Image-Größe kann auf 50-70 MB sinken.
\n\nMehrschichtige Architektur mit Spring Boot
\n\nSpring Boot 3+ unterstützt die Aufteilung von Fat JARs in Schichten. Nutzen Sie dies im Dockerfile:
\n\nFROM eclipse-temurin:21-jre-alpine AS builder\nWORKDIR /app\nCOPY build/libs/*.jar app.jar\nRUN java -Djarmode=layertools -jar app.jar extract\n\nFROM eclipse-temurin:21-jre-alpine\nWORKDIR /app\nCOPY --from=builder /app/dependencies/ ./\nCOPY --from=builder /app/spring-boot-loader/ ./\nCOPY --from=builder /app/snapshot-dependencies/ ./\nCOPY --from=builder /app/application/ ./\nENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]\n\nBei Code-Änderungen wird jetzt nur noch die Schicht application neu erstellt, während Abhängigkeiten und der Spring Boot Loader gecached bleiben.
3. Containerisierung von Ktor-Anwendungen
\n\nKtor ist ein leichtgewichtiges Framework zur Erstellung asynchroner Server in Kotlin. Seine Containerisierung hat eigene Besonderheiten.
\n\nDockerfile für Ktor mit Netty
\n\nFROM gradle:8.5-jdk21 AS builder\nWORKDIR /app\nCOPY build.gradle.kts settings.gradle.kts ./\nRUN gradle dependencies --