如何利用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文件,内容如下:

<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文件,文件内容如下:

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

上一篇 / 目录 / 下一篇

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容