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

Tob*_*mer 43 java gradle maven docker dockerfile

我想要做的是为我的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文件.

结论

在我看来,更好的方法是让构建工具控制流程.这将产生干净的泊坞窗图像,因为图像是我们想要提供的,这是非常重要的.为避免可能无法正常工作的Dockerfile,应将其作为构建的一部分创建.因此,没有人会不小心使用它来开始破坏构建.

但这不允许我与DockerHub集成.

我还有另一种方法吗?

Mar*_*nor 29

docker注册表中心有一个可用于创建Java容器的Maven映像.

使用这种方法,构建机器不需要预先安装Java或Maven,Docker控制整个构建过程.

??? Dockerfile
??? pom.xml
??? src
    ??? main
    ?   ??? java
    ?   ?   ??? org
    ?   ?       ??? demo
    ?   ?           ??? App.java
    ?   ??? resources
    ?       ??? log4j.properties
    ??? test
        ??? java
            ??? org
                ??? demo
                    ??? AppTest.java
Run Code Online (Sandbox Code Playgroud)

容器构建如下:

docker build -t my-maven .
Run Code Online (Sandbox Code Playgroud)

运行如下:

$ docker run -it --rm my-maven
0    [main] INFO  org.demo.App  - hello world
Run Code Online (Sandbox Code Playgroud)

Dockerfile

FROM maven:3.3-jdk-8-onbuild
CMD ["java","-jar","/usr/src/app/target/demo-1.0-SNAPSHOT-jar-with-dependencies.jar"]
Run Code Online (Sandbox Code Playgroud)

更新

如果要优化容器以排除源,可以创建仅包含构建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"]
Run Code Online (Sandbox Code Playgroud)

并分两步构建容器:

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 .
Run Code Online (Sandbox Code Playgroud)

__

更新(2017-07-27)

Docker现在具有多阶段构建功能.这使得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"]
Run Code Online (Sandbox Code Playgroud)


Rid*_*hil 7

java应用程序的结构

Demo
??? src
|    ??? main
|    ?   ??? java
|    ?   ?   ??? org
|    ?   ?       ??? demo
|    ?   ?           ??? App.java
|    ?   ??? resources
|    ?       ??? application.properties
|    ??? test
|         ??? java
|               ??? org
|                   ??? demo
|                         ??? App.java  
????? Dockerfile
????? pom.xml
Run Code Online (Sandbox Code Playgroud)

Dockerfile的内容

FROM java:8
EXPOSE 8080
ADD /target/demo.jar demo.jar
ENTRYPOINT ["java","-jar","demo.jar"]
Run Code Online (Sandbox Code Playgroud)

用于构建和运行映像的命令

  • 转到项目目录.让我们说D:/ Demo
$ cd D/demo
$ mvn clean install
$ docker build demo .
$ docker run -p 8080:8080 -t demo
Run Code Online (Sandbox Code Playgroud)

检查容器是否正在运行

$ docker ps
Run Code Online (Sandbox Code Playgroud)

输出将是

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
Run Code Online (Sandbox Code Playgroud)