上一篇 / 目录 / 下一篇
笔者在《如何开发容器化的Java程序》中创建了一个Java程序,并且将其打包到基于JDK8的docker镜像中。然而在文章末尾有两个问题还没有解答:
- 怎样才能把本机的镜像发布出去,供其他用户安装和使用呢?
- 如何使用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的镜像版本:
往下滑动页面,可以找到JDK12的版本,如下图所示。可以看出,openjdk:12, openjdk:jdk, openjdk:latest都是相同的镜像,均包含JDK12.
不过,笔者还将介绍另外一种创建Java镜像的方法,那就是自己动手拷贝一个JDK 12到镜像中。
2 拷贝JDK12到镜像中
到OpenJDK 的官网下载OpenJDK 12 for Linux 版本,网址是: http://jdk.java.net/12/
下载的文件名为: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:
第二行脚本ADD openjdk-12.0.2_linux-x64_bin.tar.gz /opt
,使用ADD命令把当前目录中的openjdk 12 压缩包解压到镜像的/opt
目录下。网上有很多文章介绍ADD和COPY命令的区别,其实笔者总结了一条最简单的区别:COPY正如我们在windows CMD中的copy指令一样,功能是文件的拷贝;而ADD则会把压缩包的内容解压到镜像中。
接下来的脚本则是设置了JAVA_HOME
和PATH
环境变量。熟悉Java开发的读者对此应该都比较熟悉。
最后,则是运行命令jshell
。jshell
命令是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
。
它是一个tar.gz压缩包,使用tar -xf .\openjdk-12.0.2_linux-x64_bin.tar.gz
命令将其解压缩到当前目录,会生成一个新的目录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
,仍然可以运行这个新的镜像容器。
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
上一篇 / 目录 / 下一篇