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żywania Dockerfile
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 przypadku Dockerfile
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):
- kopia pom do obrazu
- zależności i pobieranie wtyczek.
O tym, mvn dependency:resolve-plugins
przykuty do mvn 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ładnie mvn
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.
- kopia kodu źródłowego do obrazu
- spakuj aplikację
Uruchom etap (obraz JDK lub JRE):
- skopiuj słoik z poprzedniego etapu
Oto dwa przykłady.
a) Prosty sposób bez pamięci podręcznej dla pobranych zależności maven
Dockerfile:
FROM maven:3.6-jdk-11 as maven_build
WORKDIR /app
COPY pom.xml .
RUN mvn clean package -Dmaven.test.skip -Dmaven.main.skip -Dspring-boot.repackage.skip && rm -r target/
COPY src ./src
RUN mvn clean package -Dmaven.test.skip
RUN mkdir -p target/docker-packaging && cd target/docker-packaging && jar -xf ../my-app*.jar
FROM openjdk:11.0-jre
WORKDIR /app
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
CMD java -cp .:classes:lib
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:
FROM maven:3.6-jdk-11 as maven_build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN --mount=type=cache,target=/root/.m2 mvn clean package -Dmaven.test.skip
RUN mkdir -p target/docker-packaging && cd target/docker-packaging && jar -xf ../my-app*.jar
FROM openjdk:11.0-jre
WORKDIR /app
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
CMD java -cp .:classes:lib
mavenCentral()
w moich zależnościach gradlemaven {url "http://nexus:8081..."
i teraz pojawiają się problemy z rozwiązaniem.