镜像瘦身:每一层都不能放过

Posted 悟初境

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了镜像瘦身:每一层都不能放过相关的知识,希望对你有一定的参考价值。

网上很多人都说镜像瘦身需要把所有命令放在一条来执行,这没有错,但只是问题表象,没有触及本质。
当我打了一个带源码编译的镜像,结果异常庞大,明明已经删了源码包,为什么还那么大呢?当我想尝试放在一条命令时,简洁理论告诉我这非常不美观,我每个RUN都是一个不同的软件,放在一起不是很混乱吗?可读性变差了,所以一定不是这样的。

镜像瘦身

下面的镜像是为了升级centos7里面的openssl版本为1.1.1,从源码构建。
Dockerfile如下:

ARG BASE_IMAGE=centos:7.6.1810
FROM $BASE_IMAGE

ENV  TZ           Asia/Shanghai

RUN yum install -y gcc gcc-c++ vim kde-l10n-Chinese bison gettext texinfo make autotool automake \\
        wget curl zip unzip bzip2 file sudo readline ncurses which \\
        git net-tools man-db man-pages-zh-CN wqy-microhei-fonts bash-completion \\
        man-pages && yum clean all && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

ADD  packages.tar    /home/
WORKDIR /home/packages

RUN tar -xf make-4.2.1.tar.gz && cd make-4.2.1 && mkdir build && cd build && ../configure --prefix=/usr && make && make install

# 安装openssl1.1.1
RUN tar -xzvf openssl-1.1.1q.tar.gz && cd openssl-1.1.1q \\
    && ./config shared --openssldir=/usr/local/openssl --prefix=/usr/local/openssl \\
    && make && make install \\
    && echo "/usr/local/openssl/lib/" >> /etc/ld.so.conf \\
    && ldconfig

