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

How to build a docker container for a Java application

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

问题

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

可以通过三种方式实现:

让构建工具控制进程

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

但是我的 Docker 文件不再是独立的。它的工作取决于 Docker 之外发生的步骤。在我的 Docker 文件中,我将有一个 COPYADD 语句,用于将 JAR 文件复制到图像。如果未事先创建 jar,此语句将失败。所以只执行 Docker 文件可能行不通。如果您想与仅使用当前 Docker 文件构建的服务集成,例如 DockerHub 上的自动构建功能。

让Docker控制构建

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

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

编辑:

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

两个独立的构建

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

结论

我认为更好的方法是让构建工具控制过程。这将产生一个干净的 docker 图像,因为图像是我们想要提供的,所以这一点很重要。为避免出现可能无法正常工作的 Docker 文件,应将其创建为构建的一部分。所以没有人会不小心用它来启动一个损坏的构建。

但这不允许我与 DockerHub 集成。

问题

还有其他方法我想念吗?

2020 年 6 月更新

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

2021 年 2 月更新

我发现此博客 post 和来自 James Ward 的视频更好地反映了当前的技术水平。 https://cloud.google.com/blog/topics/developers-practitioners/comparing-containerization-methods-buildpacks-jib-and-dockerfile

几件事:

  • 如果您在添加文件的同一指令中删除文件,它们将不会占用映像中的 space。如果您查看官方镜像的一些 Dockerfile,您会看到它们下载源代码、构建它并在同一步骤中将其全部删除(例如 https://github.com/docker-library/python/blob/0fa3202789648132971160f686f5a37595108f44/3.5/slim/Dockerfile)。这确实意味着您需要做一些烦人的体操,但这是完全可行的。

  • 我没有发现两个单独的 Dockerfile 有问题。这样做的好处是您可以使用 JRE 而不是 JDK 来托管您的 jar。

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

Docker文件

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

更新

如果您想优化图像以排除源,您可以创建一个 Docker 仅包含构建的 jar 的文件:

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

运行ning jar 或 war 包有其他用法

  • 将 jar 添加到图像中。
  • 为 java
  • 设置堆大小
  • 运行 通过入口点的 jar 命令

示例 dockerfile

FROM base
ADD sample.jar renamed.jar
ENV HEAP_SIZE 256m
ENTRYPOINT exec java -Xms$HEAP_SIZE -Xmx$HEAP_SIZE -jar renamed.jar

另外 tomcat

上的包部署示例
FROM tomcat7
ADD sample.war ${CATALINA_HOME}/webapps/ROOT.war
CMD ${CATALINA_HOME}/bin/catalina.sh run

将 dockerfiles 构建为镜像

cp tomcat.dockerfile /workingdir/Dockerfile
docker build -t name /workingdir/Dockerfile .

列出图片

docker images

使用镜像创建容器

docker run --name cont_name --extra-vars var1=val1 var2=val2 imagename

Here 我描述一下我是如何在我的开发环境中做到这一点的。

  • 使用 Maven
  • 在本地构建 war/jar
  • 将其复制到本地 Docker 文件夹
  • 运行 Intellij Docker plugin 创建包含 war/jar、运行 应用程序服务器的 docker 图像并将其部署到远程 Docker服务器

希望对您有所帮助。

Structure of java aplication

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

Content of Dockerfile

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

Commands to build and run image

  • Go to the directory of project.Lets say D:/Demo
$ cd D/demo
$ mvn clean install
$ docker build demo .
$ docker run -p 8080:8080 -t demo

Check that container is running or not

$ docker ps

The output will be

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

我们使用了 Spotify Docker Maven Plugin 一段时间。该插件允许您将 Docker 构建它绑定到 Maven 生命周期的一个阶段。

一个例子: 运行 Docker 在打包(阶段:打包)您的应用程序后构建,通过配置插件将构建的应用程序作为资源添加到 Docker 构建上下文。在部署阶段 运行 Docker 推送目标将您的 Docker 映像推送到注册表。这可以 运行 除了正常的部署插件,它将工件发布到像 Nexus 这样的存储库中。

稍后,我们在 CI 服务器上将构建拆分为两个单独的作业。由于 Docker 只是 运行 您的应用程序的一种方式(有时我们需要在不同环境下发布的应用程序而不仅仅是 Docker),Maven 构建不应依赖 Docker .

所以第一个作业在 Nexus 中发布应用程序(通过 Maven 部署)。第二个作业(可以是第一个作业的下游依赖项)下载最新版本的工件,执行 Docker 构建并将映像推送到注册表。要下载最新版本,我们使用 Versions Maven Plugin (versions:use-latest-releases) as well as the Maven Dependency Plugin(依赖项:get 和 dependency:copy)。

第二个作业也可以针对特定版本的应用程序启动(重新)构建旧版本的 Docker 映像。此外,您可以使用构建管道(在 Jenkins 上),它执行两个作业并将发布版本或发布工件传递给 Docker 构建。

最简单的方法是让构建工具控制进程。否则,您将不得不维护构建工具的构建文件(例如 Maven 的 pom.xml 或 Gradle 的 build.gradle)以及 Dockerfile.

为您的 Java 应用构建 Docker 容器的一种简单方法是使用 Jib, which is available as Maven and Gradle 插件。

例如,如果您正在使用 Maven 并希望将容器构建到 运行ning Docker 守护进程,您可以 运行 这个命令:

mvn compile com.google.cloud.tools:jib-maven-plugin:0.9.2:dockerBuild

您还可以 build directly to a Docker registry 使用 Jib 而无需安装 docker、运行 Docker 守护程序(这需要root 权限),或者写一个 Dockerfile。它也更快并且可重复地构建图像。

在其 Github 存储库中查看有关 Jib 的更多信息:https://github.com/GoogleContainerTools/jib

使用 Jib 工具将您的 java 应用程序容器化,无需编写 dockerfile

Jib 是由 Google 维护的开源 Java 工具,用于构建 Java 应用程序的 Docker 图像。它简化了容器化,因为有了它,我们不需要编写 docker文件。实际上,我们甚至不需要安装 docker 就可以自己创建和发布 docker 图像。

Google 将 Jib 作为 Maven 和 Gradle 插件发布。 https://github.com/GoogleContainerTools/jib

Containerize your java application using Maven project

https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin#quickstart

Containerize your java application using Gradle project

https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin#quickstart