java - 如何为 Java 应用程序构建 docker 容器

标签 java maven gradle docker dockerfile

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

问题

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

可以通过三种方式执行此操作:

让构建工具控制流程

在这种情况下,我的构建工具控制整个过程。因此它准备 JAR 文件,并在创建 JAR 之后调用 Docker 来创建镜像。这是因为 JAR 是预先创建的,并且 Docker 可以忽略创建 JAR 所需的构建过程。

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

让 Docker 控制构建

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

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

编辑:

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

两个单独的构建

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

结论

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

但这不允许我与 DockerHub 集成。

问题

我还有其他失踪的方式吗?

2020 年 6 月更新

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

2021 年 2 月更新

我发现 James Ward 的这篇博文和视频更好地反射(reflect)了当前的技术水平。 https://cloud.google.com/blog/topics/developers-practitioners/comparing-containerization-methods-buildpacks-jib-and-dockerfile

最佳答案

docker 注册表中心有一个 Maven image可用于创建 java 容器。

使用这种方法,构建机器不需要预安装 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 现在有一个 multi-stage build能力。这使得 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"]

关于java - 如何为 Java 应用程序构建 docker 容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41491698/

相关文章:

java - 使用 Java 配置和 Spring Security 3.2 的安全方法注释

java - Maven、Git、Android 和 Maven 发布插件

java - Wildfly Maven 插件未注册 Web 上下文

google-app-engine - 如何使用 Kotlin DSL 配置 AppEngine Gradle 插件

java - 在Android Studio Project中添加外部jar文件

java - Selenium Java WebDriverWait - 无法访问 java.util.function

java.lang.NoClassDefFoundError : org/hibernate/util/DTDEntityResolver

maven - 如何避免 IntelliJ IDEA 中出现 "The package is not exported by the bundle dependencies"错误?

gradle - 如何从 GradleBuild 任务中设置另一个 gradle 构建脚本任务的输出文件?

java - 用java从网络上检索图像