Статьи

Spring Boot: запуск и сборка в Docker

Существует множество руководств по «Docker для Java-разработчиков», но большинство из них не заботятся о небольших и эффективных образах Docker.

Я объединил много ресурсов о том, как создать простой и быстрый образ Docker, содержащий любое приложение, похожее на Spring Boot.

Мои цели:

  • Создайте отдельный и портативный Dockerfile (как можно более общий).
  • Заставьте Maven строить внутри Docker (нет необходимости иметь Maven локально).
  • Не загружайте никакие зависимости Maven повторно, если в pom.xml нет изменений (перестраивайте изображение как можно быстрее).
  • Окончательный образ Docker должен содержать только само приложение (без исходных кодов, без зависимостей Maven, требуемых сборкой Maven и т. Д.)
  • Окончательное изображение должно быть как можно меньше (полный JDK не требуется).
  • Приложение внутри Docker должно оставаться максимально настраиваемым (со всеми параметрами конфигурации Spring Boot).
  • Возможность включить отладку (по запросу).
  • Возможность просмотра файлов журнала.

Окончательное изображение предназначено для разработки, но оно не содержит никаких производственных деталей и полностью настраивается.


Чтобы увидеть рабочий пример, посмотрите 
мой проект GitHub .

Чтобы выполнить одно требование переносимого Dockerfile, мне нужно использовать  многоэтапные сборки Docker .

Он будет состоять из двух основных частей (этапов):

  • Строительная часть.
  • Часть времени выполнения.

Строительная часть Dockerfile

### BUILD image
FROM maven:3-jdk-11 as builder
# create app folder for sources
RUN mkdir -p /build
WORKDIR /build
COPY pom.xml /build
#Download all required dependencies into one layer
RUN mvn -B dependency:resolve dependency:resolve-plugins
#Copy source code
COPY src /build/src
# Build application
RUN mvn package

Я начал с  официального имиджа Maven , так что вы можете изменить это, как пожелаете. Самая интересная часть это:

RUN mvn -B dependency:resolve dependency:resolve-plugins

Он загружает все зависимости, требуемые вашим приложением или плагинами, вызываемыми в процессе сборки. Тогда все зависимости являются частью одного слоя. Этот слой не изменяется до тех пор, пока в файле pom.xml не будут найдены какие-либо изменения. 

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

Второй вариант, как загрузить необходимые зависимости, приходит с  официального сайта Docker Maven  (когда у вас есть проблемы с предыдущим вариантом):

RUN mvn -B -e -C -T 1C org.apache.maven.plugins:maven-dependency-plugin:3.0.2:go-offline

Как настроить параметры Maven

Есть много ситуаций, когда вам нужно изменить настройки Maven по умолчанию для вашей индивидуальной сборки. Для этого вам необходимо скопировать файл settings.xml в изображение, прежде чем предоставить определение образа компоновщика, например:

FROM maven:3-jdk-11 as builder
#Copy Custom Maven settings
COPY settings.xml /root/.m2/

Часть времени выполнения Dockerfile

FROM openjdk:11-slim as runtime
EXPOSE 8080
#Set app home folder
ENV APP_HOME /app
#Possibility to set JVM options (https://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html)
ENV JAVA_OPTS=""

#Create base app folder
RUN mkdir $APP_HOME
#Create folder to save configuration files
RUN mkdir $APP_HOME/config
#Create folder with application logs
RUN mkdir $APP_HOME/log

VOLUME $APP_HOME/log
VOLUME $APP_HOME/config

WORKDIR $APP_HOME
#Copy executable jar file from the builder image
COPY --from=builder /build/target/*.jar app.jar

ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar app.jar" ]
#Second option using shell form:
#ENTRYPOINT exec java $JAVA_OPTS -jar app.jar $0 $@

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

#Copy executable jar file from the builder image
COPY --from=builder /build/target/*.jar app.jar

Я копирую из образа строителя, вижу парам  –from. Для получения дополнительной информации о копировании файлов из других изображений см. Страницу документации Docker. 

Что касается приложения Spring Boot, созданный файл JAR является исполняемым, поэтому можно запустить наше приложение с помощью одной команды:

ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar app.jar" ]

Чтобы сократить  время запуска Tomcat,  необходимо указать системное свойство /dev/urandom.

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

Как за один шаг собрать и запустить Spring Boot Application в Docker

docker build -t <image_tag> . &amp;&amp; docker run -p 8080:8080 <image_tag>

Приведенная выше команда создаст ваше приложение с Maven и запустит его без каких-либо задержек. Это самый простой способ без каких-либо настроек. Файл может содержать некоторые конкретные требования, поэтому вот пара из них.

Теперь вы можете посетить URL, чтобы получить ответ из  моего примера GitHub :

http://localhost:8081/customer/10

Как отладить?

В моем примере используется Java 11, поэтому есть несколько параметров JVM для включения режима отладки:

docker build -t <image_tag> . && docker run -p 8080:8080 -p 5005:5005 --env JAVA_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 <image_tag>

Вам нужно добавить переменную окружения Docker,  JAVA_OPTS с параметрами виртуальной машины Java и карту внутренний порт отладки на внешней стороне контейнера:  -p 5005:5005.

Для контейнеров Java 5-8 используйте этот  JAVA_OPTS параметр:

JAVA_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

Как настроить ведение журнала

Контейнер времени выполнения содержит папку под названием app /app/log со всеми файлами журнала. Этот путь может быть легко смонтирован на вашем хосте:

docker build -t <image_tag> . && docker run -p 8080:8080 -v /opt/spring-boot/test/log:/app/log <image_tag>

Как изменить конфигурацию приложения

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

Обратите внимание, что все конфигурации возможны при использовании  формы exec ENTRYPOINT . При использовании формы оболочки  ENTRYPOINTвам необходимо передать все аргументы командной строки вручную:

ENTRYPOINT exec java $JAVA_OPTS -jar app.jar $0 $@

Аргументы командной строки

Spring Boot автоматически принимает все аргументы командной строки, и эти аргументы передаются в  run команду внутри Docker:

docker build -t <image_tag> . && docker run -p 8080:8080 <image_tag> --logging.level.org.springframework=debug

Свойства системы

Аналогичным образом используются обычные системные свойства:

docker build -t <image_tag> . && docker run -p 8080:8080 --env JAVA_OPTS=-Dlogging.level.org.springframework=DEBUG <image_tag>

Переменные среды

Вы можете использовать переменные среды вместо системных свойств. В большинстве операционных систем не допускаются имена ключей, разделенные точками, но вместо этого можно использовать подчеркивание (например,   SPRING_CONFIG_NAME  вместо   spring.config.name ). Проверьте  страницу документации  для получения дополнительной информации.

docker build -t <image_tag> . && docker run -p 8080:8080 --env LOGGING_LEVEL_ORG_SPRINGFRAMEWORK=DEBUG <image_tag>

Смонтируйте свой собственный файл конфигурации

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

docker build -t <image_tag> . && docker run -p 8080:8080 -v /opt/spring-boot/test/config:/app/config:ro <image_tag>

Таким образом, ваша локальная папка  /opt/spring-boot/test/config должна содержать файл  application.properties. Это имя файла конфигурации по умолчанию, и его можно легко изменить, установив свойство spring.config.name.

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


Как упоминалось выше, см. Пример проекта на 
моем GitHub  для всего кода.

Несколько интересных ссылок: