如何利用Docker虛擬化JDK12的Java程序

上一篇 / 目錄 / 下一篇

筆者在《如何開發(fā)容器化的Java程序》中創(chuàng)建了一個(gè)Java程序,并且將其打包到基于JDK8的docker鏡像中。然而在文章末尾有兩個(gè)問題還沒有解答:

  1. 怎樣才能把本機(jī)的鏡像發(fā)布出去,供其他用戶安裝和使用呢?
  2. 如何使用Docker虛擬化一個(gè)基于JDK12的Java程序呢?

下面我們就一起來看一下具體的操作步驟吧。

1 修改Dockerfile

如果Java程序想運(yùn)行在JDK12的JVM上,那么最簡單的方法就是修改Dockerfile,將基礎(chǔ)鏡像從openjdk:8 修改為openjdk:12 (或openjdk:latest) 即可。openjdk:12的鏡像包含了一個(gè)JDK12的JVM,基于此為基礎(chǔ)鏡像創(chuàng)建的所有鏡像自然也就包含了JDK12。當(dāng)然,所有openjdk提供的docker鏡像版本都可以在其github文檔中查詢到,網(wǎng)址是:https://github.com/docker-library/docs/blob/master/openjdk/README.md
讀者們也可以從中選擇一種作為基礎(chǔ)鏡像。比如,我們訪問上述鏈接,可以看到很多openjdk的鏡像版本:

openjdk docker image的github網(wǎng)頁

往下滑動(dòng)頁面,可以找到JDK12的版本,如下圖所示。可以看出,openjdk:12, openjdk:jdk, openjdk:latest都是相同的鏡像,均包含JDK12.

包含JDK12的版本

不過,筆者還將介紹另外一種創(chuàng)建Java鏡像的方法,那就是自己動(dòng)手拷貝一個(gè)JDK 12到鏡像中。

2 拷貝JDK12到鏡像中

到OpenJDK 的官網(wǎng)下載OpenJDK 12 for Linux 版本,網(wǎng)址是: http://jdk.java.net/12/

OpenJDK下載頁面

下載的文件名為:openjdk-12.0.2_linux-x64_bin.tar.gz

接下來創(chuàng)建目錄docker-jdk12用來保存Java代碼:

mkdir docker-jdk12
cd docker-jdk12

將剛才下載的openjdk 12 拷貝到docker-jdk12目錄中。

然后創(chuàng)建一個(gè)Dockerfile文件,文件名為jdk-12-debian-slim.Dockerfile,文件包含下面的內(nèi)容:

# A JDK 12 with Debian slim
FROM debian:stable-slim
# Add openjdk12 to folder /opt
ADD openjdk-12.0.2_linux-x64_bin.tar.gz /opt
# Set up env variables
ENV JAVA_HOME=/opt/jdk-12.0.2
ENV PATH=$PATH:$JAVA_HOME/bin
CMD ["jshell"]

這里我們使用的基礎(chǔ)鏡像是debian:stable-slim。debian是一個(gè)免費(fèi)的Linux操作系統(tǒng),stable-slim版本是debian的穩(wěn)定版,并且鏡像的容量進(jìn)行了精簡。在Docker Hub網(wǎng)站上可以看到,debian:stable-slim的鏡像大小只有25.84MB:

Docker Hub上顯示的debian:stable-slim

第二行腳本ADD openjdk-12.0.2_linux-x64_bin.tar.gz /opt,使用ADD命令把當(dāng)前目錄中的openjdk 12 壓縮包解壓到鏡像的/opt目錄下。網(wǎng)上有很多文章介紹ADD和COPY命令的區(qū)別,其實(shí)筆者總結(jié)了一條最簡單的區(qū)別:COPY正如我們在windows CMD中的copy指令一樣,功能是文件的拷貝;而ADD則會(huì)把壓縮包的內(nèi)容解壓到鏡像中。

