我想要做的是为我的 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/