将 1.47G 的Docker镜像压缩到 600M

抛开场景聊需求都是耍流氓

在目前网络环境这么好的情况下,大文件传输似乎已经没有什么问题了,动辄上G的文件传起来也不在话下,在这个场景下,再大的镜像体积,看起来也没什么问题。我这个镜像体积也不算过分,但是需要通过微信传输 - SVN推送 - SVN下载 - 镜像推送 - K8S 部署这几个步骤,一整套下来也确实挺浪费时间的,而且都是通过公网传输,在没有办法改变流程的情况下(客户要求,无解),尽量缩小镜像体积,会提高很大的效率。

早期的镜像

最早的镜像和最新的镜像体积差别,<none>是不断更新过程中被覆盖的

file

可以先用 docker history 命令看下镜像的构建步骤,可以简单理解成构建了多少层,这是原来刚开始构建镜像的时候写的Dockerfile,在构建过程中,步骤达到了30+,很多RUN操作虽然不会增加体积,但是无形中增加了很多镜像的 layer ,积少成多,会影响构建的效率。

另外还有几十甚至是几百兆的体积增加,最好都尽量避免掉

早期镜像的history

file

现在的镜像

优化之后的镜像history

file

优化方法

由于 Docker 镜像构建,就像它的名字和Logo,码头、集装箱这种,构建镜像,就是不断的再往上叠放新的集装箱,而不改变下面的其他层,每一层都记录自己本身和上一层发生的变化,有点像 git的每一次提交,例如

#第一层
FROM ubuntu

#往上叠一层
RUN echo 1 > foo.txt

也就是这个特性,让已经构建好的层,不会再有变化,如果在其他层改变了原来的层的内容,体积也不会缩小

假如这个镜像构建出来是 100M

#第一层
FROM ubuntu

这个镜像也会是100M,不会因为删除了底层的内容而减少

#第一层
FROM ubuntu
#删除底层镜像中的文件
RUN rm -rf /usr

1. 合并 RUN 指令

每个 RUN 指令都会产生一个新的层,即使只是 cat 或者 ls 命令,所及尽量把需要运行的指令放到一起,例如,需要创建一些目录并修改所有者(因为客户需要容器以特定的用户运行)

#原来的操作,会导出新增很多无用的层
RUN mkdir -p /usr/local/tomcat
RUN mkdir -p /usr/local/jdk
RUN mkdir -p /var/lib/nginx
RUN chown -R xxx:xxx /usr/local/tomcat
RUN chown -R xxx:xxx /usr/local/jdk
RUN chown -R xxx:xxx /etc/nginx

# 修改后的操作
RUN mkdir -p /usr/local/tomcat && \
    mkdir -p /usr/local/jdk && \
    mkdir -p /var/lib/nginx && \
    chown -R xxx:xxx /usr/local/tomcat && \
    chown -R xxx:xxx /usr/local/jdk && \
    chown -R xxx:xxx /etc/nginx

2. 直接拷贝目录而不是拷贝压缩文件+解压

在之前的Dockerfile中,我是将部分代码、软件的压缩包COPY到镜像中,然后解压,最后删除,但是在之前的Docker特性中了解到,这样做拷贝到镜像中的压缩包的体积并不会减少,所以做如下修改

#修改前,拷贝、解压、最后删除
COPY apache-tomcat-9.0.70.tar.gz /opt/kakashine/
COPY jdk-8u202-linux-x64.tar.gz /opt/kakashine/
RUN tar -xzf /opt/kakashine/apache-tomcat-9.0.70.tar.gz -C /usr/local/tomcat --strip-components=1
RUN tar -xzf /opt/kakashine/jdk-8u202-linux-x64.tar.gz -C /usr/local/jdk --strip-components=1

#修改后,直接在构建目录中把文件解压,然后把目录复制进去,并且直接设置好owner
COPY --chown=sjclxxgl:sjclxxgl tomcat /usr/local/tomcat
COPY --chown=sjclxxgl:sjclxxgl jdk /usr/local/jdk

3. 谨慎对目录批量操作

在之前的dockerfile中,由于需要使用普通用户运行容器,所以在脚本最后,将很多目录用 chown 命令修改了所有人,导致 RUN chown 命令的构建会产生几百兆的体积变化。
因为按照上面所说的docker特性中,整个目录下的所有文件和上一层比较都发生了变化,在新的层中,会产生很大的文件体积差异,所以尽量避免这样做。

#可以先用 ROOT 用户创建好空目录,然后直接修改空目录的所有者,接着用普通用户去往文件夹里解压、拷贝,这样就避免了修改整个目录下所有文件的信息

#先用root用户做完所有要做的事情
RUN apt-get update && apt-get install -y nginx=1.18.0-0ubuntu1.4

# 先把需要的目录都创建好
RUN mkdir -p /usr/local/tomcat && \
    mkdir -p /usr/local/jdk && \
    mkdir -p /var/lib/nginx && \
    mkdir -p /var/log/nginx

# 创建非特权用户并指定UID和GID
RUN groupadd -r -g 1234 myuser && useradd -r -u 1234 -g myuser myuser

#把一些目录给普通用户授权
RUN chown -R myuser:myuser /usr/local/tomcat && \
    chown -R myuser:myuser /usr/local/jdk && \
    chown -R myuser:myuser /var/lib/nginx && \
    chown -R myuser:myuser /var/log/nginx && \

#切换非特权用户
USER myuser

# 剩下的命令
RUN xxx

一顿操作下来,镜像体积从1.47G缩小到了600M+,构建过程也从30+变成了17,都比以前少了接近一半,嗯,遥遥领先!

站内相关文章:

Comment ()
如果您有不同的看法,或者疑问,欢迎指教