Tworzenie obrazów Dockera za pomocą Spring Boot

1. Wstęp

W miarę jak coraz więcej organizacji zbliża się do kontenerów i serwerów wirtualnych, Docker staje się coraz ważniejszą częścią przepływów pracy związanych z tworzeniem oprogramowania. W tym celu jedną ze wspaniałych nowych funkcji Spring Boot 2.3 jest możliwość łatwego tworzenia obrazu Dockera dla aplikacji Spring Boot.

W tym samouczku przyjrzymy się, jak tworzyć obrazy Docker dla aplikacji Spring Boot.

2. Tradycyjne kompilacje Dockera

Tradycyjnym sposobem budowania obrazów Dockera za pomocą Spring Boot jest użycie pliku Dockerfile. Poniżej znajduje się prosty przykład:

FROM openjdk:8-jdk-alpine EXPOSE 8080 ARG JAR_FILE=target/demo-app-1.0.0.jar ADD ${JAR_FILE} app.jar ENTRYPOINT ["java","-jar","/app.jar"]

Moglibyśmy wtedy użyć polecenia docker build, aby utworzyć obraz Dockera . Działa to dobrze w przypadku większości aplikacji, ale ma kilka wad.

Najpierw używamy słoika tłuszczu stworzonego przez Spring Boot. Może to mieć wpływ na czas uruchamiania, szczególnie w środowisku kontenerowym . Możemy zaoszczędzić czas uruchamiania, dodając zamiast tego rozbitą zawartość pliku jar.

Po drugie, obrazy Dockera są wbudowane w warstwy. Charakter plików jar w Spring Boot powoduje, że cały kod aplikacji i biblioteki innych firm są umieszczane w jednej warstwie. Oznacza to, że nawet gdy zmienia się tylko jedna linia kodu, cała warstwa musi zostać odbudowana .

Rozbijając słoik przed utworzeniem, kod aplikacji i biblioteki innych firm otrzymują własną warstwę. To pozwala nam skorzystać z mechanizmu buforowania Dockera. Teraz, gdy zmieniany jest jeden wiersz kodu, trzeba odbudować tylko odpowiednią warstwę.

Mając to na uwadze, przyjrzyjmy się, jak Spring Boot ulepszył proces tworzenia obrazów Dockera.

3. Buildpacks

Buildpacks to narzędzie, które zapewnia zależności struktury i aplikacji .

Na przykład, biorąc pod uwagę fat jar Spring Boot, pakiet kompilacji zapewni nam środowisko wykonawcze Javy. Dzięki temu możemy pominąć plik Dockerfile i automatycznie uzyskać rozsądny obraz Dockera.

Spring Boot obsługuje zarówno Maven, jak i Gradle dla pakietów kompilacji. Na przykład, budując z Mavenem, uruchomilibyśmy polecenie:

./mvnw spring-boot:build-image

Przyjrzyjmy się niektórym stosownym wynikom, aby zobaczyć, co się dzieje:

[INFO] Building jar: target/demo-0.0.1-SNAPSHOT.jar ... [INFO] Building image 'docker.io/library/demo:0.0.1-SNAPSHOT' ... [INFO] > Pulling builder image 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 100% ... [INFO] [creator] ===> DETECTING [INFO] [creator] 5 of 15 buildpacks participating [INFO] [creator] paketo-buildpacks/bellsoft-liberica 2.8.1 [INFO] [creator] paketo-buildpacks/executable-jar 1.2.8 [INFO] [creator] paketo-buildpacks/apache-tomcat 1.3.1 [INFO] [creator] paketo-buildpacks/dist-zip 1.3.6 [INFO] [creator] paketo-buildpacks/spring-boot 1.9.1 ... [INFO] Successfully built image 'docker.io/library/demo:0.0.1-SNAPSHOT' [INFO] Total time: 44.796 s

Pierwsza linia pokazuje, że zbudowaliśmy nasz standardowy słoik na tłuszcz, tak jak każdy typowy pakiet mavena.

Następny wiersz rozpoczyna kompilację obrazu platformy Docker. Zaraz potem widzimy, że kompilacja wciąga się w kreatorze Packeto.

Packeto to implementacja pakietów kompilacji natywnych dla chmury. Wykonuje pracę polegającą na analizie naszego projektu i określeniu wymaganych frameworków i bibliotek . W naszym przypadku określa, że ​​mamy projekt Spring Boot i dodaje wymagane pakiety kompilacji.

Na koniec widzimy wygenerowany obraz Dockera i całkowity czas kompilacji. Zwróć uwagę, że przy pierwszym budowaniu spędzamy dużo czasu na pobieraniu pakietów kompilacji i tworzeniu różnych warstw.

Jedną z wielkich cech buildpacków jest to, że obraz Dockera składa się z wielu warstw. Jeśli więc zmienimy tylko kod naszej aplikacji, kolejne kompilacje będą znacznie szybsze:

... [INFO] [creator] Reusing layer 'paketo-buildpacks/executable-jar:class-path' [INFO] [creator] Reusing layer 'paketo-buildpacks/spring-boot:web-application-type' ... [INFO] Successfully built image 'docker.io/library/demo:0.0.1-SNAPSHOT' ... [INFO] Total time: 10.591 s

4. Warstwowe słoiki

