參考資料:https://github.com/docker/labs/tree/master/developer-tools/java/
如果讀者朋友閱讀了我之前的文章《Docker入門詳解》壁拉,那么大家應(yīng)該對Docker已經(jīng)有了一個基本的了解,也理解了容器化程序的概念,以及容器化為軟件行業(yè)特別是云計(jì)算領(lǐng)域的軟件行業(yè)帶來的便利性嘀粱。接下來浸遗,我將為大家介紹如何使用Docker來開發(fā)容器化的Java程序。
創(chuàng)建基于JDK 8的Docker鏡像
這里筆者將介紹如何創(chuàng)建基于JDK 8的Docker鏡像。首先使用下面的maven命令創(chuàng)建一個java項(xiàng)目帚湘。
mvn archetype:generate -DgroupId=org.examples.java -DartifactId=helloworld -DinteractiveMode=false
上述maven命令會在helloworld目錄中創(chuàng)建一個最簡單的java程序军洼。
我們使用mvn package
來進(jìn)行編譯巩螃。
cd helloworld
mvn package
編譯成功后,會在target目錄下生成打包好的jar文件匕争。
接下來使用java
命令來運(yùn)行這個程序避乏。
java -cp target\helloworld-1.0-SNAPSHOT.jar org.examples.java.App
讀者會看到該程序輸出一行字符串:
Hello World!
但是,如果想要讓這個程序在其他用戶的電腦上正常的運(yùn)行甘桑,用戶還需要在電腦上安裝一個JRE拍皮。假如是更加復(fù)雜的程序,可能還需要其他的依賴軟件支持才能正常工作跑杭。借助docker铆帽,則可以把這些依賴的軟件都打包到docker鏡像中,這樣用戶想使用這個軟件時德谅,只需要下載這個鏡像就可以運(yùn)行了爹橱,不再需要安裝其他的依賴軟件。
下面我們就把這個helloworld程序打包到docker鏡像中窄做。我們準(zhǔn)備讓java程序在基于JDK 8的環(huán)境下運(yùn)行愧驱,因此基礎(chǔ)鏡像需要包含JDK 8慰技。這里我們使用的基礎(chǔ)鏡像是openjdk:8。運(yùn)行下面的命令可以啟動一個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為基礎(chǔ)鏡像來創(chuàng)建一個新的鏡像,那么需要在之前的helloworld
目錄糟红,即我們的java項(xiàng)目所在的目錄下創(chuàng)建一個文本文件手报,命名為Dockerfile
。其內(nèi)容如下:
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作為基礎(chǔ)鏡像掩蛤。因此新創(chuàng)建的鏡像將包含openjdk:8的所有功能。
第二行陈肛,COPY target/helloworld-1.0-SNAPSHOT.jar /usr/src/helloworld-1.0-SNAPSHOT.jar
揍鸟,它的作用是進(jìn)行一個文件拷貝【浜担拷貝哪個文件呢阳藻,就是我們使用mvn package
命令編譯java程序后所生成的jar文件。具體來說谈撒,就是把當(dāng)前主機(jī)上的jar文件target/helloworld-1.0-SNAPSHOT.jar
腥泥,拷貝到docker鏡像的目錄/usr/src/
中。
第三行啃匿,CMD java -cp /usr/src/helloworld-1.0-SNAPSHOT.jar org.examples.java.App
蛔外,作用是當(dāng)啟動一個容器來運(yùn)行鏡像時,執(zhí)行命令java -cp /usr/src/helloworld-1.0-SNAPSHOT.jar org.examples.java.App
來啟動這個java程序溯乒。
有了Dockerfile夹厌,就可以使用下面的命令來創(chuàng)建鏡像了:
docker image build -t hello-java .
不要忘了上述命令末尾的小數(shù)點(diǎn),它表示當(dāng)前目錄裆悄,這樣docker就會在當(dāng)前目錄中查找Dockerfile文件矛纹,并根據(jù)Dockerfile的描述來創(chuàng)建鏡像。-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
想要運(yùn)行這個鏡像也很簡單或南,只需要執(zhí)行下面的命令:
docker container run hello-java
容器運(yùn)行后會輸出字符串:
Hello World!
使用Docker Maven Plugin
maven不但可以編譯java程序,也可以用來創(chuàng)建docker鏡像艾君。利用docker-maven-plugin插件可以在編譯java程序的同時自動創(chuàng)建鏡像采够,這樣省去了創(chuàng)建Dockerfile并輸入docker命令行的步驟,顯得更加方便腻贰。
為此吁恍,需要修改java項(xiàng)目中的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>
關(guān)于maven-docker-plugin播演,截止筆者發(fā)布此博客時冀瓦,其最新的版本是0.31.0。讀者可以在maven repository網(wǎng)站上查詢這個plugin的發(fā)布版本號写烤。在插件的github主頁有更加詳細(xì)的使用說明翼闽。
在我們的示例中用到了這么幾個maven goals:
- docker:build 編譯并創(chuàng)建docker鏡像。
要使這個maven goal 命令正常工作洲炊,需要在pom的<configuration>
中添加<build>
元素來定義如何創(chuàng)建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>
表示基礎(chǔ)鏡像為openjdk:8。<assembly>
元素用來定義鏡像需要包含的文件暂衡,即我們需要把哪些文件拷貝到新創(chuàng)建的鏡像之中询微。一個簡單的配置就是使用<descriptorRef>artifact</descriptorRef>
,這樣就可以把編譯后的jar文件拷貝到鏡像中狂巢。<cmd>
元素用來定義容器運(yùn)行時執(zhí)行的命令行撑毛,本例執(zhí)行一個java命令來運(yùn)行我們的java程序。
- docker:start 和 docker:run 創(chuàng)建并運(yùn)行docker容器唧领。
在<configuration>
中添加<run>
元素藻雌,可以對容器的運(yùn)行方式進(jìn)行配置。示例中定義的<run>
元素如下:
<run>
<wait>
<log>Hello World!</log>
</wait>
</run>
這里的<wait>
用來阻塞maven命令的執(zhí)行斩个,直到檢測到容器輸出日志"Hello World!"時胯杭,再恢復(fù)maven命令的執(zhí)行過程。
- docker:logs 把容器輸出的日志打印到當(dāng)前的命令行窗口中受啥。
接下來做个,使用命令mvn -Pdocker package
編譯java項(xiàng)目,并且創(chuàng)建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鏡像已經(jīng)創(chuàng)建成功了。使用docker image ls
來看一下本機(jī)的鏡像列表核畴。在列表中可以發(fā)現(xiàn)hello-java鏡像:
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-java latest cf28b4b83a26 2 minutes ago 488MB
我們也可以使用mvn -Pdocker install
命令來編譯java項(xiàng)目膝但,創(chuàng)建鏡像,并啟動一個容器來運(yùn)行這個鏡像谤草。該命令輸出的日志類似下面這樣:
[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容器已經(jīng)啟動,并且輸出了字符串Hello World!丑孩。此時按下Ctrl + C
可以停止這個容器冀宴。
至此,我們的容器化JAVA程序開發(fā)指南就告一段落了温学。在最后略贮,留給大家兩個思考題。
筆者介紹了兩種打包docker鏡像的方法,一種是使用Dockerfile逃延,另一種是使用maven插件maven-docker-plugin览妖。這兩種方法都會把新創(chuàng)建的鏡像保存在本機(jī)的鏡像倉庫中。但是怎樣才能把本機(jī)的鏡像發(fā)布出去揽祥,供其他用戶安裝和使用呢讽膏?
筆者創(chuàng)建了一個JAVA程序,并且將其打包到基于JDK 8的docker鏡像中了拄丰,這意味著我們的JAVA程序不能包含JDK 9及以上版本的新功能府树。那么如果想要使用JDK 12的新API和新的java語法,那該怎么做呢料按?
讀者朋友如果知道答案的話奄侠,歡迎大家在博客下方留言。筆者將在下一篇博客中為大家揭曉答案载矿。謝謝大家遭铺。