Я новичок в Docker и не знаю, как запустить java-проект с maven, хотя я прочитал много документов и попробовал много методов.
- Должен ли я создавать образ с помощью
Dockerfile
? - Какие команды похожи на запуск проекта maven на хосте
Dockerfile
?
Ответы:
Это не учебник по весенней загрузке. Это обновленный ответ на вопрос о том, как запустить сборку Maven в контейнере Docker.
Вопрос был опубликован 4 года назад.
Используйте инициализатор Spring для создания демонстрационного приложения
Распакуйте zip-архив локально
#
# Build stage
#
FROM maven:3.6.0-jdk-11-slim AS build
COPY src /home/app/src
COPY pom.xml /home/app
RUN mvn -f /home/app/pom.xml clean package
#
# Package stage
#
FROM openjdk:11-jre-slim
COPY --from=build /home/app/target/demo-0.0.1-SNAPSHOT.jar /usr/local/lib/demo.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/local/lib/demo.jar"]
Заметка
docker build -t demo .
$ docker run --rm -it demo:latest
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.3.RELEASE)
2019-02-22 17:18:57.835 INFO 1 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication v0.0.1-SNAPSHOT on f4e67677c9a9 with PID 1 (/usr/local/bin/demo.jar started by root in /)
2019-02-22 17:18:57.837 INFO 1 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2019-02-22 17:18:58.294 INFO 1 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.711 seconds (JVM running for 1.035)
Прочтите документацию Docker Hub о том, как можно оптимизировать сборку Maven для использования локального репозитория для кеширования jar-файлов.
Этому вопросу уже 4 года, и за это время будет справедливо сказать, что создание приложения с использованием Docker претерпело значительные изменения.
Этот новый стиль позволяет вам создавать более легкие изображения, которые не инкапсулируют ваши инструменты сборки и исходный код.
В этом примере снова используется официальный базовый образ maven для запуска первого этапа сборки с использованием желаемой версии Maven. Вторая часть файла определяет, как собранный jar собирается в окончательное выходное изображение.
FROM maven:3.5-jdk-8 AS build
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
RUN mvn -f /usr/src/app/pom.xml clean package
FROM gcr.io/distroless/java
COPY --from=build /usr/src/app/target/helloworld-1.0.0-SNAPSHOT.jar /usr/app/helloworld-1.0.0-SNAPSHOT.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/app/helloworld-1.0.0-SNAPSHOT.jar"]
Заметка:
Я не использовал этот подход, но кажется заслуживающим исследования, поскольку он позволяет создавать образы без необходимости создавать такие неприятные вещи, как файлы Docker :-)
https://github.com/GoogleContainerTools/jib
В проекте есть плагин Maven, который интегрирует упаковку вашего кода непосредственно в рабочий процесс Maven.
Попробуйте использовать новые официальные изображения, есть один для Maven
https://registry.hub.docker.com/_/maven/
Образ можно использовать для запуска Maven во время сборки для создания скомпилированного приложения или, как в следующих примерах, для запуска сборки Maven в контейнере.
Следующая команда запускает вашу сборку Maven внутри контейнера:
docker run -it --rm \
-v "$(pwd)":/opt/maven \
-w /opt/maven \
maven:3.2-jdk-7 \
mvn clean install
Примечания:
Запустите контейнер Nexus
docker run -d -p 8081:8081 --name nexus sonatype/nexus
Создайте файл "settings.xml":
<settings>
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://nexus:8081/content/groups/public/</url>
</mirror>
</mirrors>
</settings>
Теперь запустите Maven, связывающийся с контейнером нексуса, чтобы зависимости были кэшированы.
docker run -it --rm \
-v "$(pwd)":/opt/maven \
-w /opt/maven \
--link nexus:nexus \
maven:3.2-jdk-7 \
mvn -s settings.xml clean install
Примечания:
Может быть много способов .. Но я реализовал двумя способами
Данный пример относится к проекту maven.
1. Использование Dockerfile в проекте maven
Используйте следующую файловую структуру:
Demo
└── src
| ├── main
| │ ├── java
| │ └── org
| │ └── demo
| │ └── Application.java
| │
| └── test
|
├──── Dockerfile
├──── pom.xml
И обновите Dockerfile как:
FROM java:8
EXPOSE 8080
ADD /target/demo.jar demo.jar
ENTRYPOINT ["java","-jar","demo.jar"]
Перейдите в папку проекта и введите следующую команду, с помощью которой вы сможете создать образ и запустить этот образ:
$ mvn clean
$ mvn install
$ docker build -f Dockerfile -t springdemo .
$ docker run -p 8080:8080 -t springdemo
Получите видео на Spring Boot с Docker
2. Использование плагинов Maven
Добавить данный плагин maven в pom.xml
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.4.5</version>
<configuration>
<imageName>springdocker</imageName>
<baseImage>java</baseImage>
<entryPoint>["java", "-jar", "/${project.build.finalName}.jar"]</entryPoint>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
Перейдите в папку проекта и введите следующую команду, с помощью которой вы сможете создать образ и запустить этот образ:
$ mvn clean package docker:build
$ docker images
$ docker run -p 8080:8080 -t <image name>
В первом примере мы создаем Dockerfile и предоставляем базовый образ и добавляем jar и так, после этого мы запустим команду docker для создания образа с определенным именем, а затем запустим этот образ.
В то время как во втором примере мы используем плагин maven, в котором мы предоставляем, baseImage
и imageName
поэтому нам не нужно создавать здесь Dockerfile ... после упаковки проекта maven мы получим образ докера, и нам просто нужно запустить этот образ ..
Как правило, вы должны создавать толстый JAR с использованием Maven (JAR, который содержит ваш код и все зависимости).
Затем вы можете написать Dockerfile , соответствующий вашим требованиям (если вы можете создать толстый JAR, вам понадобится только базовая ОС, такая как CentOS, и JVM).
Это то, что я использую для приложения Scala (основанного на Java).
FROM centos:centos7
# Prerequisites.
RUN yum -y update
RUN yum -y install wget tar
# Oracle Java 7
WORKDIR /opt
RUN wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/7u71-b14/server-jre-7u71-linux-x64.tar.gz
RUN tar xzf server-jre-7u71-linux-x64.tar.gz
RUN rm -rf server-jre-7u71-linux-x64.tar.gz
RUN alternatives --install /usr/bin/java java /opt/jdk1.7.0_71/bin/java 1
# App
USER daemon
# This copies to local fat jar inside the image
ADD /local/path/to/packaged/app/appname.jar /app/appname.jar
# What to run when the container starts
ENTRYPOINT [ "java", "-jar", "/app/appname.jar" ]
# Ports used by the app
EXPOSE 5000
Это создает образ на основе CentOS с Java7. При запуске он выполнит jar-файл вашего приложения.
Лучший способ развернуть его - через реестр Docker, это как Github для образов Docker.
Вы можете построить такой образ:
# current dir must contain the Dockerfile
docker build -t username/projectname:tagname .
Затем вы можете отправить изображение следующим образом:
docker push username/projectname # this pushes all tags
Как только образ появится в реестре Docker, вы можете извлечь его из любой точки мира и запустить.
См. Руководство пользователя Docker для получения дополнительной информации.
Что нужно иметь в виду :
Вы также можете перетащить свой репозиторий внутрь изображения и создать банку как часть выполнения контейнера, но это не лучший подход, поскольку код может измениться, и вы можете без предупреждения использовать другую версию приложения.
Создание толстой банки решает эту проблему.
RUN wget -O {project.build.finalname}.jar
но я хочу загрузить указанную выше банку из nexus.
Вот мой вклад.
Я не буду пытаться перечислять все инструменты / библиотеки / плагины, которые существуют для использования Docker с Maven. Некоторые ответы уже сделали это.
вместо этого я сосредоточусь на типологии приложений и способе работы с Dockerfile.
Dockerfile
действительно простая и важная концепция Docker (все известные / общедоступные образы полагаются на это), и я думаю, что попытка избежать понимания и использования Dockerfile
s - не обязательно лучший способ войти в мир Docker.
1) Для приложений, которые мы хотим продолжить, запускать их на установленном / автономном сервере Java (Tomcat, JBoss и т. Д.)
Дорога сложнее, и это не идеальная цель, потому что это увеличивает сложность (мы должны управлять / поддерживать сервер), а также он менее масштабируем и менее быстр, чем встроенные серверы, с точки зрения сборки / развертывания / удаления.
Но для устаревших приложений это можно рассматривать как первый шаг.
Как правило, идея здесь состоит в том, чтобы определить образ Docker для сервера и определить образ для развертывания каждого приложения.
Образы докеров для приложений создают ожидаемые WAR / EAR, но они не выполняются как контейнер, а образ для серверного приложения развертывает компоненты, созданные этими образами, как развернутые приложения.
Это действительно хорошее улучшение для огромных приложений (миллионы строк кода) с большим количеством устаревших вещей, которые так сложно перенести на встроенное решение с полной загрузкой с пружинной загрузкой.
Я не буду подробно описывать этот подход, поскольку он предназначен для незначительных случаев использования Docker, но я хотел раскрыть общую идею этого подхода, потому что я думаю, что разработчикам, которые сталкиваются с этими сложными случаями, приятно знать, что некоторые двери открыты для интегрировать Docker.
2) Для приложений, которые встраивают / загружают сам сервер (Spring Boot со встроенным сервером: Tomcat, Netty, Jetty ...)
Это идеальная цель для Docker . Я указал Spring Boot, потому что это действительно хороший фреймворк для этого и который также имеет очень высокий уровень ремонтопригодности, но теоретически мы могли бы использовать любой другой способ Java для достижения этого.
Как правило, идея здесь состоит в том, чтобы определить образ Docker для каждого развертываемого приложения.
Образы докеров для приложений создают JAR или набор файлов JAR / классов / конфигурации, и они запускают JVM с приложением (команда java), когда мы создаем и запускаем контейнер из этих образов.
Для новых приложений или приложений, которые не слишком сложны для миграции, этот способ должен быть предпочтительнее автономных серверов, потому что это стандартный и наиболее эффективный способ использования контейнеров.
Я подробно расскажу об этом подходе.
1) Без Spring Boot
Идея состоит в том, чтобы создать толстую банку с Maven (плагин сборки maven и помощь плагина maven shade для этого), которая содержит как скомпилированные классы приложения, так и необходимые зависимости maven.
Тогда мы можем выделить два случая:
если приложение является настольным или автономным приложением (которое не нужно развертывать на сервере): мы могли бы указать, как CMD/ENTRYPOINT
в Dockerfile
java-исполнении приложения:java -cp .:/fooPath/* -jar myJar
если приложение является серверным приложением, например Tomcat, идея такая же: получить толстую банку приложения и запустить JVM в CMD/ENTRYPOINT
. Но здесь есть важное отличие: нам нужно включить некоторую логику и определенные библиотеки ( org.apache.tomcat.embed
библиотеки и некоторые другие), которые запускают встроенный сервер при запуске основного приложения.
У нас есть подробное руководство на сайте heroku .
В первом случае (автономное приложение) это простой и эффективный способ использования Docker.
Во втором случае (серверное приложение), которое работает, но не является прямым, может быть подвержено ошибкам и не является очень расширяемой моделью, потому что вы не помещаете свое приложение в рамки зрелой структуры, такой как Spring Boot, которая делает много этих вещей для вас, а также обеспечивает высокий уровень расширения.
Но у этого есть преимущество: у вас есть высокий уровень свободы, потому что вы напрямую используете встроенный Tomcat API.
2) С Spring Boot
Наконец, мы идем.
Это одновременно просто, эффективно и очень хорошо документировано.
На самом деле существует несколько подходов к запуску приложения Maven / Spring Boot в Docker.
Разоблачать их всех было бы долго и, может быть, скучно.
Лучший выбор зависит от ваших требований.
Но как бы то ни было, стратегия сборки с точки зрения слоев докеров выглядит одинаково.
Мы хотим использовать многоступенчатую сборку: одна полагается на Maven для разрешения зависимостей и для сборки, а другая полагается на JDK или JRE для запуска приложения.
Этап сборки (образ Maven):
mvn dependency:resolve-plugins
прикованный к нему mvn dependency:resolve
может работать, но не всегда. package
выполнение для упаковки толстой банки могут полагаться на разные артефакты / плагины и даже для одного и того же артефакта / плагина, они все равно могут получить другую версию. Таким образом, более безопасный подход, хотя и потенциально более медленный, заключается в разрешении зависимостей путем выполнения именно той mvn
команды, которая используется для упаковки приложения (которая будет извлекать именно те зависимости, которые вам нужны), но путем пропуска исходной компиляции и удаления целевой папки, чтобы ускорить обработку и предотвратить обнаружение нежелательного изменения слоя на этом этапе. Этап выполнения (JDK или образ JRE):
Вот два примера.
а) Простой способ без кеша для загруженных зависимостей maven
Dockerfile:
########Maven build stage########
FROM maven:3.6-jdk-11 as maven_build
WORKDIR /app
#copy pom
COPY pom.xml .
#resolve maven dependencies
RUN mvn clean package -Dmaven.test.skip -Dmaven.main.skip -Dspring-boot.repackage.skip && rm -r target/
#copy source
COPY src ./src
# build the app (no dependency download here)
RUN mvn clean package -Dmaven.test.skip
# split the built app into multiple layers to improve layer rebuild
RUN mkdir -p target/docker-packaging && cd target/docker-packaging && jar -xf ../my-app*.jar
########JRE run stage########
FROM openjdk:11.0-jre
WORKDIR /app
#copy built app layer by layer
ARG DOCKER_PACKAGING_DIR=/app/target/docker-packaging
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/lib /app/lib
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/classes /app/classes
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/META-INF /app/META-INF
#run the app
CMD java -cp .:classes:lib/* \
-Djava.security.egd=file:/dev/./urandom \
foo.bar.MySpringBootApplication
Недостаток этого решения? Любые изменения в pom.xml означают воссоздание всего уровня, который загружает и сохраняет зависимости maven. Как правило, это неприемлемо для приложений со многими зависимостями (а Spring Boot извлекает много зависимостей), в целом, если вы не используете диспетчер репозитория maven во время сборки образа.
б) Более эффективный способ с кешем для загруженных зависимостей maven
Подход здесь тот же, но загрузки зависимостей maven кешируются в кеше построителя докеров.
Операция с кешем зависит от buildkit (экспериментального API докера).
Чтобы включить buildkit, необходимо установить переменную env DOCKER_BUILDKIT = 1 (вы можете сделать это там, где хотите: .bashrc, командная строка, json-файл демона докеров ...).
Dockerfile:
# syntax=docker/dockerfile:experimental
########Maven build stage########
FROM maven:3.6-jdk-11 as maven_build
WORKDIR /app
#copy pom
COPY pom.xml .
#copy source
COPY src ./src
# build the app (no dependency download here)
RUN --mount=type=cache,target=/root/.m2 mvn clean package -Dmaven.test.skip
# split the built app into multiple layers to improve layer rebuild
RUN mkdir -p target/docker-packaging && cd target/docker-packaging && jar -xf ../my-app*.jar
########JRE run stage########
FROM openjdk:11.0-jre
WORKDIR /app
#copy built app layer by layer
ARG DOCKER_PACKAGING_DIR=/app/target/docker-packaging
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/lib /app/lib
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/classes /app/classes
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/META-INF /app/META-INF
#run the app
CMD java -cp .:classes:lib/* \
-Djava.security.egd=file:/dev/./urandom \
foo.bar.MySpringBootApplication
mavenCentral()
в своих зависимостях gradle наmaven {url "http://nexus:8081..."
и теперь просто получаю проблемы с разрешением.