参考资料:https://github.com/docker/labs/tree/master/developer-tools/java/
如果读者朋友阅读了我之前的文章《Docker入门详解》,那么大家应该对Docker已经有了一个基本的了解,也理解了容器化程序的概念,以及容器化为软件行业特别是云计算领域的软件行业带来的便利性。接下来,我将为大家介绍如何使用Docker来开发容器化的Java程序。
创建基于JDK 8的Docker镜像
这里笔者将介绍如何创建基于JDK 8的Docker镜像。首先使用下面的maven命令创建一个java项目。
mvn archetype:generate -DgroupId=org.examples.java -DartifactId=helloworld -DinteractiveMode=false
上述maven命令会在helloworld目录中创建一个最简单的java程序。
我们使用mvn package
来进行编译。
cd helloworld
mvn package
编译成功后,会在target目录下生成打包好的jar文件。
接下来使用java
命令来运行这个程序。
java -cp target\helloworld-1.0-SNAPSHOT.jar org.examples.java.App
读者会看到该程序输出一行字符串:
Hello World!
但是,如果想要让这个程序在其他用户的电脑上正常的运行,用户还需要在电脑上安装一个JRE。假如是更加复杂的程序,可能还需要其他的依赖软件支持才能正常工作。借助docker,则可以把这些依赖的软件都打包到docker镜像中,这样用户想使用这个软件时,只需要下载这个镜像就可以运行了,不再需要安装其他的依赖软件。
下面我们就把这个helloworld程序打包到docker镜像中。我们准备让java程序在基于JDK 8的环境下运行,因此基础镜像需要包含JDK 8。这里我们使用的基础镜像是openjdk:8。运行下面的命令可以启动一个openjdk:8的容器:
docker container run -it openjdk:8
-it
表示以交互模式启动容器,因此你会看到类似下面的输出:
root@2e1c05978452:/#
这时可以在交互模式下输入命令java -version
来查看jdk 的版本信息:
openjdk version "1.8.0_222"
OpenJDK Runtime Environment (build 1.8.0_222-b10)
OpenJDK 64-Bit Server VM (build 25.222-b10, mixed mode)
输入exit
命令可以停止openjdk:8容器。
如果我们以openjdk:8为基础镜像来创建一个新的镜像,那么需要在之前的helloworld
目录,即我们的java项目所在的目录下创建一个文本文件,命名为Dockerfile
。其内容如下:
FROM openjdk:8
COPY target/helloworld-1.0-SNAPSHOT.jar /usr/src/helloworld-1.0-SNAPSHOT.jar
CMD java -cp /usr/src/helloworld-1.0-SNAPSHOT.jar org.examples.java.App
第一行,FROM openjdk:8
的作用是以openjdk:8作为基础镜像。因此新创建的镜像将包含openjdk:8的所有功能。
第二行,COPY target/helloworld-1.0-SNAPSHOT.jar /usr/src/helloworld-1.0-SNAPSHOT.jar
,它的作用是进行一个文件拷贝。拷贝哪个文件呢,就是我们使用mvn package
命令编译java程序后所生成的jar文件。具体来说,就是把当前主机上的jar文件target/helloworld-1.0-SNAPSHOT.jar
,拷贝到docker镜像的目录/usr/src/
中。
第三行,CMD java -cp /usr/src/helloworld-1.0-SNAPSHOT.jar org.examples.java.App
,作用是当启动一个容器来运行镜像时,执行命令java -cp /usr/src/helloworld-1.0-SNAPSHOT.jar org.examples.java.App
来启动这个java程序。
有了Dockerfile,就可以使用下面的命令来创建镜像了:
docker image build -t hello-java .
不要忘了上述命令末尾的小数点,它表示当前目录,这样docker就会在当前目录中查找Dockerfile文件,并根据Dockerfile的描述来创建镜像。-t hello-java
表示把新的镜像命名为hello-java。
上述命令会得到类似下面的输出信息:
Sending build context to Docker daemon 50.69kB
Step 1/3 : FROM openjdk:8
---> 27da2af61908
Step 2/3 : COPY target/helloworld-1.0-SNAPSHOT.jar /usr/src/helloworld-1.0-SNAPSHOT.jar
---> cb30cc329413
Step 3/3 : CMD java -cp /usr/src/helloworld-1.0-SNAPSHOT.jar org.examples.java.App
---> Running in 9b85e8fbd443
Removing intermediate container 9b85e8fbd443
---> df751388fd1b
Successfully built df751388fd1b
Successfully tagged hello-java:latest
想要运行这个镜像也很简单,只需要执行下面的命令:
docker container run hello-java
容器运行后会输出字符串:
Hello World!
使用Docker Maven Plugin
maven不但可以编译java程序,也可以用来创建docker镜像。利用docker-maven-plugin插件可以在编译java程序的同时自动创建镜像,这样省去了创建Dockerfile并输入docker命令行的步骤,显得更加方便。
为此,需要修改java项目中的pom.xml文件,如下所示:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.examples.java</groupId>
<artifactId>helloworld</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>helloworld</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>docker</id>
<build>
<plugins>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.31.0</version>
<configuration>
<images>
<image>
<name>hello-java</name>
<build>
<from>openjdk:8</from>
<assembly>
<descriptorRef>artifact</descriptorRef>
</assembly>
<cmd>java -cp maven/${project.name}-${project.version}.jar org.examples.java.App</cmd>
</build>
<run>
<wait>
<log>Hello World!</log>
</wait>
</run>
</image>
</images>
</configuration>
<executions>
<execution>
<id>docker:build</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
<execution>
<id>docker:start</id>
<phase>install</phase>
<goals>
<goal>run</goal>
<goal>logs</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
关于maven-docker-plugin,截止笔者发布此博客时,其最新的版本是0.31.0。读者可以在maven repository网站上查询这个plugin的发布版本号。在插件的github主页有更加详细的使用说明。
在我们的示例中用到了这么几个maven goals:
- docker:build 编译并创建docker镜像。
要使这个maven goal 命令正常工作,需要在pom的<configuration>
中添加<build>
元素来定义如何创建docker镜像。示例中定义的<build>
元素如下:
<build>
<from>openjdk:8</from>
<assembly>
<descriptorRef>artifact</descriptorRef>
</assembly>
<cmd>java -cp maven/${project.name}-${project.version}.jar org.examples.java.App</cmd>
</build>
<from>
表示基础镜像为openjdk:8。<assembly>
元素用来定义镜像需要包含的文件,即我们需要把哪些文件拷贝到新创建的镜像之中。一个简单的配置就是使用<descriptorRef>artifact</descriptorRef>
,这样就可以把编译后的jar文件拷贝到镜像中。<cmd>
元素用来定义容器运行时执行的命令行,本例执行一个java命令来运行我们的java程序。
- docker:start 和 docker:run 创建并运行docker容器。
在<configuration>
中添加<run>
元素,可以对容器的运行方式进行配置。示例中定义的<run>
元素如下:
<run>
<wait>
<log>Hello World!</log>
</wait>
</run>
这里的<wait>
用来阻塞maven命令的执行,直到检测到容器输出日志"Hello World!"时,再恢复maven命令的执行过程。
- docker:logs 把容器输出的日志打印到当前的命令行窗口中。
接下来,使用命令mvn -Pdocker package
编译java项目,并且创建docker镜像。这个命令会输出类似下面的日志:
[INFO] Copying files to C:\Users\i062893\OneDrive\code\docker-workspace\docker-java\helloworld\target\docker\hello-java\build\maven
[INFO] Building tar: C:\Users\i062893\OneDrive\code\docker-workspace\docker-java\helloworld\target\docker\hello-java\tmp\docker-build.tar
[INFO] DOCKER> [hello-java:latest]: Created docker-build.tar in 155 milliseconds
[INFO] DOCKER> [hello-java:latest]: Built image sha256:cf28b
[INFO] DOCKER> [hello-java:latest]: Removed old image sha256:df751
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
注意这三行以"DOCKER>"开头的日志了吗?这说明hello-java:latest镜像已经创建成功了。使用docker image ls
来看一下本机的镜像列表。在列表中可以发现hello-java镜像:
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-java latest cf28b4b83a26 2 minutes ago 488MB
我们也可以使用mvn -Pdocker install
命令来编译java项目,创建镜像,并启动一个容器来运行这个镜像。该命令输出的日志类似下面这样:
[INFO] --- docker-maven-plugin:0.31.0:run (docker:start) @ helloworld ---
[INFO] DOCKER> [hello-java:latest]: Start container f8deb08d2692
[INFO] DOCKER> Pattern 'Hello World!' matched for container f8deb08d2692
f8deb0> Hello World!
[INFO] DOCKER> [hello-java:latest]: Waited on log out 'Hello World!' 674 ms
读者可以看到,hello-java容器已经启动,并且输出了字符串Hello World!。此时按下Ctrl + C
可以停止这个容器。
至此,我们的容器化JAVA程序开发指南就告一段落了。在最后,留给大家两个思考题。
-
笔者介绍了两种打包docker镜像的方法,一种是使用Dockerfile,另一种是使用maven插件maven-docker-plugin。这两种方法都会把新创建的镜像保存在本机的镜像仓库中。但是怎样才能把本机的镜像发布出去,供其他用户安装和使用呢?
-
笔者创建了一个JAVA程序,并且将其打包到基于JDK 8的docker镜像中了,这意味着我们的JAVA程序不能包含JDK 9及以上版本的新功能。那么如果想要使用JDK 12的新API和新的java语法,那该怎么做呢?
读者朋友如果知道答案的话,欢迎大家在博客下方留言。笔者将在下一篇博客中为大家揭晓答案。谢谢大家。
网友评论