Przykład roboczy.
To nie jest samouczek dotyczący uruchamiania na wiosnę. To zaktualizowana odpowiedź na pytanie, jak uruchomić kompilację Mavena w kontenerze Dockera.
Pytanie pierwotnie wysłane 4 lata temu.
1. Wygeneruj aplikację
Użyj inicjatora wiosny, aby wygenerować aplikację demonstracyjną
https://start.spring.io/
Rozpakuj archiwum zip lokalnie
2. Utwórz plik Dockerfile
#
# 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"]
Uwaga
- W tym przykładzie zastosowano kompilację wieloetapową . Pierwszy etap służy do budowania kodu. Drugi etap zawiera tylko zbudowany plik jar i środowisko JRE do jego uruchomienia (zwróć uwagę, jak plik jar jest kopiowany między etapami).
3. Zbuduj obraz
docker build -t demo .
4. Uruchom obraz
$ 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)
Różne
Przeczytaj dokumentację centrum Docker, w jaki sposób można zoptymalizować kompilację Maven, aby używać lokalnego repozytorium do buforowania plików JAR.
Aktualizacja (2019-02-07)
To pytanie ma już 4 lata i można śmiało powiedzieć, że budowanie aplikacji za pomocą Dockera przeszło znaczną zmianę.
Opcja 1: Kompilacja wieloetapowa
Ten nowy styl umożliwia tworzenie lżejszych obrazów, które nie obejmują narzędzi do kompilacji ani kodu źródłowego.
Poniższy przykład ponownie wykorzystuje oficjalny obraz podstawowy Mavena do uruchomienia pierwszego etapu kompilacji przy użyciu żądanej wersji Mavena. Druga część pliku określa, w jaki sposób zbudowany plik jar jest montowany w ostatecznym obrazie wyjściowym.
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"]
Uwaga:
- Używam bezproblemowego obrazu podstawowego Google , który stara się zapewnić wystarczający czas działania dla aplikacji Java.
Opcja 2: wysięgnik
Nie korzystałem z tego podejścia, ale wydaje mi się, że warto go zbadać, ponieważ umożliwia tworzenie obrazów bez konieczności tworzenia nieprzyjemnych rzeczy, takich jak Dockerfiles :-)
https://github.com/GoogleContainerTools/jib
Projekt ma wtyczkę Maven, która integruje pakowanie twojego kodu bezpośrednio z przepływem pracy Maven.
Oryginalna odpowiedź (uwzględniona w celu zapewnienia kompletności, ale napisana przed wiekami)
Spróbuj użyć nowych oficjalnych obrazów, jest jeden dla Mavena
https://registry.hub.docker.com/_/maven/
Obraz może służyć do uruchamiania Mavena w czasie kompilacji w celu utworzenia skompilowanej aplikacji lub, jak w poniższych przykładach, do uruchomienia kompilacji Maven w kontenerze.
Przykład 1 - Maven działający w kontenerze
Następujące polecenie uruchamia kompilację Mavena wewnątrz kontenera:
docker run -it --rm \
-v "$(pwd)":/opt/maven \
-w /opt/maven \
maven:3.2-jdk-7 \
mvn clean install
Uwagi:
- Zaletą tego podejścia jest to, że całe oprogramowanie jest instalowane i uruchamiane w kontenerze. Potrzebujesz tylko dockera na komputerze hosta.
- Zobacz Dockerfile dla tej wersji
Przykład 2 - Użyj Nexusa do buforowania plików
Uruchom kontener Nexus
docker run -d -p 8081:8081 --name nexus sonatype/nexus
Utwórz plik „settings.xml”:
<settings>
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://nexus:8081/content/groups/public/</url>
</mirror>
</mirrors>
</settings>
Teraz uruchom Maven łączący się z kontenerem Nexusa, aby zależności zostały zapisane w pamięci podręcznej
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
Uwagi:
- Zaletą uruchamiania Nexusa w tle jest to, że repozytoriami innych firm można zarządzać przez adres URL administratora w sposób przejrzysty dla kompilacji Mavena działających w lokalnych kontenerach.
mavenCentral()
w moich zależnościach gradlemaven {url "http://nexus:8081..."
i teraz pojawiają się problemy z rozwiązaniem.Sposobów może być wiele. Ale wdrożyłem je na dwa sposoby
Podany przykład dotyczy projektu maven.
1. Korzystanie z Dockerfile w projekcie maven
Użyj następującej struktury plików:
Demo └── src | ├── main | │ ├── java | │ └── org | │ └── demo | │ └── Application.java | │ | └── test | ├──── Dockerfile ├──── pom.xml
I zaktualizuj plik Dockerfile jako:
FROM java:8 EXPOSE 8080 ADD /target/demo.jar demo.jar ENTRYPOINT ["java","-jar","demo.jar"]
Przejdź do folderu projektu i wpisz następujące polecenie, dzięki któremu będziesz mógł utworzyć obraz i uruchomić ten obraz:
$ mvn clean $ mvn install $ docker build -f Dockerfile -t springdemo . $ docker run -p 8080:8080 -t springdemo
Pobierz wideo w Spring Boot za pomocą Dockera
2. Korzystanie z wtyczek Maven
Dodaj daną wtyczkę 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>
Przejdź do folderu projektu i wpisz następujące polecenie, aby utworzyć obraz i uruchomić ten obraz:
$ mvn clean package docker:build $ docker images $ docker run -p 8080:8080 -t <image name>
W pierwszym przykładzie tworzymy plik Dockerfile i udostępniamy obraz bazowy i dodajemy jar i so, po wykonaniu tej czynności uruchomimy polecenie docker, aby zbudować obraz o określonej nazwie, a następnie uruchomimy ten obraz.
Natomiast w drugim przykładzie używamy Maven plugin w którym dostarczanie
baseImage
aimageName
więc nie ma potrzeby tworzenia Dockerfile tutaj .. po zapakowaniu Maven projekt otrzymamy obraz Döcker i po prostu trzeba uruchomić ten obraz ..źródło
Z reguły powinieneś zbudować gruby plik JAR przy użyciu Maven (JAR, który zawiera zarówno twój kod, jak i wszystkie zależności).
Następnie możesz napisać plik Dockerfile, który odpowiada Twoim wymaganiom (jeśli możesz zbudować gruby JAR, potrzebujesz tylko podstawowego systemu operacyjnego, takiego jak CentOS i JVM).
Właśnie tego używam w przypadku aplikacji Scala (opartej na Javie).
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
Tworzy to obraz oparty na CentOS z Java7. Po uruchomieniu wykona plik jar aplikacji.
Najlepszym sposobem na wdrożenie jest za pośrednictwem rejestru Docker, jest to jak Github dla obrazów Dockera.
Możesz zbudować taki obraz:
# current dir must contain the Dockerfile docker build -t username/projectname:tagname .
Następnie możesz przesłać obraz w ten sposób:
docker push username/projectname # this pushes all tags
Gdy obraz znajdzie się w rejestrze Docker, możesz go pobrać z dowolnego miejsca na świecie i uruchomić.
Aby uzyskać więcej informacji, zobacz Podręcznik użytkownika platformy Docker .
Coś, o czym należy pamiętać :
Możesz również wciągnąć repozytorium do obrazu i zbudować jar jako część wykonania kontenera, ale nie jest to dobre podejście, ponieważ kod może się zmienić i możesz skończyć z użyciem innej wersji aplikacji bez powiadomienia.
Zbudowanie słoika na tłuszcz rozwiązuje ten problem.
źródło
RUN wget -O {project.build.finalname}.jar
ale chcę pobrać powyższy plik jar z nexusa.Oto mój wkład.
Nie będę próbował wymieniać wszystkich narzędzi / bibliotek / wtyczek, które istnieją, aby skorzystać z Dockera z Mavenem. Niektóre odpowiedzi już to zrobiły.
zamiast tego skupię się na typologii aplikacji i sposobie Dockerfile.
Dockerfile
jest naprawdę prostą i ważną koncepcją Dockera (wszystkie znane / publiczne obrazy opierają się na tym) i myślę, że próba uniknięcia zrozumienia i używaniaDockerfile
s niekoniecznie jest lepszym sposobem na wejście w świat Dockera.Dokeryzowanie aplikacji zależy od samej aplikacji i celu do osiągnięcia
1) W przypadku aplikacji, które chcemy dalej uruchamiać na zainstalowanym / samodzielnym serwerze Java (Tomcat, JBoss itp.)
Droga jest trudniejsza i nie jest to idealny cel, ponieważ zwiększa złożoność (musimy zarządzać / utrzymywać serwer) i jest mniej skalowalny i mniej szybki niż serwery wbudowane pod względem budowania / wdrażania / niewydrażania.
Ale w przypadku starszych aplikacji można to uznać za pierwszy krok.
Generalnie chodzi o zdefiniowanie obrazu Docker dla serwera i zdefiniowanie obrazu dla aplikacji do wdrożenia.
Obrazy dockera dla aplikacji generują oczekiwane WAR / EAR, ale nie są one wykonywane jako kontener, a obraz aplikacji serwerowej wdraża komponenty utworzone przez te obrazy jako wdrożone aplikacje.
W przypadku ogromnych aplikacji (miliony linii kodów) z wieloma starszymi elementami i tak trudnych do migracji do pełnego rozwiązania wbudowanego w rozruchu sprężynowym, jest to naprawdę niezła poprawa.
Nie będę szczegółowo opisywał tego podejścia, ponieważ dotyczy to drobnych przypadków użycia Dockera, ale chciałem ujawnić ogólną ideę tego podejścia, ponieważ myślę, że dla programistów zajmujących się tymi złożonymi przypadkami dobrze jest wiedzieć, że niektóre drzwi są otwarte dla zintegrować Docker.
2) W przypadku aplikacji, które same osadzają / uruchamiają serwer (Spring Boot z wbudowanym serwerem: Tomcat, Netty, Jetty ...)
To idealny cel w przypadku Dockera . Wybrałem Spring Boot, ponieważ jest to naprawdę fajny framework do tego, który ma również bardzo wysoki poziom utrzymania, ale teoretycznie moglibyśmy użyć dowolnego innego sposobu Java, aby to osiągnąć.
Generalnie chodzi o to, aby zdefiniować obraz platformy Docker na aplikację do wdrożenia.
Obrazy dockera dla aplikacji tworzą plik JAR lub zestaw plików JAR / classes / configuration, a te uruchamiają maszynę JVM z aplikacją (polecenie java), gdy tworzymy i uruchamiamy kontener z tych obrazów.
W przypadku nowych aplikacji lub aplikacji, które nie są zbyt skomplikowane do migracji, ten sposób musi być preferowany w stosunku do samodzielnych serwerów, ponieważ jest to standardowy i najbardziej efektywny sposób korzystania z kontenerów.
Opiszę szczegółowo to podejście.
Dokeryzowanie aplikacji maven
1) Bez butów sprężynowych
Chodzi o to, aby utworzyć fat jar z Mavenem (wtyczką asemblera maven i pomoc dla wtyczki maven shadow), która zawiera zarówno skompilowane klasy aplikacji, jak i potrzebne zależności maven.
Następnie możemy zidentyfikować dwa przypadki:
jeśli aplikacja jest aplikacją desktopową lub autonomiczną (która nie musi być wdrażana na serwerze): możemy określić, jak
CMD/ENTRYPOINT
w przypadkuDockerfile
wykonania aplikacji w języku Java:java -cp .:/fooPath/* -jar myJar
jeśli aplikacja jest aplikacją serwerową, na przykład Tomcat, idea jest taka sama: pobrać gruby plik aplikacji i uruchomić JVM w
CMD/ENTRYPOINT
. Ale tutaj z ważną różnicą: musimy uwzględnić pewną logikę i określone biblioteki (org.apache.tomcat.embed
biblioteki i kilka innych), które uruchamiają wbudowany serwer po uruchomieniu głównej aplikacji.Mamy obszerny przewodnik po stronie heroku .
W pierwszym przypadku (aplikacja autonomiczna) jest to prosty i skuteczny sposób korzystania z Dockera.
W drugim przypadku (aplikacja serwerowa), który działa, ale nie jest prosty, może być podatny na błędy i nie jest modelem bardzo rozszerzalnym, ponieważ nie umieszczasz aplikacji w ramie dojrzałej struktury, takiej jak Spring Boot, która wykonuje wiele tych rzeczy dla Ciebie, a także zapewnia wysoki poziom rozszerzenia.
Ma to jednak zaletę: masz wysoki poziom swobody, ponieważ używasz bezpośrednio wbudowanego interfejsu Tomcat API.
2) Z butem sprężynowym
Wreszcie zaczynamy.
Jest to proste, wydajne i bardzo dobrze udokumentowane.
Istnieje naprawdę kilka podejść, aby aplikacja Maven / Spring Boot działała na platformie Docker.
Odsłanianie ich wszystkich byłoby długie i może nudne.
Najlepszy wybór zależy od Twoich wymagań.
Ale niezależnie od sposobu, strategia kompilacji pod względem warstw dockera wygląda tak samo.
Chcemy użyć kompilacji wieloetapowej: jednej polegającej na Maven do rozwiązywania zależności i do kompilacji, a drugiej polegającej na JDK lub JRE do uruchamiania aplikacji.
Scena budowy (obraz Mavena):
O tym,
mvn dependency:resolve-plugins
przykuty domvn dependency:resolve
może wykonać swoją pracę, ale nie zawsze.Czemu ? Ponieważ te wtyczki i
package
wykonanie do pakowania słoika tłuszczu mogą opierać się na różnych artefaktach / wtyczkach, a nawet na tym samym artefakcie / wtyczce, mogą one nadal pobierać inną wersję. Tak więc bezpieczniejszym podejściem, chociaż potencjalnie wolniejszym, jest rozwiązywanie zależności przez wykonanie dokładniemvn
polecenia używanego do spakowania aplikacji (które ściągnie dokładnie zależności, których potrzebujesz), ale pomijając kompilację źródłową i usuwając folder docelowy, aby przyspieszyć przetwarzanie i zapobiec wykryciu niepożądanej zmiany warstwy na tym etapie.Uruchom etap (obraz JDK lub JRE):
Oto dwa przykłady.
a) Prosty sposób bez pamięci podręcznej dla pobranych zależności 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
Wada tego rozwiązania? Wszelkie zmiany w pom.xml oznaczają ponowne utworzenie całej warstwy, która pobiera i przechowuje zależności maven. Jest to generalnie niedopuszczalne w przypadku aplikacji z wieloma zależnościami (a Spring Boot ściąga wiele zależności), ogólnie rzecz biorąc, jeśli nie używasz menedżera repozytorium maven podczas budowania obrazu.
b) Bardziej efektywny sposób z pamięcią podręczną dla pobranych zależności maven
Podejście jest tutaj takie samo, ale pobieranie zależności maven, które są buforowane w pamięci podręcznej narzędzia docker builder.
Operacja pamięci podręcznej opiera się na buildkicie (eksperymentalnym interfejsie API dockera).
Aby włączyć buildkit, należy ustawić zmienną env DOCKER_BUILDKIT = 1 (możesz to zrobić gdzie chcesz: .bashrc, wiersz poleceń, plik json demona Dockera ...).
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
źródło