如何构build一个Java应用程序的docker容器
我想要做的是为我的Java应用程序构build一个docker镜像,但是对于大多数编译语言来说,下面的考虑应该是真实的。
问题
在我的构build服务器上,我想为我的应用程序生成一个docker镜像作为交付物。 为此,我必须使用一些构build工具(通常是Gradle,Maven或Ant)来编译应用程序,然后将创build的JAR文件添加到泊坞窗图像中。 因为我希望Docker镜像只执行JAR文件,所以我将从已经安装了Java的基础镜像开始。
有三种方法可以做到这一点:
让构build工具控制过程
在这种情况下,我的构build工具控制整个过程。 所以它准备了JAR文件,在创buildJAR之后,它会调用Docker来创build映像。 这是因为事先创build了JAR,而Docker可能忘记了创buildJAR所需的构build过程。
但是我的Dockerfile不再是独立的。 它取决于在Docker之外发生的步骤。 在我的Dockerfile中,我将有一个COPY
或ADD
语句,它应该将JAR文件复制到图像中。 当事先未创buildjar时,这个语句会失败。 所以只是执行Dockerfile可能不起作用。 如果您想要使用DockerHub上的自动构buildfunction来构build使用当前Dockerfile的服务,就会成为一个问题。
让Docker控制构build
在这种情况下,创build映像所需的所有步骤都会添加到Dockerfile中,以便通过执行Docker构build来创build映像。
这种方法的主要问题是没有办法添加到Dockerfile命令,这些命令应该在创build的docker镜像之外执行。 这意味着我必须将我的源代码和构build工具添加到泊坞窗图像中,并在图像中构build我的JAR文件。 这将导致我的图像比它必须是由于所有添加的文件在运行时将是不必要的。 这也会为我的图片添加额外的图层。
编辑:
正如@ adrian-mouat指出,如果我要添加源,build立应用程序并删除一个RUN语句中的源,我可以避免添加不必要的文件和图层到Docker镜像。 这将意味着创造一些疯狂的链式命令。
两个独立的版本
在这种情况下,我们将构build分为两部分:首先,使用构build工具创buildJAR文件,并将其上传到存储库(Maven或Ivy存储库)。 然后,我们触发一个单独的Docker构build,它只是从存储库中添加JAR文件。
结论
在我看来,更好的方法是让构build工具控制过程 。 这将导致一个干净的docker形象,因为图像是我们想要传递这是重要的。 为了避免有一个潜在的不工作的Dockerfile应该被创build作为构build的一部分。 所以没有人会意外地使用它来开始一个破碎的构build。
但是这不允许我与DockerHub集成。
题
有没有另一种方法我失踪?
docker注册中心有一个可用于创buildjava容器的Maven图像 。
使用这种方法,构build机器不需要预先安装Java或Maven,Docker控制整个构build过程。
例
├── Dockerfile ├── pom.xml └── src ├── main │ ├── java │ │ └── org │ │ └── demo │ │ └── App.java │ └── resources │ └── log4j.properties └── test └── java └── org └── demo └── AppTest.java
容器build造如下:
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"]
更新
如果你想优化你的容器来排除源代码,你可以创build一个只包含内置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"]
并分两步build造容器:
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现在有一个多阶段的构build能力。 这使得Docker可以构build一个包含构build工具的映像的容器,但只输出一个只有运行时依赖的映像。
以下示例演示了这个概念,注意如何从第一个构build阶段的目标目录中复制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应用程序的结构
Demo └── src | ├── main | │ ├── java | │ │ └── org | │ │ └── demo | │ │ └── App.java | │ └── resources | │ └── application.properties | └── test | └── java | └── org | └── demo | └── App.java ├──── Dockerfile ├──── pom.xml
Dockerfile的内容
FROM java:8 EXPOSE 8080 ADD /target/demo.jar demo.jar ENTRYPOINT ["java","-jar","demo.jar"]
构build和运行图像的命令
- 进入项目目录。让我们说D:/ Demo
$ cd D/demo $ mvn clean install $ docker build demo . $ docker run -p 8080:8080 -t demo
检查容器是否正在运行
$ docker ps
输出将是
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
几件事情:
-
如果您删除的文件中添加了相同的指令,它们将不会消耗图像中的空间。 如果你看官方图片的一些Dockerfiles,你会看到他们下载源代码,build立它,并删除所有在同一步骤(例如https://github.com/docker-library/python/blob/0fa3202789648132971160f686f5a37595108f44/3.5 / slim / Dockerfile )。 这确实意味着你需要做一些令人讨厌的体操,但这是完全可行的。
-
我没有看到两个单独的Dockerfiles的问题。 关于这个的好处是你可以使用JRE而不是JDK来托pipe你的jar。
有运行jar或war包的替代用法
- 将jar添加到图像中。
- 为java设置heapsize
- 通过入口点运行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
将docker文件构build为图像
cp tomcat.dockerfile /workingdir/Dockerfile docker build -t name /workingdir/Dockerfile .
列出图像
docker images
使用图像来创build一个容器
docker run --name cont_name --extra-vars var1=val1 var2=val2 imagename
在这里我描述了我在开发环境中如何做到这一点。
- 使用Maven在本地构buildwar / jar
- 将其复制到本地Docker文件夹
- 运行Intellij Docker插件 ,它创build一个包含war / jar的docker镜像,运行应用程序服务器并将其部署到远程Docker服务器上
希望能帮助到你。
我们使用了Spotify Docker Maven Plugin一段时间。 该插件允许你将一个Docker构build到Maven生命周期的一个阶段。
示例:通过configuration插件将打包的应用程序作为资源添加到Docker构build上下文,打包(phase:package)应用程序后运行Docker构build。 在部署阶段运行Docker推送目标,将Docker镜像推送到registry中。 这可以在正常的部署插件旁边运行,该部署插件将工件发布到像Nexus这样的存储库中。
稍后,我们将构build拆分为CI服务器上的两个独立作业。 由于Docker只是运行应用程序的一种方式(有时我们需要在不同的环境中发布应用程序,不仅仅是Docker),Maven构build不应该依赖Docker。
所以第一份工作就是在Nexus上发布应用程序(通过Maven部署)。 第二个工作(可以是第一个工作的下游依赖)下载最新的发行工件,执行Docker构build并将映像推送到registry。 为了下载最新版本,我们使用版本Maven Plugin (版本:use-latest-releases)以及Maven Dependency Plugin (依赖:get和dependency:copy)。
第二个工作也可以启动特定版本的应用程序来(重新)构build旧版本的Docker镜像。 此外,您可以使用构buildpipe道(在Jenkins上),它执行这两个作业,并将发布版本或发布工件传递给Docker构build版本。