W niektórych przypadkach możemy nie chcieć używać pakietów kompilacji - być może nasza infrastruktura jest już powiązana z innym narzędziem lub mamy już niestandardowe pliki Dockerfile, które chcemy ponownie wykorzystać.

Z tych powodów Spring Boot obsługuje również tworzenie obrazów Dockera przy użyciu warstwowych plików JAR . Aby zrozumieć, jak to działa, przyjrzyjmy się typowemu układowi słoików Spring Boot:

org/ springframework/ boot/ loader/ ... BOOT-INF/ classes/ ... lib/ ...

Słoik na tłuszcz składa się z 3 głównych części:

  • Klasy bootstrap wymagane do uruchomienia aplikacji Spring
  • Kod aplikacji
  • Biblioteki zewnętrzne

W przypadku warstwowych słoików struktura wygląda podobnie, ale otrzymujemy nowy plik layer.idx , który mapuje każdy katalog w słoiku tłuszczu na warstwę:

- "dependencies": - "BOOT-INF/lib/" - "spring-boot-loader": - "org/" - "snapshot-dependencies": - "application": - "BOOT-INF/classes/" - "BOOT-INF/classpath.idx" - "BOOT-INF/layers.idx" - "META-INF/"

Po wyjęciu z pudełka, Spring Boot ma cztery warstwy:

  • zależności : typowe zależności od stron trzecich
  • Snapshot-dependencies : zależności migawek od stron trzecich
  • zasoby : zasoby statyczne
  • aplikacja : kod aplikacji i zasoby

Celem jest umieszczenie kodu aplikacji i bibliotek innych firm w warstwach, które odzwierciedlają częstotliwość ich zmian .

Na przykład kod aplikacji jest prawdopodobnie tym, co zmienia się najczęściej, więc otrzymuje własną warstwę. Co więcej, każda warstwa może ewoluować samodzielnie i tylko wtedy, gdy warstwa ulegnie zmianie, zostanie przebudowana na obraz Dockera.

Teraz, gdy rozumiemy nową warstwową strukturę jar, przyjrzyjmy się, jak możemy ją wykorzystać do tworzenia obrazów Dockera.

4.1. Tworzenie warstwowych słoików

Najpierw musimy skonfigurować nasz projekt, aby stworzyć warstwowy słoik. W przypadku Maven oznacza to dodanie nowej konfiguracji do sekcji wtyczek Spring Boot naszego POM:

 org.springframework.boot spring-boot-maven-plugin   true   

W tej konfiguracji polecenie pakietu Maven (wraz z dowolnym z jego poleceń zależnych) wygeneruje nowy warstwowy jar przy użyciu czterech domyślnych warstw wspomnianych wcześniej.

4.2. Przeglądanie i wyodrębnianie warstw

Następnie musimy wyodrębnić warstwy z słoika, aby obraz Dockera miał odpowiednie warstwy.

Aby zbadać warstwy dowolnego warstwowego słoika, możemy uruchomić polecenie:

java -Djarmode=layertools -jar demo-0.0.1.jar list

Następnie, aby je wyodrębnić, uruchomilibyśmy:

java -Djarmode=layertools -jar demo-0.0.1.jar extract

4.3. Tworzenie obrazu platformy Docker

Najłatwiejszym sposobem włączenia tych warstw do obrazu platformy Docker jest użycie pliku Dockerfile:

FROM adoptopenjdk:11-jre-hotspot as builder ARG JAR_FILE=target/*.jar COPY ${JAR_FILE} application.jar RUN java -Djarmode=layertools -jar application.jar extract FROM adoptopenjdk:11-jre-hotspot COPY --from=builder dependencies/ ./ COPY --from=builder snapshot-dependencies/ ./ COPY --from=builder spring-boot-loader/ ./ COPY --from=builder application/ ./ ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

Ten plik Dockerfile wyodrębnia warstwy z naszego słoika, a następnie kopiuje każdą warstwę do obrazu Dockera. Każda dyrektywa COPY powoduje utworzenie nowej warstwy w ostatecznym obrazie platformy Docker .

Jeśli utworzymy ten plik Dockerfile, zobaczymy, że każda warstwa z warstwowego jar jest dodawana do obrazu Dockera jako osobna warstwa:

... Step 6/10 : COPY --from=builder dependencies/ ./ ---> 2c631b8f9993 Step 7/10 : COPY --from=builder snapshot-dependencies/ ./ ---> 26e8ceb86b7d Step 8/10 : COPY --from=builder spring-boot-loader/ ./ ---> 6dd9eaddad7f Step 9/10 : COPY --from=builder application/ ./ ---> dc80cc00a655 ...

5. Wniosek

W tym samouczku widzieliśmy różne sposoby tworzenia obrazów Dockera za pomocą Spring Boot. Korzystając z pakietów kompilacji, możemy uzyskać odpowiednie obrazy Dockera bez standardowych lub niestandardowych konfiguracji. Lub, przy odrobinie wysiłku, możemy użyć warstwowych słoików, aby uzyskać bardziej dopasowany obraz Dockera.

Wszystkie przykłady w tym samouczku można znaleźć na GitHub.

Aby uzyskać więcej informacji na temat używania języka Java i Docker, zapoznaj się z samouczkiem na temat jib.