[java] 메이븐 프로젝트를 Dockerize하는 방법? 이를 달성하는 방법은 몇 가지입니까?
저는 Docker를 처음 사용하고 많은 문서를 읽고 많은 방법을 시도했지만 maven으로 Java 프로젝트를 실행하는 방법을 모릅니다.
- 사용하여 이미지를 빌드해야합니까
Dockerfile
? - 호스트에서 maven 프로젝트를 실행할 때 명령은 무엇입니까
Dockerfile
?
답변
작업 예.
이것은 스프링 부트 튜토리얼이 아닙니다. Docker 컨테이너 내에서 Maven 빌드를 실행하는 방법에 대한 질문에 대한 업데이트 된 답변입니다.
질문은 원래 4 년 전에 게시되었습니다.
1. 애플리케이션 생성
스프링 이니셜 라이저를 사용하여 데모 앱 생성
zip 아카이브를 로컬로 추출
2. 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"]
노트
- 이 예에서는 다단계 빌드 를 사용합니다 . 첫 번째 단계는 코드를 작성하는 데 사용됩니다. 두 번째 단계에는 빌드 된 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)
기타
로컬 저장소를 사용하여 jar를 캐시하도록 Maven 빌드를 최적화하는 방법에 대한 Docker 허브 문서를 읽어보세요.
업데이트 (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의 distroless 기본 이미지를 사용하고 있습니다.
옵션 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>
이제 nexus 컨테이너에 연결하는 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
메모:
- 백그라운드에서 Nexus를 실행할 때의 장점은 다른 타사 리포지토리가 로컬 컨테이너에서 실행되는 Maven 빌드에 대해 관리 URL을 통해 투명하게 관리 될 수 있다는 것입니다.
답변
여러 가지 방법이있을 수 있습니다.하지만 두 가지 방법으로 구현했습니다.
주어진 예는 maven 프로젝트입니다.
1. Maven 프로젝트에서 Dockerfile 사용
다음 파일 구조를 사용하십시오.
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
Docker 로 Spring Boot 에서 비디오 가져 오기
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 명령을 실행하여 특정 이름으로 이미지를 빌드 한 다음 해당 이미지를 실행합니다.
두 번째 예에서 우리는 우리가 제공하는 받는다는 플러그인에서 사용하는 반면 baseImage
그리고 imageName
우리가 여기 Dockerfile를 만들 필요가 없습니다 .. 메이븐 프로젝트를 패키징 한 후 우리는 고정 표시기 이미지를 얻을 것이다 우리는 단지 그 이미지를 실행해야합니다 ..
답변
경험상 Maven (코드와 모든 종속성을 모두 포함하는 JAR)을 사용하여 팻 JAR 을 빌드해야합니다 .
그런 다음 요구 사항과 일치 하는 Dockerfile 을 작성할 수 있습니다 (팻 JAR을 빌드 할 수있는 경우 CentOS 및 JVM과 같은 기본 OS 만 필요함).
이것이 제가 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
이렇게하면 Java7을 사용하여 CentOS 기반 이미지가 생성됩니다. 시작되면 앱 jar를 실행합니다.
배포하는 가장 좋은 방법은 Docker 레지스트리를 사용하는 것이며 Docker 이미지 용 Github와 같습니다.
다음과 같은 이미지를 만들 수 있습니다.
# current dir must contain the Dockerfile
docker build -t username/projectname:tagname .
그런 다음 다음과 같은 방법으로 이미지를 푸시 할 수 있습니다.
docker push username/projectname # this pushes all tags
이미지가 Docker 레지스트리에 있으면 전 세계 어디에서나 가져와 실행할 수 있습니다.
자세한 내용은 Docker 사용 설명서 를 참조하십시오.
명심해야 할 사항 :
이미지 내에서 저장소를 가져와 컨테이너 실행의 일부로 jar를 빌드 할 수도 있지만 코드가 변경 될 수 있고 예고없이 다른 버전의 앱을 사용하게 될 수 있으므로 좋은 접근 방식은 아닙니다.
뚱뚱한 항아리를 만들면이 문제가 제거됩니다.
답변
여기 제 공헌이 있습니다.
Maven과 함께 Docker를 활용하기 위해 존재하는 모든 도구 / 라이브러리 / 플러그인을 나열하려고하지 않습니다. 일부 답변은 이미 완료되었습니다.
대신 애플리케이션 유형과 Dockerfile 방식에 중점을 둘 것입니다.
Dockerfile
Docker의 간단하고 중요한 개념입니다 (모든 알려진 / 공개 이미지는 그것에 의존합니다). Dockerfile
s를 이해하고 사용하는 것을 피하려고 노력하는 것이 반드시 Docker 세계에 들어가는 더 좋은 방법은 아니라고 생각합니다.
애플리케이션 Dockerizing은 애플리케이션 자체와 도달하려는 목표에 따라 다릅니다.
1) 설치된 / 독립형 Java 서버 (Tomcat, JBoss 등)에서 계속 실행하려는 애플리케이션의 경우
길이 더 어려워지고 (서버를 관리 / 유지해야 함) 복잡성이 추가되고 빌드 / 배포 / 배포 해제 측면에서 임베디드 서버보다 확장 성이 떨어지고 빠르기 때문에 이상적인 목표가 아닙니다 .
그러나 레거시 애플리케이션의 경우 첫 번째 단계로 간주 될 수 있습니다.
일반적으로 여기서 아이디어는 서버용 Docker 이미지를 정의하고 배포 할 애플리케이션별로 이미지를 정의하는 것입니다.
애플리케이션의 도커 이미지는 예상되는 WAR / EAR를 생성하지만 컨테이너로 실행되지 않으며 서버 애플리케이션의 이미지는 이러한 이미지에서 생성 된 구성 요소를 배포 된 애플리케이션으로 배포합니다.
레거시 항목이 많고 풀 스프링 부트 임베디드 솔루션으로 마이그레이션하기가 너무 어려운 거대한 애플리케이션 (수백만 줄의 코드)의 경우 정말 좋은 개선입니다.
Docker의 사소한 사용 사례를위한 것이기 때문에 그 접근 방식을 자세히 설명하지는 않겠지 만, 이러한 복잡한 사례에 직면 한 개발자에게 일부 문이 열려 있다는 것을 아는 것이 좋습니다. Docker를 통합하십시오.
2) 서버 자체를 내장 / 부트 스트랩하는 애플리케이션의 경우 (서버가 내장 된 스프링 부트 : Tomcat, Netty, Jetty …)
이것이 Docker의 이상적인 대상입니다 . 저는 Spring Boot를 지정했습니다. 이것이 그렇게하기에 정말 좋은 프레임 워크이고 매우 높은 수준의 유지 보수성을 가지고 있기 때문입니다. 그러나 이론적으로는이를 달성하기 위해 다른 Java 방식을 사용할 수 있습니다.
일반적으로 여기서 아이디어는 배포 할 애플리케이션별로 Docker 이미지를 정의하는 것입니다.
애플리케이션의 도커 이미지는 JAR 또는 JAR / 클래스 / 구성 파일 세트를 생성하며 이러한 이미지에서 컨테이너를 생성하고 시작할 때 애플리케이션 (java 명령)과 함께 JVM을 시작합니다.
마이그레이션하기에 너무 복잡하지 않은 새로운 애플리케이션이나 애플리케이션의 경우 컨테이너를 사용하는 가장 효율적인 방법이자 표준 방식이기 때문에 독립형 서버보다이 방식을 선호해야합니다.
그 접근 방식을 자세히 설명하겠습니다.
Maven 애플리케이션 Dockerizing
1) 스프링 부트 없음
아이디어는 응용 프로그램의 컴파일 된 클래스와 필요한 maven 종속성을 모두 포함하는 Maven (Maven 어셈블리 플러그인 및이를위한 Maven 쉐이드 플러그인 도움말)을 사용하여 fat jar를 만드는 것입니다.
그런 다음 두 가지 경우를 확인할 수 있습니다.
-
애플리케이션이 데스크톱 또는 자율 애플리케이션 인 경우 (서버에 배포 할 필요가 없음) : 애플리케이션의 Java 실행
CMD/ENTRYPOINT
에서와 같이 지정할 수 있습니다Dockerfile
.java -cp .:/fooPath/* -jar myJar
-
응용 프로그램이 서버 응용 프로그램 (예 : Tomcat) 인 경우 아이디어는 동일합니다. 응용 프로그램의 큰 항아리를 가져오고
CMD/ENTRYPOINT
. 그러나 여기서 중요한 차이점이 있습니다.org.apache.tomcat.embed
메인 애플리케이션이 시작될 때 임베디드 서버를 시작하는 로직과 특정 라이브러리 ( 라이브러리 및 기타)를 포함해야합니다. heroku 웹 사이트에
종합 가이드 가 있습니다 .
첫 번째 경우 (자율적 애플리케이션)의 경우 Docker를 사용하는 간단하고 효율적인 방법입니다.
두 번째 경우 (서버 애플리케이션)의 경우 작동하지만 직선적이지 않고 오류가 발생하기 쉬우 며 많은 작업을 수행하는 Spring Boot와 같은 성숙한 프레임 워크의 프레임에 애플리케이션을 배치하지 않기 때문에 확장 가능한 모델이 아닙니다. 이러한 것들이 당신을 위해 그리고 또한 높은 수준의 확장을 제공합니다.
하지만 이점이 있습니다. 임베디드 Tomcat API를 직접 사용하기 때문에 높은 수준의 자유가 있습니다.
2) 스프링 부트 포함
드디어 시작합니다.
그것은 간단하고 효율적이며 매우 잘 문서화되어 있습니다.
Docker에서 실행할 Maven / Spring Boot 애플리케이션을 만드는 방법에는 여러 가지가 있습니다.
그것들을 모두 노출시키는 것은 길고 지루할 것입니다.
최선의 선택은 귀하의 요구 사항에 따라 다릅니다.
그러나 어쨌든 도커 레이어 측면에서 빌드 전략은 동일하게 보입니다.
다중 단계 빌드를 사용하려고합니다. 하나는 종속성 해결 및 빌드를 위해 Maven에 의존하고 다른 하나는 애플리케이션을 시작하기 위해 JDK 또는 JRE에 의존합니다.
빌드 단계 (Maven 이미지) :
- 이미지에 pom 복사
- 종속성 및 플러그인 다운로드.
그것에 대해,mvn dependency:resolve-plugins
chained tomvn dependency:resolve
는 일 을 할 수 있지만 항상 그런 것은 아닙니다.
왜 ? 이러한 플러그인과package
fat jar를 패키징하기 위한 실행은 서로 다른 아티팩트 / 플러그인에 의존 할 수 있으며 동일한 아티팩트 / 플러그인에 대해서도 여전히 다른 버전을 가져올 수 있습니다. 따라서 잠재적으로 더 느린 방법mvn
은 응용 프로그램을 패키지하는 데 사용되는 명령 (필요한 종속성을 정확하게 가져옴)을 실행하여 종속성을 해결 하지만 소스 컴파일을 건너 뛰고 대상 폴더를 삭제하여 처리 속도를 높이고 해당 단계에서 원하지 않는 레이어 변경 감지를 방지합니다. - 이미지에 소스 코드 복사
- 애플리케이션 패키징
실행 단계 (JDK 또는 JRE 이미지) :
- 이전 단계에서 항아리를 복사
여기에 두 가지 예가 있습니다.
a) 다운로드 된 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 종속성을 다운로드하고 저장하는 전체 계층을 다시 생성한다는 것을 의미합니다. 일반적으로 이미지 빌드 중에 Maven 저장소 관리자를 사용하지 않는 경우 일반적으로 많은 종속성이있는 애플리케이션에는 허용되지 않습니다 (Spring Boot는 많은 종속성을 가져옴).
b) 다운로드 된 maven 종속성에 대한 캐시를 사용하는보다 효율적인 방법
접근 방식은 동일하지만 도커 빌더 캐시에 캐시 된 메이븐 종속성 다운로드입니다.
캐시 작업은 빌드 킷 (도커의 실험적 API)에 의존합니다.
빌드 킷을 활성화하려면 env 변수 DOCKER_BUILDKIT = 1을 설정해야합니다 (원하는 곳에서 .bashrc, 명령 줄, docker daemon 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