如何为 Java 应用程序构建 Docker 容器问题有三种方法可以做到这一点:结论问题2020年6月更新2021年2月更新例更新

2022-08-31 19:38:25

我想做的是为我的Java应用程序构建一个docker映像,但对于大多数编译语言来说,以下注意事项应该是正确的。

问题

在我的构建服务器上,我想为我的应用程序生成一个 docker 映像作为可交付结果。为此,我必须使用一些构建工具(通常是Gradle,Maven或Ant)编译应用程序,然后将创建的JAR文件添加到docker映像中。由于我希望 docker 映像只执行 JAR 文件,我当然会从已安装 Java 的基础映像开始。

有三种方法可以做到这一点:

让构建工具控制过程

在这种情况下,我的构建工具控制整个过程。因此,它准备JAR文件,并在创建JAR后调用Docker来创建映像。这之所以有效,是因为 JAR 是事先创建的,而 Docker 可能会忘记创建 JAR 所需的构建过程。

但是我的 Dockerfile 不再是独立的。这取决于Docker之外发生的步骤才能正常工作。在我的 Dockerfile 中,我将有一个 or 语句,它应该将 JAR 文件复制到映像中。如果未事先创建 jar,则此语句将失败。因此,仅执行 Dockerfile 可能不起作用。如果您想与仅使用当前Dockerfile构建的服务(如DockerHub上的自动构建功能)集成,这将成为一个问题。COPYADD

让 Docker 控制构建

在这种情况下,创建映像的所有必要步骤都将添加到 Dockerfile 中,以便只需执行 Docker 构建即可创建映像。

这种方法的主要问题是,没有办法向 Dockerfile 添加应该在正在创建的 docker 映像之外执行的命令。这意味着我必须将源代码和构建工具添加到 docker 映像中,并在映像内生成 JAR 文件。这将导致我的映像大于它必须的大小,因为添加的所有文件在运行时都是不必要的。这也将为我的图像添加额外的图层。

编辑:

正如@adrian-mouat所指出的那样,如果我在一个RUN语句中添加源代码,构建应用程序并删除源代码,我可以避免向Docker映像添加不必要的文件和层。这将意味着创建一些疯狂的链接命令。

两个独立的构建

在这种情况下,我们将构建一分为二:首先,我们使用构建工具创建JAR文件,然后将其上传到存储库(Maven或Ivy存储库)。然后,我们触发一个单独的 Docker 构建,该构建仅从存储库中添加 JAR 文件。

结论

在我看来,更好的方法是让构建工具控制该过程。这将产生一个干净的 docker 映像,并且由于映像是我们想要交付的,因此这一点很重要。为了避免周围有一个可能不工作的Dockerfile,应该将其创建为构建的一部分。因此,没有人会意外地使用它来启动损坏的构建。

但这不允许我与DockerHub集成。

问题

我还有别的缺失方式吗?

2020年6月更新

自从我第一次创建这个问题以来的几年里,很多东西都发生了变化。在这一点上,我主张使用Googel的JIB工具。它与最常见的Java构建工具(Maven和Gradle)集成,并允许您直接从构建中创建容器。这比我多年前考虑过的任何旧方法都要简洁得多。

2021年2月更新

我发现詹姆斯·沃德(James Ward)的这篇博客文章和视频更好地反映了目前最先进的技术。https://cloud.google.com/blog/topics/developers-practitioners/comparing-containerization-methods-buildpacks-jib-and-dockerfile


答案 1

Docker 注册表中心具有可用于创建 Java 容器的 Maven 映像

使用这种方法,构建机器不需要预安装Java或Maven,Docker控制整个构建过程。

├── Dockerfile
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── org
    │   │       └── demo
    │   │           └── App.java
    │   └── resources
    │       └── log4j.properties
    └── test
        └── java
            └── org
                └── demo
                    └── AppTest.java

映像的构建方式如下:

docker build -t my-maven .

并按如下方式运行:

$ docker run -it --rm my-maven
0    [main] INFO  org.demo.App  - hello world

Dockerfile

FROM maven:3.3-jdk-8-onbuild
CMD ["java","-jar","/usr/src/app/target/demo-1.0-SNAPSHOT-jar-with-dependencies.jar"]

更新

如果你想优化你的镜像来排除源代码,你可以创建一个只包含构建的jar的Dockerfile:

FROM java:8
ADD target/demo-1.0-SNAPSHOT-jar-with-dependencies.jar /opt/demo/demo-1.0-SNAPSHOT-jar-with-dependencies.jar
CMD ["java","-jar","/opt/demo/demo-1.0-SNAPSHOT-jar-with-dependencies.jar"]

然后分两步构建映像:

docker run -it --rm -w /opt/maven \
   -v $PWD:/opt/maven \
   -v $HOME/.m2:/root/.m2 \
   maven:3.3-jdk-8 \
   mvn clean install

docker build -t my-app .

__

更新 (2017-07-27)

Docker 现在具有多阶段构建功能。这使 Docker 能够生成包含生成工具但仅包含运行时依赖项的映像。

以下示例演示了此概念,请注意如何从第一个构建阶段的目标目录中复制 jar

FROM maven:3.3-jdk-8-onbuild 

FROM java:8
COPY --from=0 /usr/src/app/target/demo-1.0-SNAPSHOT.jar /opt/demo.jar
CMD ["java","-jar","/opt/demo.jar"]

答案 2

Java应用程序的结构

Demo
└── src
|    ├── main
|    │   ├── java
|    │   │   └── org
|    │   │       └── demo
|    │   │           └── App.java
|    │   └── resources
|    │       └── application.properties
|    └── test
|         └── java
|               └── org
|                   └── demo
|                         └── App.java  
├──── Dockerfile
├──── pom.xml

Dockerfile 的内容

FROM java:8
EXPOSE 8080
ADD /target/demo.jar demo.jar
ENTRYPOINT ["java","-jar","demo.jar"]

用于生成和运行映像的命令

  • 转到项目的目录。假设 D:/演示
$ cd D/demo
$ mvn clean install
$ docker build demo .
$ docker run -p 8080:8080 -t demo

检查容器是否正在运行

$ docker ps

输出将为

CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                    NAMES
55c11a464f5a        demo1               "java -jar demo.jar"   21 seconds ago      Up About a minute   0.0.0.0:8080->8080/tcp   cranky_mayer

推荐