Jak zrobić minimalny kontener dockerowy pod Spring Boot?

Docker logo

Dlaczego użyłem takiego stacku technologicznego?

Zacznę od tego, że uwielbiam Javę. Uwielbiam też Dockera, a Spring Boot to wymarzony framework. Jednak najbardziej kocham podejście KISS i minimalistyczne rozwiązania.
Co się stanie jeśli to wszystko wrzucę do jednego garnka?

Tym razem przedstawię Wam efekty poszukiwania idealnego środowiska dla Spring Boot. Starałem się używać najnowszego oprogramowania.
Kolejnym celem była minimalizacja kontenera.

Co z tego wyszło?

Efektem tych starań jest obraz Dockerowy na Linux Apline. Zawiera on “odchudzoną” Java 14 i Spring Boot 2.3.0, a co więcej pozwala uruchomić dowolną aplikację Spring Boot spakowaną w .jar.

Kroki wymagane do osiągnięcia celu:

  1. Ściągamy openjdk 14 i “odchudzamy” ją jak tylko się da.
  2. JDK po “diecie” kopiujemy do czystego alpine linuxa.
  3. Kopiujemy do obrazu aplikację w formie gotowego .jar.
  4. Konfigurujemy tak, by obraz uruchamiał Spring Boot.
  5. Używając docker compose lub docker run możemy uruchomić obraz.

Proces krok po kroku…

Docker ma ogromne możliwości budowania obrazu. Kroki 1-4 mogą być z powodzeniem załatwione jednym .dockerfile. A więc po kolei:

FROM openjdk:14-alpine as runtime-builder

Potrzebujemy openjdk. Ja preferuje dystrybucję z alpine, jednak każdy obraz z Java będzie ok.

RUN jlink --no-header-files --no-man-pages --compress=2 --strip-java-debug-attributes --output java-runtime --add-modules java.base,java.desktop,java.management,java.security.jgss,java.instrument,java.sql,jdk.unsupported

Tutaj zaczynają się prawdziwe czary. Używając jlinka tworzymy własny, zminimalizowany JDK. Pierwsze kilka opcji to optymalizacje zmniejszające paczkę. Po –ad-modules mamy spis modułów, które są nam potrzebne. Ich lista została ustalona zarówno przy użyciu narzędzia jdeps, jak również dzięki metodzie prób i błędów.

FROM alpine:latest

Używając multi-stage build z Dockera możemy użyć czystego alpine linuxa, jako obraz końcowy.

COPY --from=runtime-builder /java-runtime /java-runtime

Kopiujemy gotowe środowisko na nowy obraz…

COPY target/test.jar test.jar

Kopiujemy zbudowaną aplikację…

EXPOSE 80
CMD ["java-runtime/bin/java", "-jar", "test.jar"]

i na koniec ustawiamy nudne szczegóły dockerowe. To jest koniec pliku .dockerfile.

docker build . -t test:latest

A teraz zbudujmy to wszystko przy użyciu dockera.

docker run test:latest

Po uruchomieniu obrazu naszym oczom ukazuje się “Started TestApplication in 10.531 seconds”. Nasz obraz zajmuje 107 MB i zawiera linuxa, javę oraz aplikację spring boot (sama aplikacja to ponad 50 MB).

Na moje oko, można to uznać za sukces : )

Ewentualne pytania …

Na koniec odpowiem na kilka pytań, które mogą pojawić się w głowach osób czytających ten post:

Dlaczego używam wersji latest?

Wiem, nie jest to dobra praktyka i nigdy nie poleciłbym używania znacznika wersji “latest” dla wdrożonych systemów. Jednak dopóki system jest w fazie startu lub jest po prostu “poligonem testowym”, nie widzę w tym problemu.

Czy da się to mocniej odchudzić?

Z pewnością tak. Jeśli masz pomysł jak to osiągnąć – zostaw komentarz …

Czy da się zrobić szybszy obraz?

Tak. Najprostszy manewr mógłby polegać na rozłożeniu jara aplikacji na części składowe. Dałoby to lepszą wydajność i szybsze budowanie. Jednak moim zdaniem, skomplikowanie tego procesu nie jest warte zysków. Jeśli ktoś ma lepszy pomysł, piszcie w komentarzu.

Leave A Comment