WORKDIR /
RUN rm -rf /home/packages && rm -rf /var/cache/yum/* && rm -rf /var/lib/rpm/__db.* && yum clean all && rm -rf /var/lib/yum/*

WORKDIR /home/finance

瘦身之前

结果镜像大小:810MB,分层查看openssl这一层145MB

$ docker images
REPOSITORY                                                    TAG             IMAGE ID       CREATED             SIZE
centos76                                                      test            ca7db4177ae6   9 seconds ago       810MB

$ docker history centos76:test 
IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
ca7db4177ae6   22 seconds ago   /bin/sh -c #(nop) WORKDIR /home/finance         0B        
903f247275d6   22 seconds ago   /bin/sh -c rm -rf /home/packages && rm -rf /…   25.5MB    
1dde3ade3113   23 seconds ago   /bin/sh -c #(nop) WORKDIR /                     0B        
416186ebbcaf   2 hours ago      /bin/sh -c tar -xzvf openssl-1.1.1q.tar.gz &…   145MB     
cb8526206e1e   2 hours ago      /bin/sh -c tar -xf make-4.2.1.tar.gz && cd m…   12MB      
5fea9247f155   2 hours ago      /bin/sh -c #(nop) WORKDIR /home/packages        0B        
07e256259d79   2 hours ago      /bin/sh -c #(nop) ADD file:9872509742c19c105…   200MB     
8e0a944d4532   2 hours ago      /bin/sh -c yum install -y gcc gcc-c++ vim kd…   225MB     
6a3b6540b169   2 hours ago      /bin/sh -c #(nop)  ENV TZ=Asia/Shanghai         0B        
bbd06e461441   3 hours ago      /bin/sh -c #(nop)  LABEL maintainer=jimo <ji…   0B        
f1cb7c7d58b7   3 years ago      /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B        
<missing>      3 years ago      /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B        
<missing>      3 years ago      /bin/sh -c #(nop) ADD file:54b004357379717df…   202MB

瘦身之后

只贴了变化的2层,最后都加了删除操作。

RUN tar -xf make-4.2.1.tar.gz && cd make-4.2.1 && mkdir build && cd build && ../configure --prefix=/usr && make && make install && rm -rf *

# 安装openssl1.1.1
RUN tar -xzvf openssl-1.1.1q.tar.gz && cd openssl-1.1.1q \\
    && ./config shared --openssldir=/usr/local/openssl --prefix=/usr/local/openssl \\
    && make && make install \\
    && echo "/usr/local/openssl/lib/" >> /etc/ld.so.conf \\
    && ldconfig && rm -rf *

结果 684MB,可以看到源码编译openssl层变得很小了,只剩下21.6MB

$ docker images
REPOSITORY                                                    TAG             IMAGE ID       CREATED          SIZE
centos76                                                      test            c1134e4948d8   54 seconds ago   684MB

$ docker history centos76:test 
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
c1134e4948d8   4 seconds ago   /bin/sh -c #(nop) WORKDIR /home/finance         0B        
2ed7d78e8d90   4 seconds ago   /bin/sh -c rm -rf /home/packages && rm -rf /…   25.5MB    
98c8b13a2b9b   5 seconds ago   /bin/sh -c #(nop) WORKDIR /                     0B        
f278729babc4   5 seconds ago   /bin/sh -c tar -xzvf openssl-1.1.1q.tar.gz &21.6MB    
17bea2739abc   2 minutes ago   /bin/sh -c tar -xf make-4.2.1.tar.gz && cd m…   9.51MB    
5fea9247f155   2 hours ago     /bin/sh -c #(nop) WORKDIR /home/packages        0B        
07e256259d79   2 hours ago     /bin/sh -c #(nop) ADD file:9872509742c19c105…   200MB     
8e0a944d4532   2 hours ago     /bin/sh -c yum install -y gcc gcc-c++ vim kd…   225MB     
6a3b6540b169   2 hours ago     /bin/sh -c #(nop)  ENV TZ=Asia/Shanghai         0B        
bbd06e461441   3 hours ago     /bin/sh -c #(nop)  LABEL maintainer=jimo <ji…   0B        
f1cb7c7d58b7   3 years ago     /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B        
<missing>      3 years ago     /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B        
<missing>      3 years ago     /bin/sh -c #(nop) ADD file:54b004357379717df…   202MB 

这个优化能有多大

上面的100多MB看起来无所谓吧,但如果你编译过GCC源码,并且最后没有删掉,你就知道这一层有多大了。

下面是我编译GCC9.4.0源码产生的镜像层大小, GCC编译万有6个多G,glibc接近1G,足矣引起重视了。

IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT   
17c1edbe5d21   6 minutes ago    /bin/sh -c tar -xf glibc-2.33.tar.gz && cd g…   989MB     
89d80b8e640f   10 minutes ago   /bin/sh -c tar -xvzf gcc-9.4.0.tar.gz && mv6.37GB    

当在结尾加上 rm -rf *后,对应的大小变为几百M,这才是质的提升。

IMAGE          CREATED             CREATED BY                                      SIZE      COMMENT
a7e28a9a41aa   2 minutes ago       /bin/sh -c tar -xf glibc-2.33.tar.gz && cd g…   382MB     
5e0ef78051a0   5 minutes ago       /bin/sh -c tar -xvzf gcc-9.4.0.tar.gz && mv …   779MB

对应的Dockerfile如下:

RUN tar -xvzf gcc-9.4.0.tar.gz && mv gmp-6.2.1.tar.xz isl-0.24.tar.gz mpc-1.2.1.tar.gz mpfr-4.1.0.tar.gz gcc-9.4.0/ && cd gcc-9.4.0 && tar -xf gmp-6.2.1.tar.xz && \\
    tar -xvzf isl-0.24.tar.gz && \\
    tar -xvzf mpc-1.2.1.tar.gz && \\
    tar -xvzf mpfr-4.1.0.tar.gz && \\
    ln -s gmp-6.2.1 gmp && \\
    ln -s isl-0.18 isl && \\
    ln -s mpc-1.2.1 mpc && \\
    ln -s mpfr-4.1.0 mpfr && \\
    mkdir build && cd build  && ../configure --prefix=/usr --enable-languages=c,c++ --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --enable-plugin --enable-default-pie --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux --disable-multilib && make -j $(nproc) && make install-strip

RUN tar -xf glibc-2.33.tar.gz && cd glibc-2.33 && \\
    mkdir build && cd build && sed -i 's/$name ne "nss_test1"/$name ne "nss_test1" \\&\\& $name ne "nss_test2"/' ../scripts/test-installation.pl && \\
    ../configure --prefix=/usr --enable-obsolete-nsl && make -j $(nproc) && make install

总结

除了删除源码包,系统安装包也要每次RUN就清理, 如果有的话:

RUN .... && yum clean all && rm -rf *
RUN .... && yum clean all && rm -rf *

这算是一个基本操作,以前没注意到是因为对docker镜像的Layer理解不够深刻。

  • 每一条指令都构成一层
  • 每一层都可以看成一个镜像,有唯一的ID
  • 每一层可以缓存,并且可以复用
  • 所以一层定下来后就不能改变了,改了指令就需要重新构建

在运行时的容器里,所有文件大小不到1G,镜像却有8G,就是因为每一层太大了,虽无用,永留存。

以上是关于镜像瘦身:每一层都不能放过的主要内容,如果未能解决你的问题,请参考以下文章

镜像瘦身:每一层都不能放过

互联网协议入门

TCP / IP协议 --- 计算机层面

简析TCP的三次握手与四次分手(TCP协议头部的格式,数据从应用层发下来,会在每一层都会加上头部信息,进行封装,然后再发送到数据接收端)good

JavaEE 知识总结

互联网协议入门