精简为王:Docker镜像体积详解

welwit 8年前
   <p> </p>    <p>不知道大家读过<a href="http://www.open-open.com/lib/view/open1463108483467.html">使用Docker容器的十大误区</a>(没读过的可以戳这个链接 <a href="http://www.open-open.com/lib/view/open1463108483467.html">http://www.open-open.com/lib/view/open1463108483467.html</a> )如果读过,相信大家已经对跑Docker容器时的几个注意事项有了大概的了解了。不过文中关于“不要把镜像体积建得很大”这一点讲得不是很详细,还有这一句:“千万不要在镜像中安装些没必要的东西,在构建镜像的时候要避免使用yum这种update命令,免得系统自动下载很多不相干的文件到新镜像层中”,大家可能还有一些问题。比方说,为什么简简单单一句“yum update”命令就会构建出一个超大体积的镜像?可能很多人都还不清楚其中的原理。为了帮助大家理解这个问题,下面我们就来深入探讨一下docker镜像的构建机制,此外还有一些小技巧,可以帮助大家在不影响镜像内容更新的条件下,缩减镜像的体积。</p>    <p>本文用到的基础镜像是最新版的 Fedora 23(用RHEL也可以),大家可以用 docker pull fedora:23 命令来pull这个镜像。</p>    <p>Pull下来之后运行 docker images ,可以看到镜像大小是204.7MB。然后我们用从GitHub下载的Dockerfile来创建一个包含JDK 1.8好WildFly 9.0.2.Final的自定义镜像,大家可以自己去GitHub下载Dockerfile。</p>    <p>镜像创建完以后体积已经达到了567.3 MB,大家可以运行一下“docker history <镜像名>”,查看每个层的大小。我的镜像里面,JDK 1.8有203.5MB,WildFly有159.1 MB,体积相当大。</p>    <p><img src="https://simg.open-open.com/show/67b82c87d89e92c4e053da3b330f82bb.jpg"></p>    <p>这还不算完,创建这个镜像之后,我们最终肯定是要升级到WildFly 10.0.0.Final版的,如果不想重新安装 JDK 1.8的话,很多人都会选择在现有镜像的基础上,用10.0.0.Final替换WildFly 9.0.2.Final。如果你以为升级下来镜像大小还是567 MB的话,那就大错特错了,实际上升级后的镜像体积是728.1 MB。就算你在往镜像中添加WildFly 10.0.0.Final之前先删除掉WildFly 9.0.2.Final,最后得到的镜像还是要比原先大160MB。文件体积的增大并不是因为WildFly版本之间的差异造成的。</p>    <p>写入时拷贝</p>    <p>那么为什么镜像文件会增大呢?原因就在于Docker容器的文件系统采用了写入时拷贝技术,这项技术的作用是加快容器的启动时间,跟一般的虚拟机相比,容器的启动速度确实非常快。虽然该技术在很大程度上提升了docker容器的运行效率,但是也会增加磁盘读写方面的开销。所以,为了避免文件体积过大,有几个问题需要我们在构建镜像的时候注意一下。</p>    <p>这里要先说一下,每次在Dockerfile中执行RUN命令的时候系统都会在镜像中新建一个层,每个镜像层都会占用一定的磁盘空间,所以,在upgrade WildFly的时候我们其实是创建了一个新的层。因此为了尽量减少镜像的层数,最好把移动、提取、删除等所有文件操作都写在同一行RUN命令下。</p>    <p>大家可以参照下面的命令行写法,这个命令把WildFly 10.0.0.Final的安装、下载、提取、移动和清理命令都写在一行里,这样创建下来的镜像体积就只有569.1 MB:</p>    <p>"cd /tmp && curl -O <a href="/misc/goto?guid=4959673089139983377" rel="nofollow,noindex">https://download.jboss.org/wildfly/</a> $WILDFLY_VERSION/wildfly-$WILDFLY_VERSION.tar.gz && tar xf wildfly-$WILDFLY_VERSION.tar.gz && mv /tmp/wildfly-$WILDFLY_VERSION /opt/jboss/wildfly && rm /tmp/wildfly-$WILDFLY_VERSION.tar.gz"</p>    <p>如果我们每写一条命令都执行一次RUN的话,镜像就会多出4个层,最后得到的镜像大小将是867.1 MB。如下图所示,我们可以用“docker history”命令来查看镜像的层以及每个层的大小:</p>    <p><img src="https://simg.open-open.com/show/318c7b2d124bcc9a45ffee1b2b7c0dca.jpg"></p>    <p>Yum Update</p>    <p>那么问题来了,“yum update”到底是做什么用的呢?为了帮助大家更好地理解“yum update”的作用,我建了fedora:22和fedora:23两个镜像。这两个自定义镜像都是用 RUN dnf -y update 命令从Dockerfile创建的,fedora:22 的大小是531.2 MB,fedora:23是358.2 MB。</p>    <p>在这个命令下,Fedora:22会自动更新到跟Fedora 23一样的版本,因此会下载安装很多文件,最后的体积也要大些。所以在构建镜像的时候,如果能用现有平台上最新的基础镜像(比如说构建的时候用Fedora 23,不要用Fedora 22)来建的话,就可以避免往中间层写入很多额外的文件,这样做不仅能加快镜像创建速度,还可以缩减镜像大小。</p>    <p>总之我想说的就是,执行“updates”命令的时候会下载一些新的rpm包,所以对镜像文件的改动还是蛮大的。虽然之前的镜像层无法修改,但rpm cache是可以清理的。我们只需要以写“RUN dnf -y update && dnf clean all”命令就可以清理cache了,执行该命令后,镜像大小从358.2 MB变到了216.2 MB,之比原来的fedora:23镜像大11.5MB。这个办法很好用,大家在执行update之后,一定要记得做清理,这样就能最大限度地节省空间。</p>    <p><img src="https://simg.open-open.com/show/8e55fd89127de308673666cd13ef07ab.jpg"></p>    <p>综上,“yum/dnf update”命令本身并没有什么问题,导致镜像文件过大的原因主要还是因为我们对docker的分层式文件系统不太了解,很多人都不知道这种文件系统会对镜像体积造成很大的影响。如果我们只有几个容器的话,这些都还算可以忍受,但如果是在集群里面部署大量容器的话,镜像体积过大就会很麻烦,但是我们又不可能完全不使用update命令,不然我们的linux容器就会产生bug或者安全漏洞等等问题。</p>    <p>最好的解决办法就是把update和清理命令都放在同一行RUN底下,在执行update的同时释放多余的空间,这样我们就可以有效地缩减镜像体积。</p>    <p>via: http://dockone.io/article/1302</p>