Русский
Русский
English
Статистика
Реклама

Перевод Создание оптимизированных образов Docker для приложения Spring Boot

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

В этой статье рассматриваются различные способы контейнеризации приложения Spring Boot:

  • создание образа Docker с помощью файла Docker,

  • создание образа OCI из исходного кода с помощью Cloud-Native Buildpack,

  • и оптимизация изображения во время выполнения путем разделения частей JAR на разные уровни с помощью многоуровневых инструментов.

Пример кода

Эта статья сопровождается примером рабочего кодана GitHub.

Терминология контейнеров

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

  • Образ контейнера (Container image): файл определенного формата.Мы конвертируем наше приложение в образ контейнера, запустив инструмент сборки.

  • Контейнер: исполняемый экземпляр образа контейнера.

  • Движок контейнера (Container engine): процесс-демон, отвечающий за запуск контейнера.

  • Хост контейнера (Container host): хост-компьютер, на котором работает механизм контейнера.

  • Реестр контейнеров (Container registry): общее расположение, используемое для публикации и распространения образа контейнера.

  • Стандарт OCI:Open Container Initiative (OCI) - это облегченная открытая структура управления, сформированная в рамках Linux Foundation. Спецификация образов OCI определяет отраслевые стандарты для форматов образов контейнеров и среды выполнения, чтобы гарантировать, что все механизмы контейнеров могут запускать образы контейнеров, созданные любым инструментом сборки.

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

Версия 2.3 Spring Boot предоставляет плагины для создания образов OCI.

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

Построение образа контейнера традиционным способом

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

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

Давайте создадим наше приложение Spring наSpring Initializrс зависимостями web,lombokиactuator.Мы также добавляем restконтроллер, чтобы предоставить API сGETметодом.

Создание файла Docker

Затем мы помещаем это приложение в контейнер, добавляяDockerfile:

