Перейти к основному содержимому

Повторное использование слоев Docker с Spring Boot

· 5 мин. чтения

1. Введение

Docker — это стандарт де-факто для создания автономных приложений. Начиная с версии 2.3.0, Spring Boot включает несколько улучшений, помогающих нам создавать эффективные образы Docker. Таким образом, это позволяет разбивать приложение на разные уровни .

Другими словами, исходный код находится на своем собственном уровне. Поэтому его можно самостоятельно перестроить, повысив эффективность и время запуска. В этом руководстве мы увидим, как использовать новые возможности Spring Boot для повторного использования слоев Docker.

2. Многоуровневые банки в Docker

Контейнеры Docker состоят из базового образа и дополнительных слоев. Как только слои будут созданы, они останутся в кэше. Поэтому последующие поколения будут намного быстрее:

./15801115e306ea2559e8eaf456169903.jpg

Изменения в слоях нижнего уровня также перестраивают слои верхнего уровня. Таким образом, редко меняющиеся слои должны оставаться внизу, а часто меняющиеся располагаться вверху.

Точно так же Spring Boot позволяет отображать содержимое артефакта на слои. Давайте посмотрим на сопоставление слоев по умолчанию:

./a6dd6619fe1996294ca5030e0c1ba7b8.jpg

Как мы видим, у приложения есть свой слой. При изменении исходного кода перестраивается только независимый слой. Загрузчик и зависимости остаются в кэше, что сокращает время создания и запуска образа Docker. Давайте посмотрим, как это сделать с помощью Spring Boot!

3. Создание эффективных образов Docker с помощью Spring Boot

В традиционном способе построения образов Docker Spring Boot использует подход «толстая банка » . В результате один артефакт включает в себя все зависимости и исходный код приложения. Таким образом, любое изменение в нашем исходном коде приводит к перестройке всего слоя.

3.1. Конфигурация слоев с Spring Boot

В Spring Boot версии 2.3.0 представлены две новые функции для улучшения генерации образов Docker:

  • Поддержка Buildpack обеспечивает среду выполнения Java для приложения, поэтому теперь можно пропустить Dockerfile и автоматически создать образ Docker.
  • Многоуровневые jar -файлы помогают нам получить максимальную отдачу от создания слоев Docker.

В этом уроке мы расширим многоуровневый подход jar.

Сначала мы настроим многоуровневую банку в Maven . При упаковке артефакта мы создадим слои. Давайте проверим файл jar:

jar tf target/spring-boot-docker-0.0.1-SNAPSHOT.jar

Как мы видим, создается файл .idx нового слоя в папке BOOT-INF внутри фляги. Конечно, он сопоставляет зависимости, ресурсы и исходный код приложения с независимыми слоями:

BOOT-INF/layers.idx

Точно так же содержимое файла разбивает различные сохраненные слои:

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

3.2. Взаимодействие со слоями

Перечислим слои внутри артефакта:

java -Djarmode=layertools -jar target/docker-spring-boot-0.0.1.jar list

Результат обеспечивает упрощенное представление содержимого файлаlayers.idx :

dependencies
spring-boot-loader
snapshot-dependencies
application

Мы также можем извлечь слои в папки:

java -Djarmode=layertools -jar target/docker-spring-boot-0.0.1.jar extract

Затем мы можем повторно использовать папки внутри Dockerfile, как мы увидим в следующем разделе:

$ ls
application/
snapshot-dependencies/
dependencies/
spring-boot-loader/

3.3. Конфигурация Dockerfile

Чтобы получить максимальную отдачу от возможностей Docker, нам нужно добавить слои в наш образ.

Во-первых, давайте добавим файл толстой банки к базовому образу:

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

Наконец, давайте скопируем извлеченные папки, чтобы добавить соответствующие слои Docker:

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"]

С этой конфигурацией, когда мы изменим наш исходный код, мы будем перестраивать только прикладной уровень. Остальные останутся в кэше.

4. Пользовательские слои

