如何利用Docker虚拟化JDK12的Java程序

上一篇 / 目录 / 下一篇

笔者在《如何开发容器化的Java程序》中创建了一个Java程序,并且将其打包到基于JDK8的docker镜像中。然而在文章末尾有两个问题还没有解答:

  1. 怎样才能把本机的镜像发布出去,供其他用户安装和使用呢?
  2. 如何使用Docker虚拟化一个基于JDK12的Java程序呢?

下面我们就一起来看一下具体的操作步骤吧。

1 修改Dockerfile

如果Java程序想运行在JDK12的JVM上,那么最简单的方法就是修改Dockerfile,将基础镜像从openjdk:8 修改为openjdk:12 (或openjdk:latest) 即可。openjdk:12的镜像包含了一个JDK12的JVM,基于此为基础镜像创建的所有镜像自然也就包含了JDK12。当然,所有openjdk提供的docker镜像版本都可以在其github文档中查询到,网址是:https://github.com/docker-library/docs/blob/master/openjdk/README.md
读者们也可以从中选择一种作为基础镜像。比如,我们访问上述链接,可以看到很多openjdk的镜像版本:

openjdk docker image的github网页

往下滑动页面,可以找到JDK12的版本,如下图所示。可以看出,openjdk:12, openjdk:jdk, openjdk:latest都是相同的镜像,均包含JDK12.

包含JDK12的版本

不过,笔者还将介绍另外一种创建Java镜像的方法,那就是自己动手拷贝一个JDK 12到镜像中。

2 拷贝JDK12到镜像中

到OpenJDK 的官网下载OpenJDK 12 for Linux 版本,网址是: http://jdk.java.net/12/

OpenJDK下载页面

下载的文件名为:openjdk-12.0.2_linux-x64_bin.tar.gz

接下来创建目录docker-jdk12用来保存Java代码:

mkdir docker-jdk12
cd docker-jdk12

将刚才下载的openjdk 12 拷贝到docker-jdk12目录中。

然后创建一个Dockerfile文件,文件名为jdk-12-debian-slim.Dockerfile,文件包含下面的内容:

# 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"]

这里我们使用的基础镜像是debian:stable-slim。debian是一个免费的Linux操作系统,stable-slim版本是debian的稳定版,并且镜像的容量进行了精简。在Docker Hub网站上可以看到,debian:stable-slim的镜像大小只有25.84MB:

Docker Hub上显示的debian:stable-slim

第二行脚本ADD openjdk-12.0.2_linux-x64_bin.tar.gz /opt,使用ADD命令把当前目录中的openjdk 12 压缩包解压到镜像的/opt目录下。网上有很多文章介绍ADD和COPY命令的区别,其实笔者总结了一条最简单的区别:COPY正如我们在windows CMD中的copy指令一样,功能是文件的拷贝;而ADD则会把压缩包的内容解压到镜像中。

接下来的脚本则是设置了JAVA_HOMEPATH环境变量。熟悉Java开发的读者对此应该都比较熟悉。

最后,则是运行命令jshelljshell命令是JDK9新增加的一个功能,它提供了交互式的Java代码运行环境。

使用下面的命令创建这个镜像,为新的镜像命名为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看一下新创建的镜像:

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

然后可以使用下面的命令来运行这个镜像。其中-m=200M参数的作用是给容器分配200MB的内存空间。-it表示以交互模式启动容器。--rm表示当容器停止时,自动将该容器删除。

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

容器启动后,你会看到如下的输出:

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>

此时我们已经启动了一个jdk-12-debian-slim镜像容器,并且进入了jshell交互程序。我们输入一行Java语句,看看能否执行成功:

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

jshell>

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

3 Hello World程序

下面来创建一个简单的Hello World程序。

首先新建一个目录helloworld-java-12存放我们的程序:

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

然后在该目录中新建pom.xml文件,内容如下:


  4.0.0
  org.examples.java
  helloworld-java-12
  jar
  1.0-SNAPSHOT
  helloworld-java-12
  http://maven.apache.org
  
    12
    12
  
  
    
      junit
      junit
      3.8.1
      test
    
  

接下来新建目录src\main\java\org\examples\java

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

src/main/java/org/examples/java 目录中新建App.java文件,文件内容如下:

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

最后回到helloworld-java-12目录,输入mvn package进行编译。

mvn package

编译成功后在target目录下会生成helloworld-java-12-1.0-SNAPSHOT.jar

