如何为 Java 应用程序构建 docker 容器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31696439/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to build a docker container for a Java application
提问by Tobias Kremer
What I want to do is build a docker image for my Java application but the following considerations should be true for most compiled languages.
我想要做的是为我的 Java 应用程序构建一个 docker 映像,但对于大多数编译语言,以下注意事项应该是正确的。
problem
问题
On my build server I want to produce a docker image for my application as the deliverable. For this I have to compile the application using some build tool (typically Gradle, Maven or Ant) and then add the created JAR file to the docker image. As I want the docker image to just execute the JAR file I will of course start from a base image with Java already installed.
在我的构建服务器上,我想为我的应用程序生成一个 docker 镜像作为可交付成果。为此,我必须使用一些构建工具(通常是 Gradle、Maven 或 Ant)编译应用程序,然后将创建的 JAR 文件添加到 docker 镜像。因为我希望 docker 镜像只执行 JAR 文件,所以我当然会从已经安装了 Java 的基础镜像开始。
There are three ways of doing this:
有以下三种方法:
let the build tool control the process
让构建工具控制过程
In this case my build tool controls the whole process. So it prepares the JAR file and after the JAR is created it calls Docker to create the image. This works as the JAR is created beforehand and Docker can be oblivious of the build process needed to create the JAR.
在这种情况下,我的构建工具控制了整个过程。因此它准备 JAR 文件,并在创建 JAR 后调用 Docker 来创建映像。这是因为 JAR 是预先创建的,而 Docker 可能会忽略创建 JAR 所需的构建过程。
But my Dockerfile is no longer standalone. It depends on steps to happen outside of Docker for it work. In my Dockerfile I will have a COPY
or ADD
statement that is supposed to copy the JAR file to the image. This statement will fail when the jar is not created beforehand. So just executing the Dockerfile might not work. This becomes a problem if you want to integrate with services that just build using the present Dockerfile like the auto-build feature on DockerHub.
但是我的 Dockerfile 不再是独立的。它的工作取决于在 Docker 之外发生的步骤。在我的 Dockerfile 中,我将有一个COPY
orADD
语句,用于将 JAR 文件复制到映像。如果未事先创建 jar,此语句将失败。因此,仅执行 Dockerfile 可能不起作用。如果您想与仅使用现有 Dockerfile 构建的服务(如 DockerHub 上的自动构建功能)集成,这将成为一个问题。
let Docker control the build
让 Docker 控制构建
In this case all necessary steps to create the image are added to the Dockerfile so the image can be created by just executing the Docker build.
在这种情况下,创建映像的所有必要步骤都添加到 Dockerfile 中,因此只需执行 Docker 构建即可创建映像。
The main problem with this approach is that there is no way to add to a Dockerfile commands that should be executed outside the docker image being created. This means I have to add my source code and my build tools to the docker image and build my JAR file inside the image. This will result in my image being bigger than it has to be due to all the files added that will be unnecessary at runtime. This will also add extra layers to my image.
这种方法的主要问题是无法向 Dockerfile 添加应该在创建的 docker 镜像之外执行的命令。这意味着我必须将我的源代码和我的构建工具添加到 docker 镜像并在镜像中构建我的 JAR 文件。这将导致我的图像比它必须的大,因为添加了所有在运行时不必要的文件。这也会为我的图像添加额外的图层。
Edit:
编辑:
As @adrian-mouat pointed out if I would add the sources, build the application and deleted the sources in one RUN statement I could avoid adding unnecessary files and layers to the Docker image. This would mean creating some insane chained command.
正如@adrian-mouat 指出的那样,如果我在一个 RUN 语句中添加源、构建应用程序并删除源,我可以避免向 Docker 映像添加不必要的文件和层。这意味着创建一些疯狂的链式命令。
two separate builds
两个独立的构建
In this case we split our build in two: first we create the JAR file using our build tool and upload it to a repository (Maven or Ivy repository). We then trigger a separate Docker build that just adds the JAR file from the repository.
在这种情况下,我们将构建分为两部分:首先我们使用构建工具创建 JAR 文件并将其上传到存储库(Maven 或 Ivy 存储库)。然后我们触发一个单独的 Docker 构建,它只是从存储库中添加 JAR 文件。
conclusion
结论
In my opinion the better way would be letting the build tool control the process. This is will result in a clean docker image and as the image is what we want to deliver this is of importance. To avoid having a potentially not working Dockerfile lying around this should be created as part of the build. So no one would accidentally use it to start a broken build.
在我看来,更好的方法是让构建工具控制过程。这将产生一个干净的 docker 镜像,因为镜像是我们想要交付的,所以这很重要。为了避免可能无法正常工作的 Dockerfile,应该在构建过程中创建它。所以没有人会不小心使用它来启动一个损坏的构建。
But this will not allow me to integrate with DockerHub.
但这不允许我与 DockerHub 集成。
question
题
Is there another way I am missing?
还有另一种方式我失踪了吗?
采纳答案by Mark O'Connor
The docker registry hub has a Maven imagethat can be used to create java containers.
docker 注册中心有一个Maven 镜像,可以用来创建 java 容器。
Using this approach the build machine does not need to have either Java or Maven pre-installed, Docker controls the entire build process.
使用这种方法,构建机器不需要预先安装 Java 或 Maven,Docker 控制整个构建过程。
Example
例子
├── Dockerfile
├── pom.xml
└── src
├── main
│?? ├── java
│?? │?? └── org
│?? │?? └── demo
│?? │?? └── App.java
│?? └── resources
│?? └── log4j.properties
└── test
└── java
└── org
└── demo
└── AppTest.java
Image is built as follows:
图像构建如下:
docker build -t my-maven .
And run as follows:
并运行如下:
$ 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"]
Update
更新
If you wanted to optimize your image to exclude the source you could create a Dockerfile that only includes the built 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"]
And build the image in two steps:
并分两步构建镜像:
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 .
__
__
Update (2017-07-27)
更新 (2017-07-27)
Docker now has a multi-stage buildcapability. This enables Docker to build an image containing the build tools but only the runtime dependencies.
Docker 现在具有多阶段构建功能。这使 Docker 能够构建包含构建工具但仅包含运行时依赖项的映像。
The following example demonstrates this concept, note how the jar is copied from target directory of the first build phase
下面的例子演示了这个概念,注意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"]
回答by Adrian Mouat
A couple of things:
几件事:
If you delete files in the same instruction you add them, they won't consume space in the image. If you look at some of the Dockerfiles for the official images you will see they download source, build it and delete it all in the same step (e.g. https://github.com/docker-library/python/blob/0fa3202789648132971160f686f5a37595108f44/3.5/slim/Dockerfile). This does mean you need to do some annoying gymnastics, but it is perfectly doable.
I don't see the problem with two separate Dockerfiles. The nice thing about this is that you could use the JRE rather than the JDK to host your jar.
如果您在添加文件的同一指令中删除文件,它们将不会占用图像中的空间。如果您查看官方镜像的一些 Dockerfiles,您会看到它们在同一步骤中下载源代码、构建它并删除它(例如https://github.com/docker-library/python/blob/0fa3202789648132971160f686f5a37595108f44/3.5 /slim/Dockerfile)。这确实意味着你需要做一些烦人的体操,但这是完全可行的。
我没有看到两个单独的 Dockerfile 的问题。这样做的好处是您可以使用 JRE 而不是 JDK 来托管您的 jar。
回答by pmoksuz
there are alternative usages for running jar or war packages
有运行 jar 或 war 包的替代用法
- add jar into image.
- set heapsize for java
- run jar command via entrypoint
- 将 jar 添加到图像中。
- 为java设置堆大小
- 通过入口点运行 jar 命令
sample dockerfile
示例 dockerfile
FROM base
ADD sample.jar renamed.jar
ENV HEAP_SIZE 256m
ENTRYPOINT exec java -Xms$HEAP_SIZE -Xmx$HEAP_SIZE -jar renamed.jar
in addition package deployment example on tomcat
另外在 tomcat 上的包部署示例
FROM tomcat7
ADD sample.war ${CATALINA_HOME}/webapps/ROOT.war
CMD ${CATALINA_HOME}/bin/catalina.sh run
Building dockerfiles as an image
将 dockerfiles 构建为图像
cp tomcat.dockerfile /workingdir/Dockerfile
docker build -t name /workingdir/Dockerfile .
List images
列出图像
docker images
Use image to create a container
使用镜像创建容器
docker run --name cont_name --extra-vars var1=val1 var2=val2 imagename
回答by Eyal.Dahari
HereI describe how I do it in my development environment.
在这里,我描述了我如何在我的开发环境中做到这一点。
- Build the war/jar locally with Maven
- Copy it to a local Docker folder
- Run Intellij Docker pluginwhich creates a docker image that contains the war/jar, run the application server and deploys it on the remote Docker server
- 使用 Maven 在本地构建 war/jar
- 将其复制到本地 Docker 文件夹
- 运行Intellij Docker 插件,该插件创建一个包含 war/jar 的docker镜像,运行应用程序服务器并将其部署在远程 Docker 服务器上
Hope it helps.
希望能帮助到你。
回答by Riddhi Gohil
Structure of java aplication
java应用程序的结构
Demo
└── src
| ├── main
| │ ├── java
| │ │ └── org
| │ │ └── demo
| │ │ └── App.java
| │ └── resources
| │ └── application.properties
| └── test
| └── java
| └── org
| └── demo
| └── App.java
├──── Dockerfile
├──── pom.xml
Content of Dockerfile
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
构建和运行镜像的命令
- 转到项目目录。让我们说 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
回答by gclaussn
We used the Spotify Docker Maven Pluginfor a while. The plugin allows you to bind a Docker build it to a phase of the Maven lifecycle.
我们使用了一段时间的Spotify Docker Maven 插件。该插件允许您将 Docker 构建绑定到 Maven 生命周期的一个阶段。
An example: Run the Docker build after packaging (phase: package) your application by configuring the plugin to add your built application as a resource to the Docker build context. In the deploy phase run the Docker push goal to push your Docker image to a registry. This can run beside the normal deploy plugin, which publishes the artifact into a repository like Nexus.
示例:通过配置插件将构建的应用程序作为资源添加到 Docker 构建上下文,在打包(阶段:打包)应用程序后运行 Docker 构建。在部署阶段,运行 Docker push 目标将您的 Docker 映像推送到注册表。这可以在普通的部署插件旁边运行,它将工件发布到像 Nexus 这样的存储库中。
Later on, we splitted the build into two separate jobs on the CI server. Since Docker is just a one way to run your application (sometimes we need the released application on different environments not only Docker), the Maven build should not rely on Docker.
后来,我们在 CI 服务器上将构建拆分为两个独立的作业。由于 Docker 只是运行应用程序的一种方式(有时我们需要在不同环境中发布的应用程序不仅仅是 Docker),因此 Maven 构建不应依赖于 Docker。
So the first job releases the application in Nexus (via Maven deploy). The second job (which can be a downstream dependency of the first job) downloads the latest release artifact, performs the Docker build and pushes the image to the registry. For downloading the latest release we use the Versions Maven Plugin(versions:use-latest-releases) as well as the Maven Dependency Plugin(dependency:get and dependency:copy).
因此,第一个作业在 Nexus 中发布应用程序(通过 Maven 部署)。第二个作业(可以是第一个作业的下游依赖项)下载最新发布的工件,执行 Docker 构建并将映像推送到注册表。为了下载最新版本,我们使用版本 Maven 插件(versions:use-latest-releases) 以及Maven Dependency Plugin(dependency:get 和 dependency:copy)。
The second job can also be started for specific version of the application to (re)build the Docker image for an older release. Moreover you can use a build pipeline (on Jenkins), which executes both jobs and passes the release version or the release artifact to the Docker build.
也可以针对特定版本的应用程序启动第二个作业,以(重新)构建旧版本的 Docker 映像。此外,您可以使用构建管道(在 Jenkins 上),它执行两个作业并将发布版本或发布工件传递给 Docker 构建。
回答by Qingyang Chen
The easiest way is to let the build tool control the process. Otherwise, you would have to maintain your build tool's build file (like pom.xml
for Maven or build.gradle
for Gradle) as well as a Dockerfile
.
最简单的方法是让构建工具控制过程。否则,您将不得不维护构建工具的构建文件(如pom.xml
Maven 或build.gradle
Gradle)以及Dockerfile
.
A simple way to build a Docker container for your Java app is to use Jib, which is available as Mavenand Gradleplugins.
为 Java 应用程序构建 Docker 容器的一种简单方法是使用Jib,它可用作Maven和Gradle插件。
For example, if you are using Maven and want to build your container to your running Docker daemon, you can just run this one command:
例如,如果您正在使用 Maven 并希望为正在运行的 Docker 守护程序构建容器,则只需运行以下命令:
mvn compile com.google.cloud.tools:jib-maven-plugin:0.9.2:dockerBuild
You can also build directly to a Docker registrywith Jib without needing to install docker
, run a Docker daemon (which requires root privileges), or write a Dockerfile
. It's also faster and builds images reproducibly.
您还可以使用 Jib直接构建到 Docker 注册表,而无需安装docker
、运行 Docker 守护程序(需要 root 权限)或编写Dockerfile
. 它也更快并且可重复地构建图像。
See more about Jib at its Github repo: https://github.com/GoogleContainerTools/jib
在其 Github 存储库中查看有关 Jib 的更多信息:https: //github.com/GoogleContainerTools/jib
回答by anandchaugule
Containerize your java application using Jib tool without writing dockerfile
使用 Jib 工具容器化您的 Java 应用程序,而无需编写 dockerfile
Jibis an open-source Java tool maintained by Google for building Docker images of Java applications. It simplifies containerization since with it, we don't need to write a dockerfile. And actually, we don't even have to have docker installedto create and publish the docker images ourselves.
Jib是由 Google 维护的开源 Java 工具,用于构建 Java 应用程序的 Docker 映像。它简化了容器化,因为有了它,我们不需要编写 dockerfile。实际上,我们甚至不必安装docker 就可以自己创建和发布 docker 镜像。
Google publishes Jib as both a Maven and a Gradle plugin. https://github.com/GoogleContainerTools/jib
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
使用 Maven 项目容器化您的 Java 应用程序
https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin#quickstart
使用 Gradle 项目容器化您的 Java 应用程序
https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin#quickstart