接下來的腳本則是設(shè)置了JAVA_HOMEPATH環(huán)境變量。熟悉Java開發(fā)的讀者對(duì)此應(yīng)該都比較熟悉。

最后,則是運(yùn)行命令jshelljshell命令是JDK9新增加的一個(gè)功能,它提供了交互式的Java代碼運(yùn)行環(huán)境。

使用下面的命令創(chuàng)建這個(gè)鏡像,為新的鏡像命名為jdk-12-debian-slim:

docker image build -t jdk-12-debian-slim -f jdk-12-debian-slim.Dockerfile .

如果成功,則可以看到類似下面這樣的日志輸出:

Sending build context to Docker daemon  198.2MB
Step 1/5 : FROM debian:stable-slim
 ---> a4eb8e3265f8
Step 2/5 : ADD openjdk-12.0.2_linux-x64_bin.tar.gz /opt
 ---> Using cache
 ---> ba5157a8c5e5
Step 3/5 : ENV JAVA_HOME=/opt/jdk-12.0.2
 ---> Using cache
 ---> d2f758a76f4a
Step 4/5 : ENV PATH=$PATH:$JAVA_HOME/bin
 ---> Using cache
 ---> 7cc00f2a4013
Step 5/5 : CMD ["jshell"]
 ---> Using cache
 ---> 754ef0950e57
Successfully built 754ef0950e57
Successfully tagged jdk-12-debian-slim:latest

使用docker image ls看一下新創(chuàng)建的鏡像:

REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
jdk-12-debian-slim        latest              8866af1a6786        16 minutes ago      404MB

然后可以使用下面的命令來運(yùn)行這個(gè)鏡像。其中-m=200M參數(shù)的作用是給容器分配200MB的內(nèi)存空間。-it表示以交互模式啟動(dòng)容器。--rm表示當(dāng)容器停止時(shí),自動(dòng)將該容器刪除。

docker container run -m=200M -it --rm jdk-12-debian-slim

容器啟動(dòng)后,你會(huì)看到如下的輸出:

Sep 07, 2019 9:00:57 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
|  Welcome to JShell -- Version 12.0.2
|  For an introduction type: /help intro

jshell>

此時(shí)我們已經(jīng)啟動(dòng)了一個(gè)jdk-12-debian-slim鏡像容器,并且進(jìn)入了jshell交互程序。我們輸入一行Java語句,看看能否執(zhí)行成功:

jshell> System.out.println("Hello World!");
Hello World!

jshell>

如果想退出容器,可以直接按Ctrl + D

3 Hello World程序

下面來創(chuàng)建一個(gè)簡單的Hello World程序。

首先新建一個(gè)目錄helloworld-java-12存放我們的程序:

mkdir helloworld-java-12
cd helloworld-java-12

然后在該目錄中新建pom.xml文件,內(nèi)容如下:

<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-java-12</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>helloworld-java-12</name>
  <url>http://maven.apache.org</url>
  <properties>
    <maven.compiler.target>12</maven.compiler.target>
    <maven.compiler.source>12</maven.compiler.source>
  </properties>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

接下來新建目錄src\main\java\org\examples\java

mkdir src\main\java\org\examples\java

src/main/java/org/examples/java 目錄中新建App.java文件,文件內(nèi)容如下:

package org.examples.java;
public class App
{
  public static void main( String[] args )
  {
    System.out.println( "Hello World!" );
  }
}

最后回到helloworld-java-12目錄,輸入mvn package進(jìn)行編譯。

mvn package

編譯成功后在target目錄下會(huì)生成helloworld-java-12-1.0-SNAPSHOT.jar

輸入下面的命令啟動(dòng)helloworld程序:

java -classpath target\helloworld-java-12-1.0-SNAPSHOT.jar org.examples.java.App

如果運(yùn)行成功,你會(huì)看到日志輸出:

Hello World!

4 創(chuàng)建Hello World的Docker鏡像

編譯好了上述Java程序之后,我們就可以把它打包成一個(gè)Docker鏡像了。為此,這里需要對(duì)之前的jdk-12-debian-slim.Dockerfile內(nèi)容稍作修改,步驟如下:

把openjdk-12的壓縮包openjdk-12.0.2_linux-x64_bin.tar.gz拷貝到helloworld-java-12目錄下。

helloworld-java-12目錄下新建一個(gè)名為Dockerfile的文本文件,內(nèi)容如下:

FROM debian:stable-slim
ADD openjdk-12.0.2_linux-x64_bin.tar.gz /opt
COPY target/helloworld-java-12-1.0-SNAPSHOT.jar /opt/helloworld/helloworld-java-12-1.0-SNAPSHOT.jar
ENV JAVA_HOME=/opt/jdk-12.0.2
ENV PATH=$PATH:$JAVA_HOME/bin
CMD java -cp /opt/helloworld/helloworld-java-12-1.0-SNAPSHOT.jar org.examples.java.App

這個(gè)Dockerfile內(nèi)容和jdk-12-debian-slim.Dockerfile內(nèi)容差不多,這里不再贅述。

輸入下面的命令創(chuàng)建鏡像:

docker image build -t helloworld-jdk-12 .

最后啟動(dòng)這個(gè)鏡像容器試試看:

docker container run --rm helloworld-jdk-12

你會(huì)看到如下的日志輸出:

Hello World!

至此,我們成功的創(chuàng)建了一個(gè)基于JDK12 的Java程序鏡像。不過稍等,我們離成功還差最后一步。因?yàn)槲覀冞€需要進(jìn)一步裁剪鏡像的大小。

5 進(jìn)一步裁剪鏡像的大小

如果我們通過docker image ls命令查看一下剛創(chuàng)建的鏡像,可以看出這個(gè)鏡像文件的大小達(dá)到了驚人的404MB

REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
helloworld-jdk-12         latest              9a766a690d43        2 minutes ago       404MB

雖然我們使用了精簡過的Linux鏡像debian:stable-slim作為基礎(chǔ)鏡像,使得大小比直接用openjdk:12要小一些,但是對(duì)于一個(gè)簡單的Hello World程序,占用404MB的空間仍然是不可接收的一件事。

為此 ,我們可以對(duì)JVM runtime進(jìn)行裁剪。JVM runtime包含了很多的模塊,但是我們的Hello World程序僅僅使用了java.base這一個(gè)模塊,為什么不裁剪出一個(gè)輕量級(jí)的JVM呢?這樣我們的docker鏡像的文件尺寸會(huì)小很多。

首先打開powershell,進(jìn)入我們之前創(chuàng)建的helloworld-java-12目錄。還記得嗎,在該目錄下我們存放了一個(gè)linux平臺(tái)的openjdk-12: 文件名是openjdk-12.0.2_linux-x64_bin.tar.gz

openjdk-12的壓縮包

它是一個(gè)tar.gz壓縮包,使用tar -xf .\openjdk-12.0.2_linux-x64_bin.tar.gz命令將其解壓縮到當(dāng)前目錄,會(huì)生成一個(gè)新的目錄jdk-12.0.2:

解壓后的目錄jdk-12.0.2

讀者也可以使用其他的解壓縮工具,比如Windows中最常用的WinRAR或7-zip。

下面使用jlink命令創(chuàng)建一個(gè)裁剪過的JVM,使其只包含java.base模塊。生成的JVM將保存到目錄target\openjdk-12-base_linux-x64中。

jlink --compress 2 --no-header-files --verbose --module-path .\jdk-12.0.2\jmods --output .\target\openjdk-12-base_linux-x64 --add-modules java.base

然后修改Dockerfile,新的Dockerfile內(nèi)容如下:

# Hello world application with custom Java runtime
FROM debian:stable-slim
COPY target/openjdk-12-base_linux-x64 /opt/jdk-12.0.2
COPY target/helloworld-java-12-1.0-SNAPSHOT.jar /opt/helloworld/helloworld-java-12-1.0-SNAPSHOT.jar
ENV JAVA_HOME=/opt/jdk-12.0.2
ENV PATH=$PATH:$JAVA_HOME/bin
CMD java -cp /opt/helloworld/helloworld-java-12-1.0-SNAPSHOT.jar org.examples.java.App

