如何将maven项目停靠?以及实现它的方法有多少?

Yas*_*Lin 62 java maven docker

我是Docker的新手,并且不知道如何使用maven运行java项目,即使我已经阅读了很多文档并尝试了很多方法.

  1. 我应该使用构建图像Dockerfile吗?
  2. 什么是在主机上运行maven项目时的命令Dockerfile

Mar*_*nor 66

工作实例.

这不是一个春季启动教程.这是关于如何在Docker容器中运行Maven构建的问题的更新答案.

问题最初发布于4年前.

1.生成应用程序

使用spring初始化程序生成演示应用程序

https://start.spring.io/

在此输入图像描述

在本地解压缩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"]
Run Code Online (Sandbox Code Playgroud)

注意

  • 此示例使用多阶段构建.第一阶段用于构建代码.第二阶段只包含构建的jar和运行它的JRE(注意如何在各阶段之间复制jar).

3.构建图像

docker build -t demo .
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

杂项

阅读Docker hub文档,了解如何优化Maven构建以使用本地存储库缓存jar.

更新(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"]  
Run Code Online (Sandbox Code Playgroud)

注意:

  • 我正在使用Google的distroless基础映像,它努力为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
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 这种方法的巧妙之处在于所有软件都在容器内安装并运行.只需要主机上的docker.
  • 请参阅此版本的Dockerfile

示例2 - 使用Nexus缓存文件

运行Nexus容器

docker run -d -p 8081:8081 --name nexus sonatype/nexus
Run Code Online (Sandbox Code Playgroud)

创建"settings.xml"文件:

<settings>
  <mirrors>
    <mirror>
      <id>nexus</id>
      <mirrorOf>*</mirrorOf>
      <url>http://nexus:8081/content/groups/public/</url>
    </mirror>
  </mirrors>
</settings>
Run Code Online (Sandbox Code Playgroud)

现在运行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
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 在后台运行Nexus的一个优点是,其他第三方存储库可以通过管理URL透明地管理到在本地容器中运行的Maven构建.


Rid*_*hil 43

可能有很多方法..但我通过以下两种方式实施

给出的例子是maven项目.

1.在maven项目中使用Dockerfile

使用以下文件结构:

Demo
??? src
|    ??? main
|    ?   ??? java
|    ?       ??? org
|    ?           ??? demo
|    ?               ??? Application.java
|    ?   
|    ??? test
|
????? Dockerfile
????? pom.xml
Run Code Online (Sandbox Code Playgroud)

并将Dockerfile更新为:

FROM java:8
EXPOSE 8080
ADD /target/demo.jar demo.jar
ENTRYPOINT ["java","-jar","demo.jar"]
Run Code Online (Sandbox Code Playgroud)

导航到项目文件夹并键入以下命令,您将创建图像并运行该图像:

$ mvn clean
$ mvn install
$ docker build -f Dockerfile -t springdemo .
$ docker run -p 8080:8080 -t springdemo
Run Code Online (Sandbox Code Playgroud)

使用DockerSpring 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>
Run Code Online (Sandbox Code Playgroud)

导航到项目文件夹并键入以下命令,您将能够创建图像并运行该图像:

$ mvn clean package docker:build
$ docker images
$ docker run -p 8080:8080 -t <image name>
Run Code Online (Sandbox Code Playgroud)

在第一个例子中,我们创建了Dockerfile并提供了基本映像并添加了jar,所以在这之后我们将运行docker命令来构建具有特定名称的映像,然后运行该映像.

在第二个例子中,我们使用maven插件,我们提供baseImage,imageName所以我们不需要在这里创建Dockerfile ..在打包maven项目后,我们将获得docker图像,我们只需要运行该图像..


Mat*_*ini 12

根据经验,您应该使用Maven(一个包含代码和所有依赖项的JAR)构建一个胖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
Run Code Online (Sandbox Code Playgroud)

这将使用Java7创建基于CentOS的映像.启动时,它将执行您的app jar.

部署它的最佳方式是通过Docker Registry,它就像是Docker镜像的Github.

您可以构建这样的图像:

# current dir must contain the Dockerfile
docker build -t username/projectname:tagname .
Run Code Online (Sandbox Code Playgroud)

然后,您可以通过以下方式推送图像:

docker push username/projectname # this pushes all tags
Run Code Online (Sandbox Code Playgroud)

一旦图像在Docker Registry上,您就可以从世界上的任何地方拉出它并运行它.

有关更多信息,请参见Docker用户指南.

要记住的事情:

您还可以将您的存储库拉入图像并在容器执行过程中构建jar,但这不是一个好方法,因为代码可能会更改,您最终可能会使用不同版本的应用程序,恕不另行通知.

构建一个胖罐可以解决这个问题.


dav*_*xxx 7

这是我的贡献。
我不会尝试列出所有现有的工具/库/插件来利用 Docker 和 Maven。有些答案已经做到了。
相反,我将专注于应用程序类型和 Dockerfile 方式。
Dockerfile确实是 Docker 的一个简单而重要的概念(所有已知/公共镜像都依赖于它),我认为试图避免理解和使用Dockerfiles 不一定是进入 Docker 世界的更好方式。

Dockerizing 应用程序取决于应用程序本身和要达到的目标

1) 对于我们希望继续在已安装/独立的 Java 服务器(Tomcat、JBoss 等...)上运行它们的应用程序