FROM adoptopenjdk:11-jre-hotspotARG JAR_FILE=target/*.jarCOPY ${JAR_FILE} application.jarEXPOSE 8080ENTRYPOINT ["java","-jar","/application.jar"]

Наш файл Docker содержит базовый образ, изadoptopenjdk, поверх которого мы копируем наш файл JAR, а затем открываем порт,8080который будет прослушивать запросы.

Сборка приложения

Сначала нужно создать приложение с помощью Maven или Gradle.Здесь мы используем Maven:

mvn clean package

Это создает исполняемый JAR-файл приложения.Нам нужно преобразовать этот исполняемый JAR в образ Docker для работы в движке Docker.

Создание образа контейнера

Затем мы помещаем этот исполняемый файл JAR в образ Docker, выполнивкоманду docker buildиз корневого каталога проекта, содержащего файл Docker, созданный ранее:

docker build  -t usersignup:v1 .

Мы можем увидеть наше изображение в списке с помощью команды:

docker images 

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

REPOSITORY          TAG                 SIZEusersignup          v1                  249MBadoptopenjdk        11-jre-hotspot      229MB

Просмотр слоев внутри изображения контейнера

Давайте посмотрим на стопку слоев внутри изображения.Мы будем использоватьинструмент dive,чтобы просмотреть эти слои:

dive usersignup:v1

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

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

Создание образа контейнера с помощью Buildpack

Сборочные пакеты (Buildpacks)- это общий термин, используемый различными предложениями Платформа как услуга (PAAS) для создания образа контейнера из исходного кода.Он был запущен Heroku в 2011 году и с тех пор был принят Cloud Foundry, Google App Engine, Gitlab, Knative и некоторыми другими.

Преимущество облачных сборочных пакетов

Одним из основных преимуществ использования Buildpack для создания образов является то, чтоизменениями конфигурации образа можно управлять централизованно (builder) и распространять на все приложения, использующие builder.

Сборочные пакеты были тесно связаны с платформой.Cloud-Native Buildpacks обеспечивают стандартизацию между платформами, поддерживая формат образа OCI, который гарантирует, что образ может запускаться движком Docker.

Использование плагина Spring Boot

Плагин Spring Boot создает образы OCI из исходного кода с помощью Buildpack.Образы создаются с использованиемbootBuildImageзадачи (Gradle) илиspring-boot:build-imageцели (Maven) и локальной установки Docker.

Мы можем настроить имя образа, необходимого для отправки в реестр Docker, указав имя вimage tag:

<plugin>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-maven-plugin</artifactId>  <configuration>    <image>      <name>docker.io/pratikdas/${project.artifactId}:v1</name>    </image>  </configuration></plugin>

Давайте воспользуемся Maven для выполненияbuild-imageцели по созданию приложения и созданию образа контейнера.Сейчас мы не используем никаких файлов Docker.

mvn spring-boot:build-image

Результат будет примерно таким:

[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:build-image (default-cli) @ usersignup ---[INFO] Building image 'docker.io/pratikdas/usersignup:v1'[INFO] [INFO]  > Pulling builder image 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 0%.... [creator]     Adding label 'org.springframework.boot.version'.. [creator]     *** Images (c311fe74ec73):.. [creator]           docker.io/pratikdas/usersignup:v1[INFO] [INFO] Successfully built image 'docker.io/pratikdas/usersignup:v1'

Из выходных данных мы видим, чтоpaketo Cloud-Native buildpackиспользуется для создания работающего образа OCI.Как и раньше, мы можем увидеть образ, указанный как образ Docker, выполнив команду:

docker images 

Вывод:

REPOSITORY                             SIZEpaketobuildpacks/run                  84.3MBgcr.io/paketo-buildpacks/builder      652MBpratikdas/usersignup                  257MB

Создание образа контейнера с помощью Jib

Jib - это плагин для создания изображений от Google, который предоставляет альтернативный метод создания образа контейнера из исходного кода.

Настраиваемjib-maven-pluginв pom.xml:

      <plugin>        <groupId>com.google.cloud.tools</groupId>        <artifactId>jib-maven-plugin</artifactId>        <version>2.5.2</version>      </plugin>

Далее мы запускаем плагин Jib с помощью команды Maven, чтобы построить приложение и создать образ контейнера.Как и раньше, здесь мы не используем никаких файлов Docker:

mvn compile jib:build -Dimage=<docker registry name>/usersignup:v1

После выполнения указанной выше команды Maven мы получаем следующий вывод:

[INFO] Containerizing application to pratikdas/usersignup:v1.....[INFO] Container entrypoint set to [java, -cp, /app/resources:/app/classes:/app/libs/*, io.pratik.users.UsersignupApplication][INFO] [INFO] Built and pushed image as pratikdas/usersignup:v1[INFO] Executing tasks:[INFO] [==============================] 100.0% complete

Выходные данные показывают, что образ контейнера создан и помещен в реестр.

Мотивации и методы создания оптимизированных изображений

У нас есть две основные причины для оптимизации:

  • Производительность: в системе оркестровки контейнеров образ контейнера извлекается из реестра образов на хост, на котором запущен механизм контейнера.Этот процесс называется планированием.Извлечение образов большого размера из реестра приводит к длительному времени планирования в системах оркестровки контейнеров и длительному времени сборки в конвейерах CI.

  • Безопасность: изображения большого размера также имеют большую область для уязвимостей.

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

Spring Boot используеттолстый JAR вкачестве формата упаковки по умолчанию.Когда мы просматриваем толстый JAR, мы видим, что приложение составляет очень маленькую часть всего JAR.Это часть, которая меняется чаще всего.Оставшаяся часть состоит из зависимостей Spring Framework.

Формула оптимизации сосредоточена вокруг изоляции приложения на отдельном уровне от зависимостей Spring Framework.

Слой зависимостей, формирующий основную часть толстого JAR-файла, загружается только один раз и кэшируется в хост-системе.

Только тонкий слой приложения вытягивается во время обновлений приложения и планирования контейнеров,как показано на этой диаграмме:

В следующих разделах мы рассмотрим, как создавать эти оптимизированные образы для приложения Spring Boot.

Создание оптимизированного образа контейнера для приложения Spring Boot с помощью Buildpack

Spring Boot 2.3 поддерживает многоуровневость путем извлечения частей толстого JAR-файла в отдельные слои.Функция наслоения по умолчанию отключена, и ее необходимо явно включить с помощью плагина Spring Boot Maven:

<plugin>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-maven-plugin</artifactId>  <configuration>    <layers>      <enabled>true</enabled>    </layers>  </configuration> </plugin>

Мы будем использовать эту конфигурацию для создания нашего образа контейнера сначала с помощью Buildpack, а затем с помощью Docker в следующих разделах.

Давайте запустимbuild-imageцельMavenдля создания образа контейнера:

mvn spring-boot:build-image

Если мы запустим Dive, чтобы увидеть слои в результирующем изображении, мы увидим, что уровень приложения (обведен красным) намного меньше в диапазоне килобайт по сравнению с тем, что мы получили с использованием толстого формата JAR:

Создание оптимизированного образа контейнера для приложения Spring Boot с помощью Docker

Вместо использования плагина Maven или Gradle мы также можем создать многоуровневый образ JAR Docker с файлом Docker.

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

Содержимое полученного JAR после сборки с помощью Maven с включенной функцией наслоения будет выглядеть следующим образом:

META-INF/.BOOT-INF/lib/.BOOT-INF/lib/spring-boot-jarmode-layertools-2.3.3.RELEASE.jarBOOT-INF/classpath.idxBOOT-INF/layers.idx

В выходных данных отображается дополнительный JAR с именемspring-boot-jarmode-layertoolsиlayersfle.idxфайл.Этот дополнительный JAR-файл предоставляет возможность многоуровневой обработки, как описано в следующем разделе.

Извлечение зависимостей на отдельных слоях

Чтобы просмотреть и извлечь слои из нашего многоуровневого JAR, мы используем системное свойство-Djarmode=layertoolsдля запускаspring-boot-jarmode-layertoolsJAR вместо приложения:

java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar

Выполнение этой команды дает вывод, содержащий доступные параметры команды:

Usage:  java -Djarmode=layertools -jar usersignup-0.0.1-SNAPSHOT.jarAvailable commands:  list     List layers from the jar that can be extracted  extract  Extracts layers from the jar for image creation  help     Help about any command

Вывод показывает командыlist,extractиhelpсhelpбыть по умолчанию.Давайте запустим команду сlistопцией:

java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar list
dependenciesspring-boot-loadersnapshot-dependenciesapplication

Мы видим список зависимостей, которые можно добавить как слои.

Слои по умолчанию:

Имя слоя

Содержание

dependencies

любая зависимость, версия которой не содержит SNAPSHOT

spring-boot-loader

Классы загрузчика JAR

snapshot-dependencies

любая зависимость, версия которой содержит SNAPSHOT

application

классы приложений и ресурсы

Слои определены вlayers.idxфайле в том порядке, в котором они должны быть добавлены в образ Docker.Эти слои кэшируются в хосте после первого извлечения, поскольку они не меняются.На хост загружается только обновленный уровень приложения, что происходит быстрее из-за уменьшенного размера.

Построение образа с зависимостями, извлеченными в отдельные слои

Мы построим финальный образ в два этапа, используя метод, называемыймногоэтапной сборкой.На первом этапе мы извлечем зависимости, а на втором этапе мы скопируем извлеченные зависимости в окончательный образ .

Давайте модифицируем наш файл Docker для многоэтапной сборки:

# the first stage of our build will extract the layersFROM adoptopenjdk:14-jre-hotspot as builderWORKDIR applicationARG JAR_FILE=target/*.jarCOPY ${JAR_FILE} application.jarRUN java -Djarmode=layertools -jar application.jar extract# the second stage of our build will copy the extracted layersFROM adoptopenjdk:14-jre-hotspotWORKDIR applicationCOPY --from=builder application/dependencies/ ./COPY --from=builder application/spring-boot-loader/ ./COPY --from=builder application/snapshot-dependencies/ ./COPY --from=builder application/application/ ./ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

Сохраняем эту конфигурацию в отдельном файле -Dockerfile2.

Собираем образ Docker с помощью команды:

docker build -f Dockerfile2 -t usersignup:v1 .

После выполнения этой команды мы получаем такой вывод:

Sending build context to Docker daemon  20.41MBStep 1/12 : FROM adoptopenjdk:14-jre-hotspot as builder14-jre-hotspot: Pulling from library/adoptopenjdk..Successfully built a9ebf6970841Successfully tagged userssignup:v1

Мы видим, что образ Docker создается с идентификатором изображения, а затем тегируется.

Наконец, мы запускаем команду Dive, как и раньше, чтобы проверить слои внутри сгенерированного образа Docker.Мы можем указать идентификатор изображения или тег в качестве входных данных для команды Dive:

dive userssignup:v1

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

Извлечение внутренних зависимостей на отдельных слоях

Мы можем дополнительно уменьшить размер уровня приложения, извлекая любые из наших пользовательских зависимостей в отдельный уровень вместо того, чтобы упаковывать их вместе с приложением, объявив их вymlподобном файле с именемlayers.idx:

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

В этом файлеlayers.idxмы добавили настраиваемую зависимость с именем,io.myorgсодержащим зависимости организации, полученные из общего репозитория.

Вывод

В этой статье мы рассмотрели использование Cloud-Native Buildpacks для создания образа контейнера непосредственно из исходного кода.Это альтернатива использованию Docker для создания образа контейнера обычным способом: сначала создается толстый исполняемый файл JAR, а затем упаковывается его в образ контейнера, указав инструкции в файле Docker.

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

Вы можете найти весь исходный код, использованный в статье наGithub.

Справочник команд

Вот краткое изложение команд, которые мы использовали в этой статье для быстрого ознакомления.

Очистка контекста:

docker system prune -a

Создание образа контейнера с помощью файла Docker:

docker build -f <Docker file name> -t <tag> .

Собираем образ контейнера из исходного кода (без Dockerfile):

mvn spring-boot:build-image

Просмотр слоев зависимостей.Перед сборкой JAR-файла приложения убедитесь, что функция наслоения включена в spring-boot-maven-plugin:

java -Djarmode=layertools -jar application.jar list

Извлечение слоев зависимостей.Перед сборкой JAR-файла приложения убедитесь, что функция наслоения включена в spring-boot-maven-plugin:

 java -Djarmode=layertools -jar application.jar extract

Просмотр списка образов контейнеров

docker images

Просмотр слев внутри образаконтейнера (убедитесь, что установлен инструмент для погружения):

dive <image ID or image tag>
Источник: habr.com
К списку статей
Опубликовано: 06.10.2020 14:22:26
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Java

Виртуализация

Spring boot

Docker

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru