Як закріпити проект Maven? і скільки способів це зробити?


87

Я новачок у Docker, і я не знаю, як запустити java-проект з maven, хоча я прочитав багато документів і спробував багато методів.

  1. Чи слід будувати зображення за допомогою Dockerfile?
  2. Якими є команди, коли потрібно запустити проект maven у хості Dockerfile?

Відповіді:


122

Робочий приклад.

Це не підручник з весняного завантаження. Це оновлена ​​відповідь на запитання про те, як запустити збірку Maven у контейнері Docker.

Питання спочатку розміщено 4 роки тому.

1. Створіть додаток

Використовуйте пружинний ініціалізатор для створення демонстраційного додатка

https://start.spring.io/

введіть тут опис зображення

Витягніть zip-архів локально

2. Створіть файл Docker

#
# 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"]

Примітка

  • У цьому прикладі використовується багатоступенева побудова . Перший етап використовується для побудови коду. Другий етап містить лише побудований jar та JRE для його запуску (зверніть увагу, як jar копіюється між етапами).

3. Побудуйте образ

docker build -t demo .

4. Запустіть зображення

$ 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 про те, як збірку Maven можна оптимізувати для використання локального сховища для кешування банок.

Оновлення (2019-02-07)

Зараз цьому питанню виповнилося 4 роки, і на той час справедливо сказати, що створення додатків за допомогою Docker зазнало значних змін.

Варіант 1: Багатоступенева збірка

Цей новий стиль дозволяє створювати більш легкі зображення, які не включають ваші інструменти побудови та вихідний код.

У прикладі тут знову використовується офіційне базове зображення 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"]  

Примітка:

  • Я використовую безтурботне базове зображення Google, яке прагне забезпечити достатньо часу роботи для програми Java.

Варіант 2: Джиб

Я не використовував цей підхід, але видається гідним розслідування, оскільки він дозволяє створювати зображення без необхідності створювати неприємні речі, такі як Dockerfiles :-)

https://github.com/GoogleContainerTools/jib

Проект має плагін Maven, який інтегрує упаковку вашого коду безпосередньо у робочий процес Maven.


Оригінальна відповідь (включено для повноти, але написано століття тому)

Спробуйте використати нові офіційні зображення, є одне для Maven

https://registry.hub.docker.com/_/maven/

Зображення можна використовувати для запуску Maven під час побудови для створення скомпільованої програми або, як у наступних прикладах, для запуску збірки Maven у контейнері.

Приклад 1 - Maven працює в контейнері

Наступна команда запускає вашу збірку Maven всередині контейнера:

docker run -it --rm \
       -v "$(pwd)":/opt/maven \
       -w /opt/maven \
       maven:3.2-jdk-7 \
       mvn clean install

Примітки:

  • Акуратне в цьому підході полягає в тому, що все програмне забезпечення встановлюється і працює в контейнері. Докер потрібен лише на хост-машині.
  • Дивіться Dockerfile для цієї версії

Приклад 2 - Використовуйте Nexus для кешування файлів

Запустіть контейнер 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 з посиланням на контейнер nexus, щоб залежності були кешовані

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

Примітки:

  • Перевагою запуску Nexus у фоновому режимі є те, що іншими сторонніми сховищами можна керувати через URL-адресу адміністратора прозоро для збірок Maven, що працюють у локальних контейнерах.

