今天看到一篇文章,说是基础镜像‘Apline’ 有坑,闭坑,多阶段构建更小的镜像。 之前写《Spring组件之Eureka》 这篇文章的时候,介绍过如何使用把打包的java 的jar的包封装成一个docker 镜像。
今天看完文章后,再来个优化版。 让打好的镜像更小。
写在前面
在大多数的平台下,Java打包有两种大家熟悉的格式: JDK 和 JRE。
JRE 是 Java 运行时环境(Java Runtime Environment):包含了运行 Java 程序所需要的环境,即 JVM。
JDK 即 Java 开发工具包(Java Development Kit),既包含了 JRE,也包含了开发 Java 程序所需的工具,即 Java 编译器。
在JRE里环境里,就能跑jar的包,但是不能编译Java程序。
我们常用到的编译的基础镜像 都是JDK的,运行时的基础镜像是JRE的。 这样可以让镜像更小。
基于jar构建dockerFile
如果是单一的Java代码,我们可以分两个步骤,分阶段构建镜像。参见后面。
在这里,我们是基于Spring 框架构建的项目,包含了很多依赖关系。我们更倾向于用打完jar 后直接运行。
没优化前
之前步骤是,把基于Spring的项目,打包后,用 openjdk:8
作为基础镜像,封装我们的jar包。 dockerFile 文件如下:
FROM openjdk:8
COPY target/demo-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8761
CMD java -jar app.jar
原生的 openjdk:8
这个镜像可不小,有500M。
openjdk 8 0d54b885dc70 6 weeks ago 510MB
docker build 后,大小如下:
eurekaserverjdk v0.1 6a241b43b1d9 22 minutes ago 559MB
对于本地mac OS的SSD 来说,寸土寸金。我打算为镜像瘦身。
优化后
优化的方案也非常简单。我们的基础镜像选择openjdk:8-jre-alpine
这个镜像更小, 90M都不到。
openjdk 8-jre-alpine f7a292bbb70c 12 months ago 84.9MB
dockerFile 也很好写,把原来openjdk:8的基础镜像,替换为openjdk :8-jre-alpine。就可以了:
FROM openjdk:8-jre-alpine
COPY demo-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8761
CMD java -jar app.jar
我们build 一下。
docker build -f DockerfileLow -t eurekaserverjdkalpine .
Sending build context to Docker daemon 49.54MB
Step 1/4 : FROM openjdk:8-jre-alpine
---> f7a292bbb70c
Step 2/4 : COPY demo-0.0.1-SNAPSHOT.jar app.jar
---> 9537ce606603
Step 3/4 : EXPOSE 8761
---> Running in 33951dd86879
Removing intermediate container 33951dd86879
---> 6ff3c14caa3f
Step 4/4 : CMD java -jar app.jar
---> Running in 29180e188129
Removing intermediate container 29180e188129
---> 9142e469b396
Successfully built 9142e469b396
看看大小 这个镜像 134M ,也比原来小很多了。
eurekaserverjdkalpine latest 9142e469b396 22 minutes ago 134MB
基于Java Class 构建镜像
写在前面我们也说了,这里构建的镜像,够是基于jar构建的,那么我们写出来了Java的源码,可以在docker 里用JDK 编译,后运行吗?答案是肯定的。我们用jar 比较省事而已。
例如写一个数组循环,为了方便来演示,我们引入了一个String[]数据。 注意这个class文件不要加包名!
public class testDocker {
public static void main(String[] args) {
String[] worlds = {"I","love","world"};
for (String i : worlds){
System.out.println("-->" + i);
}
}
}
基础版
DockerFile 可以这么写,dockerFile 文件路径要和testDocker.java 在一个目录。
FROM openjdk:8
COPY testDocker.java .
RUN javac testDocker.java
FROM openjdk:8-jre
COPY --from=0 testDocker.class .
CMD exec java -cp . testDocker
build 一下,并 docker run .
$docker build -f DockerFile -t testdocker .
Sending build context to Docker daemon 10.75kB
Step 1/6 : FROM openjdk:8
---> 0d54b885dc70
Step 2/6 : COPY testDocker.java .
---> 636d06180d97
Step 3/6 : RUN javac testDocker.java
---> Running in 2084d3abd242
Removing intermediate container 2084d3abd242
---> 22e56e8de462
Step 4/6 : FROM openjdk:8-jre
---> 1c5f6cd937b5
Step 5/6 : COPY --from=0 testDocker.class .
---> 71409231c882
Step 6/6 : CMD exec java -cp . testDocker
---> Running in 39e4f8f37dee
Removing intermediate container 39e4f8f37dee
---> 7261f19915ef
Successfully built 7261f19915ef
Successfully tagged testdocker:latest
$docker run -ti testdocker
-->I
-->love
-->world
看下build 后的镜像,大概在265M 左右。
testdocker latest 7261f19915ef 3 minutes ago 265MB
优化版
又来用apline 这个多阶段加载了这个小镜像了。
DockerFile 这么写
FROM openjdk:8
COPY testDocker.java .
RUN javac testDocker.java
FROM openjdk:8-jre-alpine
COPY --from=0 testDocker.class .
CMD exec java -cp . testDocker
build 一下,run 一把也没问题。
dockerbuild -f dockerFileApline -t testdockerbaseapline .
docker run -ti testdockerbaseapline
-->I
-->love
-->world
···
镜像大小,不到90M,小的有点过分了啊。真激动,释放了不少磁盘空间。
```shell
testdockerbaseapline latest 594f58eba821 33 seconds ago 84.9MB
写在最后
总结,对于docker 镜像,坚持原则就是多阶段、最后用基础Apline 镜像构建。这样是最小的。
放一个github的一个地址,里面有各种语言版本的编译环境的DockerFile. 希望对你有帮助。
网友评论