Bislang habe ich Docker Images für meine Spring Boot Anwendungen immer per Dockerfile gebaut. Dafür wurde die mit dem Spring Boot Maven Plugin erzeugte JAR Datei einfach in ein Docker Image kopiert. Dieses Vorgehen hat jedoch einige Nachteile, die nur mit viel Aufwand gelöst werden können. Stattdessen empfehle ich seit einiger Zeit die Verwendung von Google Jib, das eure Java Anwendungen effizient und sicher als Docker Image verpackt.

Bisheriger Buildprozess

Das Dockerfile zum Bauen eines Docker Images für meine Spring Boot Anwendungen sah in etwa wie folgt aus:

FROM openjdk:11.0.5-slim
COPY target/*.jar /application.jar
ENTRYPOINT ["java", "-jar", "/application.jar"]

Damit ein Docker Image mit Maven gebaut werden konnte, kam noch das dockerfile-maven-plugin (wird mittlerweile nicht mehr gepflegt) zum Einsatz:

<plugin>
    <groupId>com.spotify</groupId>
    <artifactId>dockerfile-maven-plugin</artifactId>
    <version>1.4.13</version>
    <configuration>
        <repository>image</repository>
    </configuration>
</plugin>

Dieses Vorgehen hat jedoch ein paar entscheidende Nachteile:

  • das Basisimage openjdk:11.0.5-slim ist bereits 401MB groß
  • jedes neue Docker Image beinhaltet die von Spring Boot erzeugte JAR Datei mitsamt aller Abhängigkeiten
  • auf dem CI-Server muss zwingend Docker zur Verfügung stehen
  • Builds sind nicht vollständig reproduzierbar

Viele dieser Probleme können zwar durch Anpassungen am Dockerfile und dem Buildprozess gelöst werden, diese Anpassungen sind jedoch aufwändig und kosten Zeit.

Die Lösung: Google Jib

Statt den Build selbst zu optimieren, empfehle ich Google Jib zum Bauen von Docker Images für Java Anwendungen. Google Jib bringt unter anderem folgende Vorteile:

  • es wird kein Dockerfile benötigt
  • als Basisimage kommt ein Distroless Image zum Einsatz
  • das aktuelle Image für Java 11 benötigt 195MB
  • es sind nur die nötigsten Pakete installiert → weniger Angriffsmöglichkeiten
  • der Buildprozess ist so optimiert, dass die Anwendung selbst und ihre Abhängigkeiten verschiedene Layer erzeugen
  • der Cache von Docker kann besser greifen
  • Updates verbrauchen weniger Festplattenspeicher
  • der Buildprozess ist schneller
  • es wird kein Docker für den Build benötigt
  • Builds sind reproduzierbar

Um nun ein Docker Image mit dem jib-maven-plugin zu erzeugen, reicht folgender Befehl:

mvn compile com.google.cloud.tools:jib-maven-plugin:1.8.0:build -Dimage=<image>

Üblicherweise wird jedoch die pom.xml mit folgendem Eintrag erweitert

<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>jib-maven-plugin</artifactId>
    <version>1.8.0</version>
    <configuration>
        <to>
            <image>image</image>
        </to>
    </configuration>
</plugin>

wodurch die Anwendung dann per

mvn compile jib:build

gebaut und als Docker Image bereit gestellt wird. Für Gradle kann analog das jib-gradle-plugin verwendet werden. Wer mehr über Google Jib und dessen Funktionsweise erfahren möchte, sollte sich unbedingt die folgenden Quellen anschauen.

Quellen