чи це може бути використано для заміни центральної системи maven для побудови gradle? як зазначено в support.sonatype.com/entries/... Я замінив mavenCentral()у своїх залежностях gradle на maven {url "http://nexus:8081..."і зараз просто отримую проблеми з вирішенням.
mohamnag

@mohamnag Правильно, наведений вище файл "налаштувань" Maven робить саме це, перенаправляючи всі запити Maven Central до локального сховища nexus. Вам потрібно окреслити, з якими проблемами вирішення проблеми ви стикаєтесь. Може бути що завгодно ... Наприклад, ви налаштували посилання Docker, щоб хост "nexus" був належним чином вирішений?
Марк О'Коннор,

З’ясувалося, що все було нормально, але зв’язку або потрібен був час для створення індексу, або щось інше спричинило проблеми. Я також намагався запустити оновлення індексу, тому я не впевнений, який випадок вирішив проблему. Однак дякую.
mohamnag

Nexus 3 тепер також доступний як докер-контейнер. Використовуйте "sonatype / nexus3"
Андерсен

1
@avandeursen Ви праві, що параметр --link застарілий (відповідь старше 3 років). Однак більш правильним рішенням (на мій погляд) є створення докер-мережі та запуск обох контейнерів на ній. Таким чином ви можете скористатися власною функцією DNS у Docker і продовжувати посилатися на контейнер nexus за назвою. Оновимо приклад пізніше
Марк О'Коннор,

47

Шляхів може бути багато .. Але я реалізував, дотримуючись двох способів

Наведений приклад - проект maven.

1. Використання Dockerfile у проекті maven

Використовуйте таку структуру файлів:

Demo
└── src
|    ├── main
|    │   ├── java
|    │       └── org
|    │           └── demo
|    │               └── Application.java
||    └── test
|
├──── Dockerfile
├──── pom.xml

І оновіть файл Docker як:

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 with 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 a так, після цього ми запускаємо команду docker для створення образу з конкретним ім'ям, а потім запускаємо це зображення.

Тоді як у другому прикладі ми використовуємо плагін maven, в якому ми надаємо, baseImageі imageNameтому нам не потрібно створювати Dockerfile тут .. після упаковки проекту maven ми отримаємо образ docker, і нам просто потрібно запустити це зображення ..


Замість того, щоб змінювати точку входу, щоб вказати назву артефакту, ви можете скористатися підходом, як тут: alooma.com/blog/building-dockers - використовуйте плагін maven-dependency-для використання загального імені. Не потрібно поміщати версійну банку в контейнер докера, оскільки сам контейнер є версійним.
kboom

14

Як правило, вам слід створити жирний 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 .

Щось, про що слід пам’ятати :

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

Побудова жирної банки усуває цю проблему.


Привіт, я скористався вашим прикладом у своєму файлі докера, щоб скопіювати жирову банку на зображенні, але збірка не вдалася, оскільки не вдалося знайти жирову банку за місцевим шляхом. Це щось на зразок target / app.jar?
user_mda

Привіт, чи є спосіб завантажити артефакт із nexus під час виконання? Вказати артефакт, який потрібно завантажити, використовуючи властивості, а не фактичне посилання на саму банку? У файлі docker: RUN wget -O {project.build.finalname}.jar Але я хочу завантажити вищезгадану банку з nexus.
Прамод Сетлур,

1
Включення FAT JAR до коду репо є для мене повільним. Чи є спосіб використовувати maven для побудови образу?
WoLfPwNeR

1
Банки з жиром теж мають деякі проблеми, особливо з підписаними банками.
Thorbjørn Ravn Andersen

1
Як правило, ніколи не використовуйте жирову банку, пакети, які покладаються на маніфест або інші файли даних у своїх банках, швидше за все, вийдуть з ладу, оскільки немає безпечного способу об’єднати ці дані. Файли маніфесту з відповідними шляхами всередині конфлікту jar та перемоги першої чи останньої (не пам’ятаю, яких). Єдиний раз, коли вам слід подумати про використання жирної банки, це якщо у вас дуже обмежений набір залежностей, і ви добре знаєте, що їх банки не мають суперечливих маніфестних даних. В іншому випадку залишайте банки безпечними, оскільки вони були розроблені для використання (тобто окремо).
PiersyP

3

Ось мій внесок.
Я не буду намагатись перерахувати всі інструменти / бібліотеки / плагіни, які існують для того, щоб скористатися перевагами Docker with Maven. Деякі відповіді це вже зробили.
замість цього я зупинюсь на типології додатків та способі Dockerfile.
Dockerfileнасправді є простою та важливою концепцією Docker (усі відомі / загальнодоступні зображення покладаються на це), і я думаю, що спроба уникнути розуміння та використання Dockerfiles - не обов'язково кращий спосіб увійти у світ 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), коли ми створюємо та запускаємо контейнер з цих зображень.
Для нових програм або додатків, не надто складних для міграції, такий спосіб слід віддавати перевагу автономним серверам, оскільки це стандартний спосіб і найбільш ефективний спосіб використання контейнерів.
Я детально розкажу цей підхід.

Докеризація програми maven

1) Без весняного завантаження

