«1. Введение

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

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

2. Многослойные JAR-файлы в Docker

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

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

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

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

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

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

3.1. Конфигурация слоев с помощью Spring Boot

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

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

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

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

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

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

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, нам нужно добавить слои в наш образ.

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

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.baeldung.docker:*:*</include>
</into>

Кроме того, мы упорядочим новый слой:

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

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

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.