注意,和之前的Dockerfile相比,我們只是修改了一行命令,把ADD openjdk-12.0.2_linux-x64_bin.tar.gz /opt 替換成了COPY target/openjdk-12-base_linux-x64 /opt/jdk-12.0.2, 這樣,我們拷貝到鏡像中的JVM不再是完整的JVM,而是經(jīng)過裁剪后的輕量級(jí)JVM。

使用docker命令創(chuàng)建新的鏡像。

docker image build -t helloworld-jdk-12-base .

最后執(zhí)行docker image ls看看新創(chuàng)建的鏡像信息

REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
helloworld-jdk-12-base    latest              6bd8f688f404        36 seconds ago      106MB
helloworld-jdk-12         latest              9a766a690d43        3 days ago          404MB

效果很明顯。經(jīng)過裁剪,helloworld-jdk-12-base鏡像的大小比helloworld-jdk-12要小很多,只有106MB,是原始大小的26%。

使用docker container run --rm helloworld-jdk-12-base,仍然可以運(yùn)行這個(gè)新的鏡像容器。

運(yùn)行Hello World容器

6 導(dǎo)出本地的鏡像

如果按照上面的步驟操作,那么已經(jīng)在本地的docker鏡像倉庫中創(chuàng)建了一個(gè)鏡像,名稱為helloworld-jdk-12-base:latest。假如想把這個(gè)鏡像發(fā)布給其他的用戶,有兩種辦法可以實(shí)現(xiàn)。

6.1 發(fā)布到Docker Hub

Docker Hub是Docker官方維護(hù)的一個(gè)鏡像倉庫。每個(gè)人都可以免費(fèi)申請一個(gè)Docker Hub賬號(hào),這樣就可以把我們創(chuàng)建的鏡像發(fā)布到Docker Hub賬號(hào)所對(duì)應(yīng)的鏡像倉庫中。任何其他用戶,只要能連上Docker Hub,就可以下載我們發(fā)布過的鏡像并安裝運(yùn)行鏡像中的程序了。

方法是先登錄到Docker Hub:

docker login

根據(jù)提示輸入用戶名和密碼。例如:

Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: feiandytan
Password:
Login Succeeded

然后輸入下面的命令給鏡像制作一個(gè)新的標(biāo)簽:

docker tag helloworld-jdk-12-base [docker-id]/helloworld-jdk-12-base

讀者們需要把[docker-id]替換成你自己的Docker ID,例如:

docker tag helloworld-jdk-12-base feiandytan/helloworld-jdk-12-base

完整的標(biāo)簽格式是:[Docker ID]/[repository]:[tag]

其中,[Docker ID]/[repositroy] 表明鏡像會(huì)上傳到你ID中的某個(gè)鏡像倉庫中。

使用docker push來上傳鏡像。

docker push [docker-id]/helloworld-jdk-12-base

這里[docker-id]需要替換成你自己的docker id。例如:

docker push feiandytan/helloworld-jdk-12-base

最后,在用戶的機(jī)器上,只要輸入docker container run --rm feiandytan/helloworld-jdk-12-base就可以自動(dòng)從Docker Hub下載我們的鏡像文件了。

6.2 保存為tar文件

讀者也可以使用下面的命令把本機(jī)鏡像倉庫中的鏡像另存為一個(gè)tar文件,然后把這個(gè)文件放在網(wǎng)站上給用戶下載:

docker image save helloworld-jdk-12-base > helloworld-jdk-12-base.tar

用戶下載了tar文件后,使用下面的命令可以把它導(dǎo)入到本地鏡像倉庫中:

docker image load -i helloworld-jdk-12-base.tar

上一篇 / 目錄 / 下一篇

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容