1. Введение
По мере того, как все больше организаций переходят на контейнеры и виртуальные серверы, Docker становится все более важной частью рабочих процессов разработки программного обеспечения. С этой целью одной из замечательных новых функций в Spring Boot 2.3 является возможность легко создавать образ Docker для приложений Spring Boot.
В этом руководстве мы рассмотрим, как создавать образы Docker для приложения Spring Boot.
2. Традиционные сборки Docker
Традиционный способ создания образов Docker с помощью Spring Boot — использовать Dockerfile. Ниже приведен простой пример:
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"]
Затем мы могли бы использовать команду сборки docker
для создания образа Docker. Это прекрасно работает для большинства приложений, но есть пара недостатков.
Во-первых, мы используем толстую банку, созданную Spring Boot. Это может повлиять на время запуска, особенно в контейнерной среде . Мы можем сэкономить время запуска, добавив вместо этого развернутое содержимое файла jar.
Во-вторых, образы Docker строятся слоями. Природа толстых банок Spring Boot приводит к тому, что весь код приложения и сторонние библиотеки помещаются в один слой. Это означает, что даже если изменяется только одна строка кода, весь слой должен быть перестроен .
Разбивая банку перед сборкой, код приложения и сторонние библиотеки получают свой собственный уровень. Это позволяет нам использовать механизм кэширования Docker. Теперь, когда изменяется одна строка кода, необходимо перестроить только соответствующий слой.
Имея это в виду, давайте посмотрим, как Spring Boot улучшил процесс создания образов Docker.
3. Пакеты сборки
Пакеты сборки — это инструмент, который обеспечивает зависимости между фреймворком и приложением .
Например, при наличии толстого jar-файла Spring Boot сборочный пакет предоставит нам среду выполнения Java. Это позволяет нам пропустить Dockerfile и автоматически получить разумный образ Docker.
Spring Boot включает в себя поддержку сборочных пакетов Maven и Gradle. Например, при сборке с помощью Maven мы запустим команду:
./mvnw spring-boot:build-image
Давайте посмотрим на некоторые соответствующие выходные данные, чтобы увидеть, что происходит:
[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
Первая строка показывает, что мы создали нашу стандартную толстую банку, как и любой типичный пакет maven.
Следующая строка начинает сборку образа Docker. Сразу после этого мы видим сборку в сборщике Packeto .
Packeto — это реализация облачных пакетов сборки. Он выполняет работу по анализу нашего проекта и определению необходимых фреймворков и библиотек . В нашем случае он определяет, что у нас есть проект Spring Boot, и добавляет необходимые пакеты сборки.
Наконец, мы видим сгенерированный образ Docker и общее время сборки. Обратите внимание, что при первой сборке мы тратим немало времени на загрузку сборочных пакетов и создание различных слоев.
Одной из замечательных особенностей пакетов сборки является то, что образ Docker состоит из нескольких слоев. Поэтому, если мы изменим только код нашего приложения, последующие сборки будут намного быстрее:
...
[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. Слоистые банки
В некоторых случаях мы можем предпочесть не использовать пакеты сборки — возможно, наша инфраструктура уже привязана к другому инструменту или у нас уже есть пользовательские файлы Docker, которые мы хотим использовать повторно.
По этим причинам Spring Boot также поддерживает сборку образов Docker с использованием многоуровневых jar-файлов . Чтобы понять, как это работает, давайте посмотрим на типичную компоновку толстых банок Spring Boot:
org/
springframework/
boot/
loader/
...
BOOT-INF/
classes/
...
lib/
...
Жирная банка состоит из 3 основных областей:
- Классы Bootstrap, необходимые для запуска приложения Spring
- Код приложения
- сторонние библиотеки
Структура многослойных банок выглядит аналогично, но мы получаем новый файл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/"
По умолчанию Spring Boot предоставляет четыре уровня:
зависимости
: типичные зависимости от третьих лицsnapshot-dependencies
: зависимости моментальных снимков от третьих лицресурсы
: статические ресурсыapplication
: код приложения и ресурсы
Цель состоит в том, чтобы поместить код приложения и сторонние библиотеки в слои, отражающие частоту их изменения .
Например, код приложения, скорее всего, изменяется чаще всего, поэтому он получает свой собственный уровень. Кроме того, каждый уровень может развиваться сам по себе, и только после изменения слоя он будет перестроен для образа Docker.
Теперь, когда мы понимаем новую многоуровневую структуру jar, давайте посмотрим, как мы можем использовать ее для создания образов Docker.
4.1. Создание многослойных банок
Во-первых, мы должны настроить наш проект для создания многослойной банки. С Maven это означает добавление новой конфигурации в раздел плагинов Spring Boot нашего POM:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
В этой конфигурации команда пакета
Maven (вместе с любой из зависимых от нее команд) создаст новую многоуровневую банку с использованием четырех слоев по умолчанию, упомянутых ранее.
4.2. Просмотр и извлечение слоев
Затем нам нужно извлечь слои из банки, чтобы образ Docker имел правильные слои.
Чтобы изучить слои любой многослойной банки, мы можем запустить команду:
java -Djarmode=layertools -jar demo-0.0.1.jar list
Затем, чтобы извлечь их, мы запустим:
java -Djarmode=layertools -jar demo-0.0.1.jar extract
4.3. Создание образа Docker
Самый простой способ включить эти слои в образ Docker — использовать 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"]
Этот Dockerfile извлекает слои из нашей толстой банки, а затем копирует каждый слой в образ Docker. Каждая директива COPY
приводит к созданию нового слоя в окончательном образе Docker .
Если мы создадим этот Dockerfile, мы увидим, что каждый слой из многоуровневой банки добавляется в образ Docker как отдельный слой:
...
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. Вывод
В этом руководстве мы рассмотрели различные способы создания образов Docker с помощью Spring Boot. Используя пакеты сборки, мы можем получить подходящие образы Docker без шаблонных или пользовательских конфигураций. Или, приложив немного больше усилий, мы можем использовать многослойные банки, чтобы получить более адаптированный образ Docker.
Все примеры из этого руководства можно найти на GitHub .
Для получения дополнительной информации об использовании Java и Docker ознакомьтесь с руководством по jib .