输入下面的命令启动helloworld程序:

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

如果运行成功,你会看到日志输出:

Hello World!

4 创建Hello World的Docker镜像

编译好了上述Java程序之后,我们就可以把它打包成一个Docker镜像了。为此,这里需要对之前的jdk-12-debian-slim.Dockerfile内容稍作修改,步骤如下:

把openjdk-12的压缩包openjdk-12.0.2_linux-x64_bin.tar.gz拷贝到helloworld-java-12目录下。

helloworld-java-12目录下新建一个名为Dockerfile的文本文件,内容如下:

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

这个Dockerfile内容和jdk-12-debian-slim.Dockerfile内容差不多,这里不再赘述。

输入下面的命令创建镜像:

docker image build -t helloworld-jdk-12 .

最后启动这个镜像容器试试看:

docker container run --rm helloworld-jdk-12

你会看到如下的日志输出:

Hello World!

至此,我们成功的创建了一个基于JDK12 的Java程序镜像。不过稍等,我们离成功还差最后一步。因为我们还需要进一步裁剪镜像的大小。

5 进一步裁剪镜像的大小

如果我们通过docker image ls命令查看一下刚创建的镜像,可以看出这个镜像文件的大小达到了惊人的404MB

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

虽然我们使用了精简过的Linux镜像debian:stable-slim作为基础镜像,使得大小比直接用openjdk:12要小一些,但是对于一个简单的Hello World程序,占用404MB的空间仍然是不可接收的一件事。

为此 ,我们可以对JVM runtime进行裁剪。JVM runtime包含了很多的模块,但是我们的Hello World程序仅仅使用了java.base这一个模块,为什么不裁剪出一个轻量级的JVM呢?这样我们的docker镜像的文件尺寸会小很多。

首先打开powershell,进入我们之前创建的helloworld-java-12目录。还记得吗,在该目录下我们存放了一个linux平台的openjdk-12: 文件名是openjdk-12.0.2_linux-x64_bin.tar.gz

openjdk-12的压缩包

它是一个tar.gz压缩包,使用tar -xf .\openjdk-12.0.2_linux-x64_bin.tar.gz命令将其解压缩到当前目录,会生成一个新的目录jdk-12.0.2:

解压后的目录jdk-12.0.2

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

下面使用jlink命令创建一个裁剪过的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内容如下:

# 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,而是经过裁剪后的轻量级JVM。

使用docker命令创建新的镜像。

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

最后执行docker image ls看看新创建的镜像信息

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

效果很明显。经过裁剪,helloworld-jdk-12-base镜像的大小比helloworld-jdk-12要小很多,只有106MB,是原始大小的26%。

使用docker container run --rm helloworld-jdk-12-base,仍然可以运行这个新的镜像容器。

运行Hello World容器

6 导出本地的镜像

如果按照上面的步骤操作,那么已经在本地的docker镜像仓库中创建了一个镜像,名称为helloworld-jdk-12-base:latest。假如想把这个镜像发布给其他的用户,有两种办法可以实现。

6.1 发布到Docker Hub

Docker Hub是Docker官方维护的一个镜像仓库。每个人都可以免费申请一个Docker Hub账号,这样就可以把我们创建的镜像发布到Docker Hub账号所对应的镜像仓库中。任何其他用户,只要能连上Docker Hub,就可以下载我们发布过的镜像并安装运行镜像中的程序了。

方法是先登录到Docker Hub:

docker login

根据提示输入用户名和密码。例如:

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

然后输入下面的命令给镜像制作一个新的标签:

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

完整的标签格式是:[Docker ID]/[repository]:[tag]

其中,[Docker ID]/[repositroy] 表明镜像会上传到你ID中的某个镜像仓库中。

使用docker push来上传镜像。

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

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

docker push feiandytan/helloworld-jdk-12-base

最后,在用户的机器上,只要输入docker container run --rm feiandytan/helloworld-jdk-12-base就可以自动从Docker Hub下载我们的镜像文件了。

6.2 保存为tar文件

读者也可以使用下面的命令把本机镜像仓库中的镜像另存为一个tar文件,然后把这个文件放在网站上给用户下载:

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

用户下载了tar文件后,使用下面的命令可以把它导入到本地镜像仓库中:

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

上一篇 / 目录 / 下一篇

你可能感兴趣的:(如何利用Docker虚拟化JDK12的Java程序)