Кажется, все работает как шарм. Но если мы посмотрим внимательно, уровень зависимостей не разделяется между нашими сборками . То есть все они приходят к одному слою, даже внутренние. Поэтому, если мы изменим класс внутренней библиотеки, мы снова перестроим все уровни зависимостей.

4.1. Конфигурация пользовательских слоев с Spring Boot

В Spring Boot можно настраивать пользовательские слои через отдельный файл конфигурации:

<layers xmlns="http://www.springframework.org/schema/boot/layers"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/boot/layers
https://www.springframework.org/schema/boot/layers/layers-2.3.xsd">
<application>
<into layer="spring-boot-loader">
<include>org/springframework/boot/loader/**</include>
</into>
<into layer="application" />
</application>
<dependencies>
<into layer="snapshot-dependencies">
<include>*:*:*SNAPSHOT</include>
</into>
<into layer="dependencies" />
</dependencies>
<layerOrder>
<layer>dependencies</layer>
<layer>spring-boot-loader</layer>
<layer>snapshot-dependencies</layer>
<layer>application</layer>
</layerOrder>
</layers>

Как мы видим, мы сопоставляем и упорядочиваем зависимости и ресурсы по слоям. Кроме того, мы можем добавить столько пользовательских слоев, сколько захотим.

Назовем наш файлlayers.xml . Затем в Maven мы можем настроить этот файл для настройки слоев:

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
<configuration>${project.basedir}/src/layers.xml</configuration>
</layers>
</configuration>
</plugin>

Если мы упакуем артефакт, результат будет аналогичен поведению по умолчанию.

4.2. Добавление новых слоев

Давайте создадим внутреннюю зависимость, добавив наши классы приложения:

<into layer="internal-dependencies">
<include>com.foreach.docker:*:*</include>
</into>

Кроме того, мы закажем новый слой:

<layerOrder>
<layer>internal-dependencies</layer>
</layerOrder>

В результате, если мы перечислим слои внутри толстой банки, появится новая внутренняя зависимость:

dependencies
spring-boot-loader
internal-dependencies
snapshot-dependencies
application

4.3. Конфигурация Dockerfile

После извлечения мы можем добавить новый внутренний слой в наш образ Docker:

COPY --from=builder internal-dependencies/ ./

Итак, если мы сгенерируем образ, мы увидим, как Docker строит внутреннюю зависимость как новый слой:

$ mvn package
$ docker build -f src/main/docker/Dockerfile . --tag spring-docker-demo
....
Step 8/11 : COPY --from=builder internal-dependencies/ ./
---> 0e138e074118
.....

После этого мы можем проверить в истории состав слоев в образе Docker:

$ docker history --format "{{.ID}} {{.CreatedBy}} {{.Size}}" spring-docker-demo
c0d77f6af917 /bin/sh -c #(nop) ENTRYPOINT ["java" "org.s… 0B
762598a32eb7 /bin/sh -c #(nop) COPY dir:a87b8823d5125bcc4… 7.42kB
80a00930350f /bin/sh -c #(nop) COPY dir:3875f37b8a0ed7494… 0B
0e138e074118 /bin/sh -c #(nop) COPY dir:db6f791338cb4f209… 2.35kB
e079ad66e67b /bin/sh -c #(nop) COPY dir:92a8a991992e9a488… 235kB
77a9401bd813 /bin/sh -c #(nop) COPY dir:f0bcb2a510eef53a7… 16.4MB
2eb37d403188 /bin/sh -c #(nop) ENV JAVA_HOME=/opt/java/o… 0B

Как мы видим, слой теперь включает внутренние зависимости проекта.

5. Вывод

В этом уроке мы показали, как создавать эффективные образы Docker. Короче говоря, мы использовали новые функции Spring Boot для создания многоуровневых jar-файлов. Для простых проектов мы можем использовать конфигурацию по умолчанию. Мы также продемонстрировали более продвинутую конфигурацию для повторного использования слоев.

Как всегда, код доступен на GitHub .