这条路更艰难,这不是理想的目标,因为这增加了复杂性(我们必须管理/维护服务器),并且在构建/部署/取消部署方面,它的可扩展性和速度不如嵌入式服务器。
但对于遗留应用程序,这可能被视为第一步。
通常,这里的想法是定义一个包含应用程序服务器的 Docker 镜像。您可以使用自己的 Tomcat、JBoss 或 Weblogic 基础 Dockerfile 来设置和配置服务器。
关于在服务器上部署的应用程序,并没有真正的标准方法。
一种可能的方法是按应用程序或要部署在一起的应用程序集定义 Dockerfile。Dockerfile 会将之前创建的应用程序服务器 Dockerfile 作为基础映像。
例如,该应用程序 Dockerfile 的目标是从 Git 或 Maven 存储库管理器检索组件(JAR/WAR/EAR),并在应用服务器上正确部署/安装它/它们。
对于具有大量遗留内容的大型应用程序(数百万行代码),并且很难迁移到完整的 Spring Boot 嵌入式解决方案,这确实是一个很好的改进。
我不会详细说明这种方法,因为那是针对 Docker 的次要用例,但我想公开这种方法的总体思路,因为我认为对于面临这些复杂情况的开发人员来说,很高兴知道有些门是敞开的集成 Docker。

2)对于嵌入/引导服务器本身的应用程序(嵌入服务器的Spring Boot:Tomcat,Netty,Jetty ...)

这是 Docker 的理想目标。我指定 Spring Boot 是因为这是一个非常好的框架,并且具有非常高的可维护性,但理论上我们可以使用任何其他 Java 方式来实现这一点。
通常,这里的想法是为每个要部署的应用程序定义一个 Docker 映像。
应用程序的 docker 映像生成一个 JAR 或一组 JAR/类/配置文件,当我们从这些映像创建和启动容器时,这些文件会使用应用程序(java 命令)启动 JVM。
对于新的应用程序或不太复杂的应用程序迁移,这种方式必须优于独立服务器,因为这是使用容器的标准方式和最有效的方式。
我将详细介绍这种方法。

Docker 化一个 Maven 应用程序

1) 没有 Spring Boot

这个想法是用 Maven 创建一个胖 jar(maven 程序集插件和 maven shade 插件帮助),其中包含应用程序的编译类和所需的 maven 依赖项。
然后我们可以确定两种情况:

  • 如果应用程序是一个桌面或自主应用程序(即不需要被部署在服务器上):我们可以指定为CMD/ENTRYPOINTDockerfile应用程序的Java执行:java -cp .:/fooPath/* -jar myJar

  • 如果应用程序是服务器应用程序,例如 Tomcat,想法是相同的:获取应用程序的胖 jar 并在CMD/ENTRYPOINT. 但是这里有一个重要的区别:我们需要包含一些逻辑和特定的库(org.apache.tomcat.embed库和其他一些库),它们会在主应用程序启动时启动嵌入式服务器。
    我们在 heroku 网站上有一个综合指南。
    对于第一种情况(自治应用程序),这是使用 Docker 的一种直接而有效的方式。
    对于第二种情况(服务器应用程序),它可以工作但不是直接的,可能容易出错并且不是一个非常可扩展的模型,因为您没有将您的应用程序置于成熟框架(例如 Spring Boot)的框架中,该框架可以执行许多操作这些东西也为你提供了高水平的扩展。
    但这有一个优点:您拥有高度的自由度,因为您直接使用嵌入式 Tomcat API。

2) 使用 Spring Boot

最后,我们走了。
这既简单又高效,而且有据可查。
确实有几种方法可以使 Maven/Spring Boot 应用程序在 Docker 上运行。
暴露所有这些会很长,而且可能很无聊。
最佳选择取决于您的要求。
但无论如何,Docker 层的构建策略看起来是一样的。
我们想使用多阶段构建:一个依赖 Maven 进行依赖解析和构建,另一个依赖 JDK 或 JRE 来启动应用程序。

构建阶段(Maven 镜像):

  • pom 复制到图像
  • 依赖项插件下载。
    关于这一点,mvn dependency:resolve-plugins链接到mvn dependency:resolve可能会完成这项工作,但并非总是如此。
    为什么 ?因为这些插件和package打包 fat jar的执行可能依赖于不同的 artifacts/plugins,甚至对于相同的 artifact/plugin,它们仍然可能会拉不同的版本。因此,一种更安全但可能较慢的方法是通过准确mvn执行用于打包应用程序的命令(它将准确提取您需要的依赖项)来解决依赖项,但跳过源编译并删除目标文件夹以加快处理速度并防止该步骤的任何不受欢迎的层变化检测。
  • 源代码复制到图像
  • 打包应用程序

运行阶段(JDK 或 JRE 映像):

  • 复制上一阶段的 jar
  • 入口点/cmd:运行应用程序

这里有两个例子。

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
Run Code Online (Sandbox Code Playgroud)

该解决方案的缺点?pom.xml 中的任何更改都意味着重新创建下载和存储 maven 依赖项的整个层。对于具有许多依赖项(并且 Spring Boot 拉取许多依赖项)的应用程序,这通常是不可接受的,总体而言,如果您在映像构建期间不使用 Maven 存储库管理器。

b) 一种更有效的方式,为下载的 Maven 依赖项提供缓存

此处的方法相同,但 Maven 依赖项下载缓存在 docker builder 缓存中。
缓存操作依赖于buildkit(docker的实验性api)。
要启用 buildkit,必须设置环境变量 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 and download dependencies only when these are new (thanks to the cache)
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

                                                                                                                                                                        
                                                                       
                                                                       
Run Code Online (Sandbox Code Playgroud)