书接 openEuler 系列文章(可以翻看测试系列),本次跟大家说说如何将 Java 包轻量化地构建到 openEuler 镜像中且保持镜像内操作系统是全补丁状态。
之前我们都是使用现成的 jdk 镜像进行构建的,如下图:
FROM ibm-semeru-runtimes:open-8u392-b08-jre-jammyVOLUME /tmp
ADD compress-example-0.0.1.jar /home
WORKDIR /home/
ENTRYPOINT ["java","-jar","compress-example-0.0.1.jar"]
这样构建的速度又快又轻量化,如下图:
yuanzhenhui@MacBook-Pro target % docker build -t compress-example1 .
[+] Building 2.3s (8/8) FINISHED docker:desktop-linux=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 542B 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [internal] load metadata for docker.io/library/ibm-semeru-runtimes:open-8u392-b08-jre-jammy 0.0s=> [internal] load build context 0.5s=> => transferring context: 50B 0.4s=> CACHED [1/3] FROM docker.io/library/ibm-semeru-runtimes:open-8u392-b08-jre-jammy 0.0s=> [2/3] ADD compress-example-0.0.1.jar /home 1.4s=> [3/3] WORKDIR /home/ 0.0s=> exporting to image 0.3s=> => exporting layers 0.2s=> => writing image sha256:2543f431ddd2bc33b1448711965bd376a5a1849034519da7d22eec34779d3851 0.0s=> => naming to docker.io/library/compress-example1 0.0sWhat's Next?View summary of image vulnerabilities and recommendations → docker scout quickview
最终结果是原始镜像 256MB,测试应用镜像299MB。如下图:
yuanzhenhui@MacBook-Pro target % docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
compress-example1 latest 2543f431ddd2 4 seconds ago 299MB
ibm-semeru-runtimes open-8u392-b08-jre-jammy 5dd0d62cb035 4 days ago 256MB
但奈何我们有业务方面有“信创”的要求,所有第三方软件都需要“国产化”。为此选择了华为的 openEuler 作为操作系统,镜像方面也是采用华为的 openEuler 为原始镜像。下载 openEuler 最新镜像(23.09)进行验证,发现镜像中并没有安装 JDK。于是我们在接下来的构建中其实还需要安装 JDK 并进行环境变量配置。如下图:
# 基础镜像
FROM openeuler/openeuler:23.09# 上传同级目录中的毕昇jdk到镜像根目录
ADD bisheng-jre-8u392-linux-x64.tar.gz .# 在镜像内执行系统更新,保证镜像内系统已安装最新补丁(这里采用连接符来进行串联处理能够有效减少构建体积)
RUN yum update -y && \
yum upgrade -y && \
yum install fontconfig -y && \
yum autoremove -y && \
mv bisheng-jre1.8.0_392 java# 设置jdk变量
ENV JAVA_HOME /java
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH .:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH $JAVA_HOME/bin:$PATH# 设置系统编码(这里可以参考我之前为 openEuler 写的测试系列文章,里面会有答案的)
ENV LC_ALL C.utf8
ENV LANG C.utf8# 挂载文件夹路径
VOLUME /tmp# 上传jar包到制定目录
ADD compress-example-0.0.1.jar /home# 指定工作目录
WORKDIR /home/# 执行语句
ENTRYPOINT ["java","-jar","compress-example-0.0.1.jar"]
如果你像我一样没有提前下载 openEuler 镜像,那么构建的时间将会再进一步延长,如下图:
yuanzhenhui@MacBook-Pro target % docker build -t compress-example2 .
[+] Building 61.1s (10/10) FINISHED docker:desktop-linux=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 1.08kB 0.0s=> [internal] load metadata for docker.io/openeuler/openeuler:23.09 0.0s=> CACHED [1/5] FROM docker.io/openeuler/openeuler:23.09 0.0s=> [internal] load build context 0.7s=> => transferring context: 46.93MB 0.7s=> [2/5] ADD bisheng-jre-8u392-linux-x64.tar.gz . 1.4s=> [3/5] RUN yum update -y && yum upgrade -y && yum install fontconfig -y && yum autoremove 57.3s=> [4/5] ADD compress-example-0.0.1.jar /home 0.3s => [5/5] WORKDIR /home/ 0.0s => exporting to image 1.2s => => exporting layers 1.2s => => writing image sha256:1276428318f6f92f25ef16b064af257cc6add2c82a471e351d0fd501b607f508 0.0s => => naming to docker.io/library/compress-example2 0.0s What's Next?View summary of image vulnerabilities and recommendations → docker scout quickview
这个构建速度的确是有点慢要差不多一分钟,大部分时间都耗费在 yum 更新的那些事情上面了。我们再看看这个体积大小,如下图:
yuanzhenhui@MacBook-Pro target % docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
compress-example2 latest 1276428318f6 3 minutes ago 614MB
compress-example1 latest 2543f431ddd2 27 minutes ago 299MB
ibm-semeru-runtimes open-8u392-b08-jre-jammy 5dd0d62cb035 4 days ago 256MB
openeuler/openeuler 23.09 09c854b5d453 2 months ago 210MB
这个测试应用镜像 2 竟然有 614MB,几乎是原镜像的 3 倍。究竟为什么会出现 3 倍的情况呢?由什么原因造成的呢?这个我们只需要使用 Docker Desktop 来看看就好了,如下图:
不得不说这个 Docker Desktop 真的非常好用。通过镜像分析就可以知道,多出来的体积一个是来自毕昇 jdk 的上传导致的,另外一个就是应用 jar 包的体积(出现风险先忽略不计哈),还有执行 RUN 指令后进行了系统更新这个也会产生新的体积。前两个是没有办法的啦。要用国产 jdk,又要上传应用 jar ,难道不用么?那么能够压缩的就只能是 yum 更新的这部分内容了。还有右侧告诉了我们究竟更新了什么内容,我们可以将不要的 package 摘录出来,在构建的时候用 yum remove 删除掉就好。为此,我们先运行一下 openeuler/openeuler:23.09 原始镜像,如下图:
yuanzhenhui@MacBook-Pro target % docker run -it openeuler/openeuler:23.09 /bin/bash
接着使用 yum list installed 命令看看有哪些已安装但没有用的包,这个过程比较漫长就不再文章里面说明了,因为这涉及到验证(毕竟怕删错了一些依赖组件)。最终的Dockerfile 如下所示:
# 基础镜像
FROM openeuler/openeuler:23.09# 上传同级目录中的毕昇jdk到镜像根目录
ADD bisheng-jre-8u392-linux-x64.tar.gz .# 在镜像内执行系统更新,保证镜像内系统已安装最新补丁(由于镜像只做 java web 的部署和使用,这里可以将一下不需要的调试包和工具删除)
RUN yum update -y && \
yum upgrade -y && \
yum install fontconfig -y && \
yum remove vim-minimal -y && \
yum remove tar -y && \
yum remove bc -y && \
yum remove gdb-gdbserver -y && \
yum autoremove -y && \
yum clean all && \
mv bisheng-jre1.8.0_392 java# 设置jdk变量
ENV JAVA_HOME /java
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH .:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH $JAVA_HOME/bin:$PATH# 设置系统编码
ENV LC_ALL C.utf8
ENV LANG C.utf8# 挂载文件夹路径
VOLUME /tmp# 上传jar包到制定目录
ADD compress-example-0.0.1.jar /home# 指定工作目录
WORKDIR /home/# 执行语句
ENTRYPOINT ["java","-jar","compress-example-0.0.1.jar"]
得到的最终结果是这样的,如下图:
yuanzhenhui@MacBook-Pro target % docker build -t compress-example3 .
[+] Building 52.6s (10/10) FINISHED docker:desktop-linux=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 1.21kB 0.0s=> [internal] load metadata for docker.io/openeuler/openeuler:23.09 0.0s=> [1/5] FROM docker.io/openeuler/openeuler:23.09 0.0s=> [internal] load build context 0.0s=> => transferring context: 246B 0.0s=> CACHED [2/5] ADD bisheng-jre-8u392-linux-x64.tar.gz . 0.0s=> [3/5] RUN yum update -y && yum upgrade -y && yum install fontconfig -y && yum remove vim-minimal -y && yum remove tar -y && yum remove bc -y && yum remove gdb-gdbserver -y && 51.4s=> [4/5] ADD compress-example-0.0.1.jar /home 0.1s=> [5/5] WORKDIR /home/ 0.0s => exporting to image 0.9s => => exporting layers 0.9s => => writing image sha256:27c6709bae124f79cdabb6302369216c0bf04e4d1410f4034c587f72b9fd3f5a 0.0s => => naming to docker.io/library/compress-example3 0.0s What's Next?View summary of image vulnerabilities and recommendations → docker scout quickview
构建的时间比之前的还少了几秒,最后看看这几次构建的结果,如下图:
yuanzhenhui@MacBook-Pro target % docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
compress-example3 latest 27c6709bae12 About a minute ago 539MB
compress-example2 latest 1276428318f6 2 hours ago 614MB
compress-example1 latest 2543f431ddd2 2 hours ago 299MB
测试应用 3 镜像比测试应用 2 镜像体积要更小了。
至此,原生的 Docker 镜像压缩方案完成。
那么为什么要做镜像压缩呢?其实之前一直没有留意到镜像太大的问题(可能之前大部分时间都是直接拿 Docker hub 里 OpenJDK 厂家做好的镜像来构建,出来体积不会太大因此在转“信创”业务线之后也一直没有留意),直到运维那边反馈说应用包太大导致每次发布拉取的时间极长。后来到 UAT 服务器上看了一下。如下图:
REPOSITORY TAG IMAGE ID CREATED SIZE
xxx/uat/chain-evaluate 12151627 e1808f7ff397 5 days ago 1.72GB
xxx/uat/chain-service 12151626 9078e026a429 5 days ago 1.72GB
好家伙,每个镜像都几乎有 1.5 GB 以上。除了网络小水管有问题外,日后镜像私库也会告急。这…就只能想办法来压缩一下。
其实最好的做法就是看看有没有第三方的工具直接对成品镜像进行压缩。
您可别说还真有,网上比较火的就是 docker-slim(slimtoolkit)和 docker-squash 两款工具。经实测,两款工具是真的强大。唯一的缺点就是配置参数也有点多了(其实 docker-squash 还可以但 docker-slim 就…),像我这种想“开(偷)箱(懒)即(省)用(事)”的人来说还是有点麻烦。
为此就选择在构建的时候去场景进行精简处理,毕竟 Dockerfile 还是挺灵活的,指令用起来就像直接操作系统一样。最最最重要的一点是,用第三方工具一不留神有可能连关键功能都给你“嘎”掉,而你当时是不知道的,到上生产之后出现故障了才发现,又要排查一段时间…这些都是次生风险。与其这样,还不如老老实实从自己清晰的方向出发吧。