3.0.3 容器镜像

前文中,我们虽然把容器进行了隔离处理,但是它所看到的文件系统在默认情况下是与宿主机相同的。

对此,创建新进程时,除了声明要启用Mount Namespace之外,还必须告诉容器进程,哪些目录需要重新挂载。

当我们在容器启动之前重新挂载它的整个根目录"/"由于Mount Namespace的存在,这个挂载对宿主机不可见,所以容器进程就可以在里面随便折腾了。

在Linux操作系统里,有一个名为chroot的命令可以帮助你在shell中方便地完成这个工作,"change root file system" 改变进程的根目录到指定位置。

Mount Namespace正式基于chroot的不断改良才被发明出来的,也是Linux操作系统里的第一个Namespace。
Docker项目会优先使用pivot_root 系统调用,如果系统不支持
实际上,容器的根目录挂载的是一个完整的操作系统文件,如Ubuntu,这个文件系统就是容器镜像(rootfs 根文件系统)

可以理解成,Docker项目,最核心的原理实际上就是为待创建的用户:
1.启用Linux Namespace配置
2.设置指定的Cgroups参数
3.切换进程的根目录(Change Root)

rootfs只是一个操作系统所包含的文件,配置和目录,并不包含操作系统内核。因此rootfs最多也就几百兆。而传统虚拟机的镜像是一个磁盘的镜像,磁盘有多大,镜像就有多大。

实际上,对于同一台机器上的所有容器,都共享宿主机操作系统的内核。因此如果你的应用程序需要配置内核参数,加载额外的内核模块,以及跟内核直接交互。那么就需要注意这些对于机器上的所有容器都是一个全局变量。

对于A,B两个人用Ubuntu来部署Java应用时,显然希望能够直接使用以前安装过Java环境的rootfs,而不是重生再 生成一遍。

Docker在镜像的设计中,引入了层(layer)的概念,用户制作镜像的每一步操作,都会生成一个层,也就是一个增量rootfs。

Linux下具有,联合文件系统Union File System,最主要功能是将多个不同位置的目录联合挂载到同一个目录下。

Docker环境下采用AuFS,是对原生UnionFS的重写和改进

Docker容器的rootfs结构图:


image.png

第一部分 只读层:
容器的rootfs最下面五层,对应的是ubuntu镜像的五层,挂载方式都是只读的(ro+wh : readonly + whiteout )

第二部分 Init层
夹在只读层和读写层之间,专门用来存放 /etc/hosts, /etc/resolv.conf等信息,
这一层的文件本来属于只读的Ubuntu镜像的一部分,但是用户往往需要在启动容器时写入一些指定的值:(hostname ...)所以需要在可读写层对他们进行修改。
但这些修改往往只对当前容器有效,并不希望执行docker commit时,把这些信息连同可读写层一起提交

第三部分 可读写层:
容器的rootfs的最上面一层,挂载方式为rw 即 read write
为写入前,目录为空,写入后的修改以增量的方式出现在这一层, 删除操作:会创建一个whiteout文件,把只读层里的文件遮挡起来。

例如删除一个foo的文件,删除操作实际上是在可读写层,创建一个名为 .wh.foo 的文件。这样,当这两个层被联合挂载之后,foo文件就会被.wh.foo文件遮挡

最终,这7层被联合挂载到/var/lib/docker/aufs/mnt目录下,表现为一个完整的Ubuntu操作系统供容器使用。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容