众所周知,Docker从2013年开始dotCloud,到现在也才七年。如果你刚刚经历过2013-2015早年的圈子,自然应该知道,原来Docker=LXC+aufs,前者就是所谓的Linux容器,后者就是我要做的镜像说说今天。Millennium:AmazingLiveCD说到Linux发行版,除了在界面主题上做差异化之外,核心差异一般在于:如何安装更方便;如何升级更方便;但是在发行版世界中有一个清晰的趋势,除了这两个东西之外,它们是LiveCD,它们以光盘或USB棒的形式提供,它们不需要安装,也不会改变。之前创业的时候,我们公司的运维负责人童哥曾经说过:第一次看到liveCD的时候惊呆了。.我当然同意这一点,我也是当时震惊的学生之一。要知道Knoppix在2000年就问世了,它所基于的大名鼎鼎的Debian直到2005年6月Sarge(3.1)才正式推出图形界面的安装程序debian-installer(简称d-i)在发布时的稳定版本中。以前版本的安装仍然使用文本菜单。在这个时代,这样一个开盘即用,图形化界面启动的系统,当然给我们玩家带来的震撼是可想而知的。当时的LiveCD就是十三年后的Docker,绝对配得上“惊艳”二字。要知道,在一张700MB左右的CD中装下一个完整的操作系统并不容易(当然,有人上手后也不是太难,然后我最喜欢的DSL可以达到50MB)。Knoppix有一个好主意——压缩已安装的操作系统,将其放在CD上,并在需要时解压缩。这样一来,一张700MB的光盘可以装下大约2GB的根文件系统,这样你运行KDE桌面就没有问题了。当时在distrowatch.com上可以看到大量的发行版都是基于Knoppix的魔改,可见其影响力。进化:可读可写层与UnionFSKNoppix在诞生之初的执念是“绝对不要在本地存储上动一根手指”,而光盘和光驱所使用的ISO9600文件系统也是只读的,这无疑不谋而合当今流行的“不可变基础架构”趋势,但即使在今天,没有可写文件系统对于许多Linux软件来说仍然是非常困难的。毕竟任何程序都需要写一些配置文件,状态信息,锁。、日志等。Knoppix诞生之初是不可写的,所以如果你想要一个需要罗盘的东西,你必须手动挖出一个本地硬盘来挂载它,或者挂载一个NAS到/home或其他挂载点,当你不只是想这样制作应急启动盘的时候可能会有点麻烦。如果我们从今天回溯,我们可以很容易地指出overlayfs加上一个tmpfs被用作可写层。不过overlayfs直到2010年才第一次提交patchset,并在2014年被合并到3.18内核中(这中间,当时的淘宝内核组也立下了汗马功劳,踩了很多坑)。当然,在overlay之前也有类似的unionfs。最早被Docker采用的Aufs就是其中之一。出现于2006年,这里AUFS的A可以理解为Advanced,但它最早的意思其实是Another——没错,“另一个UFS”,它的前身是UnionFS。2005年5月,也就是十五年前,Knoppix创造性地推出了UnionFS,而在一年半后的5.1版本中,Knoppix推出了当年诞生的更加稳定的aufs,此后,包括大家熟悉的UbuntuLiveCD、GentooLiveCD全部使用aufs。可以说,正是LiveCDs提前8年为Docker和DockerImage的诞生做好了存储准备。给不懂的朋友简单说一句。所谓unionfs是指将多个不同的文件系统组合(堆叠)起来,呈现为一个文件系统。不同于一般FHS规定的树组织方式。如下图所示,对于左边的标准目录树结构,任何文件系统都可以挂载到树上的一个挂载点上。根据路径,可以指向某个文件系统。例如下图中,所有/usr/local/下的路径都在一个文件系统上,而其他的/usr会在另一个文件系统上;而UnionFS是多层堆叠的,你写的文件会留在顶层,如图,你修改的/etc/passwd会在最上层可写,其他文件在下层,也就是说,它允许同一目录下的不同文件处于不同的层次,因此,LiveCD操作系统像真正的本地操作系统一样运行,可以读写所有路径。块或文件:Cloop和SquashFS让我们关注只读层,这是LiveCD的基础。这个只读rootfs在LiveCD没有unionFS分层的时候就已经存在了。对于Knoppix,这一层不能直接放一个完整的、未压缩的操作系统,因为在21世纪初,大家还在使用24x到40x速度的光驱,KnoppixLiveCD面临的一个大问题就是700MB之间的出入CD-ROM和笨重的桌面操作系统。正如我们开头提到的,Knoppix的思路是“将安装好的操作系统压缩,放入光盘,随用随解”,这样就可以将精挑细选的2GBDistro压缩成一张CD,但“随用随解”并不意味着有一张。文件系统访问块设备以查找块的偏移量。压缩后偏移量就没那么容易找到了,完全解压到内存中。再次找到偏移量并不是那么容易。回到2000年,当时还是2.2内核的时候,Knoppix的作者KlausKnopper在创立Knoppix的时候就引入了一种叫做cloop的压缩(compressed)循环设备。这个设备是一种特殊的格式,包含一个索引被创建,这样解压过程对用户来说是透明的。Knoppix的cloop设备看起来像块设备,大小约为2GB。应用程序在读写有偏移量的数据时,只需要使用索引解压对应的数据块返回给用户,而不用解压整个磁盘。尽管Knoppix为LiveCD船带来了很多发行版,但许多后继者,如arch、Debian、Fedora、Gentoo、Ubuntu等发行版LiveCD,以及在熟悉的路由器上播放的OpenWrt,都没有选择cloop文件,他们选择了一个更接近应用程序语义的文件系统级解决方案——Squashfs。Squashfs压缩文件、索引节点和目录,支持从4K到1M的压缩单元大小。同样,它也是根据索引按需解压。与cloop不同的是,当用户访问一个文件时,根据索引解压对应文件所在的block,而不是经过一层本地文件系统来压缩block寻址更简单直接。事实上,Knoppix中已经有人呼吁改用squashfs。例如,在2004年,一些开发人员将knoppix转换为squashfs。此外,一些测试数据似乎表明Squashfs的性能似乎更好,尤其是在元数据操作方面。wiki上对cloop的缺点评价如下:cloop驱动的设计要求压缩块要从磁盘中整块读取。当有许多分散的读取时,这使得cloop访问本质上变慢,如果系统内存不足或启动具有许多共享库的大型程序时可能会发生这种情况。一个大问题是CD-ROM驱动器的寻道时间(~80毫秒),它大大超过了硬盘的寻道时间(~10毫秒)。另一方面,由于文件被打包在一起,读取压缩块可能会因此将多个文件带入缓存。众所周知,尾部打包的效果可以缩短寻道时间(参见reiserfs、btrfs),尤其是对于小文件。已经进行了一些与cloop相关的性能测试。让我多翻译一下:cloop旨在以压缩块的形式从磁盘读取数据。因此,当有许多随机读取时,cloop可能会显着变慢,这可能发生在系统内存不足或启动具有许多共享库的大型程序时。cloop面临的一个大问题是CD-ROM的寻道时间(约80ms),大大超过了硬盘的寻道时间(约10ms)。另一方面,由于文件可以打包在一起,读取压缩块实际上可能会将多个文件带入缓存。这样,那些支持尾部打包的文件系统(如reiserfs、btrfs)可能会显着改善seek操作时间,尤其是对于小文件。已经有一些与cloop相关的性能测试来证明这几点。当然,尽管有这些争论,cloop仍然存在于Knoppix上。不过这场争论最终在2009年用squashfs合并到2.6.29主线内核中,应该算是赢家了。开箱即用换来压倒性的市场份额和更好的支持,Squashfs的优势不仅是上面提到的大量发行版用户,还有各种压缩算法的支持,仅在不同的应用中使用场景。Docker:让UnionfsGreatAgain星光更迭,不再年轻的LiveCD火爆到不新鲜。不过,科技圈也有轮回。曾经被LiveCD普及的UnionFS,被Docker再次普及,又迎来了第二春。一般来说,虽然aufs支持多个只读层,但普通的LiveCD只需要一个只读镜像和一个用户可写层即可。然而以Solomon为首的dotCloud的小伙伴们发挥了出色的想象力,将整个文件系统变成了“包”的基本单元,从而实现了#MUGA(MakeUnionfsGreatAgain)。回想一下,从1993年的Slackware到今天的RHEL,Distro(服务器端)做的无非就是我开篇提到的两件事——安装和升级。从rpm到APT/deb再到Snappy,初始化系统后的工作本质在于如何更顺利地安装和升级,保证没有依赖问题,不占用过多的额外空间。解决这个问题的基础是rpm/deb等包和包之间的依赖关系。但是,诸如“A依赖B和C,但B与C冲突”这样的问题还是层出不穷,人们也在不断地努力解决。二十年。但是Docker跳出了一个软件包的想法。他们是这样看的——一个完整的操作系统是一个包,必须是自成一体的,如果在开发、测试、部署环境的过程中都维护着同一个完整的、不变的操作系统,那么就不会造成那么多问题通过依赖;这个完整的操作系统是不可变的,就像一张LiveCD,我们称它为镜像,你可以使用像aufs这样的unionFS放一个writableLayer,应用程序可以在运行时向writablelayer写入东西,以及一些动态生成的配置也可以放在可写层;如果一些应用软件镜像共享基础系统的相同部分,那么将这些公共部分放在Unionfs的下层作为只读层,让它们可以被不同的应用程序使用;当然,如果两个应用依赖的东西不同,那么它们使用的基础层不同,就没有必要互相迁就,自然也就没有上面的依赖有矛盾了;一个镜像可以包含多个图层,方便应用共享部分数据,节省存储和传输成本;大致示意图如下:这样,如果这三个应用程序(容器)运行在同一台机器上),那么这些共享的只读层就不需要重复下载了。此外,Docker的分层结构的另一个优点是它对开发人员非常友好。可以看到下面是一个Dockerfile的示意图。FROM代表最底层的基础层,然后进行RUN、ADD这样改变rootfs的操作,将结果保存为新的中间层,最后形成镜像。这样,开发者对软件依赖关系的组织就可以在图像的层级关系中得到清晰的展示。比如下面的Dockerfile就是一个打包的镜像。它首先安装软件的依赖包和语言环境,然后初始化打包运行用户环境,然后拉取源代码,最后将制作软件包的脚本放到镜像中。这种组织方式是从一般任务到具体任务。图像制作者希望使这些图层尽可能通用。底层的内容可以在其他图像中使用,而上层的内容与本图像的工作最直接相关。其他开发者看到这个Dockerfile,基本可以知道这个镜像里面有什么,要做什么,能不能借鉴。这个镜像的设计是Docker设计中最巧妙的地方之一,这也是为什么大家愿意认同Solomon要做的就是开发者体验(DX,DeveloperExperiences)。FROMdebian:jessieMAINTAINERHyperDevelopers
