如何停靠文件项目? 有多少方法来实现它?

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

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

根据经验,应该使用 Maven (包含代码和所有依赖项的 JAR)构建 肥坛子

然后,您可以编写一个与您的需求相匹配的 文件夹(如果您可以构建一个大型 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

这将使用 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 作为容器执行的一部分,但这不是一个好的方法,因为代码可能会发生变化,您最终可能会在没有通知的情况下使用应用程序的不同版本。

建立一个脂肪罐消除了这个问题。

举个例子。

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

这个问题最早是在4年前发布的。

1. 生成应用程序

使用 Spring 初始化器生成一个演示应用程序

Https://start.spring.io/

enter image description here

在本地提取压缩文件

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)

Misc

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

更新(2019-02-07)

这个问题现在已经有4年的历史了,在这段时间里,可以说使用 Docker 构建应用程序已经经历了重大的变化。

备选方案1: 多阶段构建

这种新样式使您能够创建更轻量级的图像,而不用封装构建工具和源代码。

这里的示例再次使用 官方专家基本映像,使用所需的 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: Jib

我还没有使用这种方法,但似乎值得研究,因为它使您能够构建映像,而不必创建像 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

备注:

  • 这种方法的优点是所有软件都在容器中安装和运行。只需要主机上的 docker。
  • 参见 这个版本的 Dockerfile

示例2-使用 Nexus 缓存文件

运行 Nexus 容器

docker run -d -p 8081:8081 --name nexus sonatype/nexus

创建一个“ setings.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 项目。

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

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

$ mvn clean
$ mvn install
$ docker build -f Dockerfile -t springdemo .
$ docker run -p 8080:8080 -t springdemo

带 Docker 的 Spring Boot获取视频

2. 使用 Maven 插件

pom.xml中添加给定的 maven 插件

<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 命令来构建一个具有特定名称的映像,然后运行该映像。.

而在第二个例子中,我们使用的是 maven 插件,其中提供了 baseImageimageName,所以我们不需要在这里创建 Dockerfile。.在包装后,maven 项目,我们将获得多克图像,我们只需要运行该图像。.

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

对应用程序进行定位取决于应用程序本身和要达到的目标

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

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

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

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

正在修改一个 Maven 应用程序

1)没有弹簧启动

这个想法是用 Maven 创建一个胖的 jar (Maven 汇编插件和 Maven 阴影插件对此有帮助) ,其中包含应用程序的编译类和所需的 Maven 依赖项。
这样我们就能确定两个案例:

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

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

2)使用弹簧启动

终于,我们开始了。
这既简单、高效,又有很好的文档记录。
在 Docker 上运行 Maven/Spring Boot 应用程序实际上有几种方法。
曝光所有这些事情会很漫长,也许会很无聊。
最好的选择取决于你的要求。
但是无论如何,构建策略在 Docker 层方面看起来是一样的。
我们希望使用多阶段构建: 一个依赖于 Maven 进行依赖性解析和构建,另一个依赖于 JDK 或 JRE 启动应用程序。

构建阶段(Maven 图像) :

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

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

  • 从前一阶段复制罐子
  • Entrypoint/cmd: 运行应用程序

这里有两个例子。

A)没有缓存的简单方法,用于下载的 maven 依赖项

文件夹:

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

这个解决方案的缺点是什么?Xml 中的任何更改都意味着重新创建下载和存储 maven 依赖项的整个层。 对于具有许多依赖项的应用程序来说,这通常是不可接受的(如果在构建映像期间没有使用 maven 存储库管理器,那么 Spring Boot 会拉动许多依赖项)。

B)一种更有效的缓存方式,用于下载 maven 依赖项

这里的方法与在 docker 构建器缓存中缓存的 maven 依赖项下载相同。
缓存操作依赖于 buildkit (docker 的实验 API)。
要启用 buildkit,必须设置 env 变量 DOCKER _ BUILDKIT = 1(您可以在需要的地方这样做: 。Bashrc,命令行,docker daemon json 文件...)。

文件夹:

# 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


                                                                                                                                                                        

                                                                       

                                                                       

Create a Dockerfile
#
# Build stage
#


FROM maven:3.6.3-jdk-11-slim AS build


WORKDIR usr/src/app


COPY . ./


RUN mvn clean package


#
# Package stage
#


FROM openjdk:11-jre-slim


ARG JAR_NAME="project-name"


WORKDIR /usr/src/app


EXPOSE ${HTTP_PORT}


COPY --from=build /usr/src/app/target/${JAR_NAME}.jar ./app.jar


CMD ["java","-jar", "./app.jar"]

Jib 作为一个 Maven 插件,使得使用 Docker 守护进程或者忽略它变得足够简单和灵活。无论是在命令行中还是在 pom.xml 中。

更多信息,你可以参考 构建码头图像使用专家

下面是一个简单的 pom.xml:

...
<properties>
<java.version>11</java.version>
<docker.name>amirkeshavarz/hellomavendocker</docker.name>
<docker.REGISTRY_USERNAME>your-dockerhub-username</docker.REGISTRY_USERNAME>
<docker.REGISTRY_PASSWORD>your-dockerhub-password</docker.REGISTRY_PASSWORD>
</properties>
...
<build>
<plugins>
...
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.8.0</version>
<configuration>
<from>
<image>openjdk:17-jdk-alpine</image>
<auth>
<username>${docker.REGISTRY_USERNAME}</username>
<password>${docker.REGISTRY_PASSWORD}</password>
</auth>
</from>
<to>
<image>${docker.name}</image>
<auth>
<username>${docker.REGISTRY_USERNAME}</username>
<password>${docker.REGISTRY_PASSWORD}</password>
</auth>
</to>
<container>
<environment></environment>
<ports>
<port>8080</port>
</ports>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
</container>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...

更新 Docker 文件@Mark O’Connor 回答-

FROM maven:3.8.6-openjdk-18-slim AS build
COPY src /home/app/src
COPY pom.xml /home/app
RUN mvn -f /home/app/pom.xml clean package


FROM openjdk:20
COPY --from=build /home/app/target/simple-spring-application-0.0.1-SNAPSHOT.jar /usr/local/lib/my-app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/local/lib/my-app.jar"]

构建、运行和日志(替换 DOCKER-ID)-

$ docker build -t my-app .
$ docker run -dp 8080:8080 --rm -it my-app:latest
$ docker logs --follow <DOCKER-ID>

链接到 git 项目 -https://github.com/idanuda/simple-spring-application