1. Введение
Docker — это стандарт де-факто для создания автономных приложений. Начиная с версии 2.3.0, Spring Boot включает несколько улучшений, помогающих нам создавать эффективные образы Docker. Таким образом, это позволяет разбивать приложение на разные уровни .
Другими словами, исходный код находится на своем собственном уровне. Поэтому его можно самостоятельно перестроить, повысив эффективность и время запуска. В этом руководстве мы увидим, как использовать новые возможности Spring Boot для повторного использования слоев Docker.
2. Многоуровневые банки в 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.
- Многоуровневые 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 .