Ідея полягає в тому, щоб створити жирову банку за допомогою Maven (допоміжний модуль maven та плагін maven для цього), який містить як складені класи програми, так і необхідні залежності maven.
Тоді ми можемо виділити два випадки:

  • якщо програма є настільною або автономною програмою (яку не потрібно розгортати на сервері): ми можемо вказати, як CMD/ENTRYPOINTпід час Dockerfileвиконання програми Java:java -cp .:/fooPath/* -jar myJar

  • якщо додаток є серверним додатком, наприклад Tomcat, ідея та ж: отримати жирний банку програми та запустити JVM в CMD/ENTRYPOINT. Але тут є важлива відмінність: нам потрібно включити деяку логіку та конкретні бібліотеки ( org.apache.tomcat.embedбібліотеки та деякі інші), яка запускає вбудований сервер при запуску основної програми.
    У нас є вичерпний посібник, але це має перевагу: ви маєте високий рівень свободи, оскільки використовуєте безпосередньо вбудований API Tomcat. на веб-сайті heroku .
    Для першого випадку (автономне додаток) це прямий та ефективний спосіб використання Docker.
    У другому випадку (серверний додаток), який працює, але це не прямо, може бути схильним до помилок і не дуже розширюваною моделлю, оскільки ви не розміщуєте свою програму в рамках зрілого фреймворку, такого як Spring Boot, який робить багато цих речей для вас, а також забезпечує високий рівень розширення.

2) З весняним завантаженням

Нарешті, ось і ми.
Це і просто, і ефективно, і дуже добре задокументовано.
Існує кілька підходів до створення програми Maven / Spring Boot для запуску на Docker.
Викриття всіх їх було б довгим і, можливо, нудним.
Найкращий вибір залежить від ваших потреб.
Але як би там не було, стратегія побудови з точки зору докер-шарів виглядає однаково.
Ми хочемо використовувати багатоступеневу збірку: одна, яка покладається на Maven для вирішення залежностей та для збірки, а інша - на JDK або JRE для запуску програми.

Етап збірки (зображення Maven):

  • пом копію на зображення
  • залежності та завантаження плагінів -.
    Про те, mvn dependency:resolve-pluginsприкутий mvn dependency:resolveможе виконувати роботу, але не завжди.
    Чому? Оскільки ці плагіни та packageвиконання пакувальника жирової банки можуть покладатися на різні артефакти / плагіни і навіть для одного артефакту / плагіна, вони все одно можуть використовувати іншу версію. Отже, більш безпечним підходом, хоча й потенційно повільнішим, є розв’язання залежностей, виконуючи саме mvnкоманду, яка використовується для упаковки програми (яка витягуватиме саме ті залежності, які вам потрібні), але пропускаючи компіляцію джерела та видаляючи цільову папку, щоб пришвидшити обробку та запобігти виявленню небажаних змін шару для цього кроку.
  • копія вихідного коду на зображення
  • упакуйте програму

Етап запуску (зображення JDK або JRE):

  • скопіюйте банку з попереднього етапу

Ось два приклади.

а) Простий спосіб без кешу для завантажених залежностей maven

Файл Docker:

########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 docker).
Щоб увімкнути buildkit, потрібно встановити змінну en DOCKER_BUILDKIT = 1 (ви можете зробити це там, де хочете: .bashrc, командний рядок, файл json демона docker ...).

Файл Docker:

# 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.