如何为 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 文件中,我将有一个 COPY
或 ADD
语句,用于将 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
我想做的是为我的 Java 应用程序构建一个 docker 图像,但对于大多数编译语言来说,以下注意事项应该是正确的。
问题
在我的构建服务器上,我想为我的应用程序生成一个 docker 图像作为可交付成果。为此,我必须使用一些构建工具(通常是 Gradle、Maven 或 Ant)编译应用程序,然后将创建的 JAR 文件添加到 docker 图像。因为我希望 docker 映像只执行 JAR 文件,所以我当然会从已安装 Java 的基本映像开始。
可以通过三种方式实现:
让构建工具控制进程
在这种情况下,我的构建工具控制着整个过程。因此它会准备 JAR 文件,并在创建 JAR 后调用 Docker 来创建图像。这是因为 JAR 是预先创建的,Docker 可以忽略创建 JAR 所需的构建过程。
但是我的 Docker 文件不再是独立的。它的工作取决于 Docker 之外发生的步骤。在我的 Docker 文件中,我将有一个 COPY
或 ADD
语句,用于将 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