Docker 镜像原理
目录
操作系统基础
Union FS(联合文件系统)
再看 Docker 镜像是什么
镜像实现原理
docker 镜像加载原理
docker 是操作系统层的虚拟化,所以 docker 镜像的本质是在模拟操作系统。我们先看下操作系统是什么。
操作系统基础
操作系统由:进程调度子系统、进程通信子系统、内存管理子系统、设备管理子系统、 文件管理子系统 、网络通信子系统、作业控制子系统组成。
Linux 的 文件管理子系统 由 bootfs 和 rootfs 组成。
(1)bootfs :要包含 bootloader 和 kernel, bootloader 主要是引导加载 kernel, Linux 刚启动时会加载 bootfs 文件系统,在 Docker 镜像的最底层是引导文件系统 bootfs。这一层与我们典型的 Linux/Unix 系统是一样的,包含 boot 加载器和内核。当 boot 加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs 转交给内核,此时系统也会卸载 bootfs 。
(2)rootfs : 在 bootfs 之上。包含的就是典型 Linux 系统中的 /dev, /proc, /bin, /etc 等标准目录和文件。 rootfs 就是各种不同的操作系统发行版,比如 Ubuntu , Centos 等等。

Union FS(联合文件系统)
联合文件系统( Union File System ), 2004 年由纽约州立大学开发,它可以把多个目录内容联合挂载到同一个目录下,而目录的物理位置是分开的。UnionFS 可以把只读和可读写文件系统合并在一起,具有写时复制功能,允许只读文件系统的修改可以保存到可写文件系统当中。
UnionFS(联合文件系统)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。 UnionFS 是一种为 Linux , FreeBSD 和 NetBSD 操作系统设计的把其他文件系统联合到一个联合挂载点的文件系统服务。它使用 branch 把不同文件系统的文件和目录 “ 透明地 ” 覆盖,形成一个单一一致的文件系统。这些 branches 或者是read-only 或者是 read-write 的,所以当对这个虚拟后的联合文件系统进行写操作的时候,系统是真正写到了一个新的文件中。看起来这个虚拟后的联合文件系统是可以对任何文件进行操作的,但是其实它并没有改变原来的文件,这是因为 unionfs 用到了一个重要的资管管理技术叫写时复制。
写时复制( copy-on-write ,下文简称 CoW),也叫隐式共享,是一种对可修改资源实现高效复制的资源管理技术。它的思想是,如果一个资源是重复的,但没有任何修改,这时候并不需要立即创建一个新的资源;这个资源可以被新旧实例共享。创建新资源发生在第一次写操作,也就是对资源进行修改的时候。通过这种资源共享的方式,可以显著地减少未修改资源复制带来的消耗, 但是也会在进行资源修改的时候增 加小部分的开销。
再看 Docker 镜像是什么
image 里面是一层层文件系统 Union FS。联合文件系统,可以将几层目录挂载到一起,形成一个虚拟文件系统。虚拟文件系统的目录结构就像普通 linux 的目录结构一样, docker 通过这些文件再加上宿主机的内核提供了一个 linux 的虚拟环境。
每一层文件系统我们叫做一层 layer,联合文件系统可以对每一层文件系统设置三种权限,只读( readonly )、读写( readwrite )和写出( whiteout-able ),但是 docker镜像中每一层文件系统都是只读的。
构建镜像的时候,从一个最基本的操作系统开始,每个构建的操作都相当于做一层的修改,增加了一层文件系统。一层层往上叠加,上层的修改会覆盖底层该位置的可见性,这也很容易理解,就像上层把底层遮住了一样。当你使用的时候,你只会看到一个完全的整体,你不知道里面有几层,也不清楚每一层所做的修改是什么。

可以看到镜像分层结构有以下特性
( 1 )镜像共享宿主机的 kernel
( 2 ) base 镜像是 linux 的最小发行版
( 3 )同一个 docker 主机支持不同的 Linux 发行版
( 4 )采用分层结构,可以上层引用下层,最大化的共享资源
( 5 )容器层位于可写层,采用 cow 技术进行修改,该层仅仅保持变化的部分,并不修改镜像下面的部分
( 6 )容器层以下都是只读层
( 7 ) docker 从上到下找文件
镜像实现原理
Docker 分层存储实现原理
1. 分层存储实现方式
docker 镜像技术的基础是联合文件系统 (UnionFS) ,其文件系统是分层的
Shell
目前 docker 支持的联合文件系统有很多种,包括: AUFS 、 overlay 、 overlay2 、
DeviceMapper 、 VSF 等
Linux 中各发行版实现的 UnionFS 各不相同,所以 docker 在不同 linux 发行版中使用的也不同。通过 docker info 命令可以查看当前系统所使用哪种 UnionFS,常见的几种发行版使用如下:
Shell
CentOS, Storage Driver: overlay2 、 overlay
debain, Storage Driver: aufs
RedHat, Storage Driver: devicemapper
overlay2 是 overlay 的升级版,官方推荐,更加稳定,而新版的 docker 默认也是这个驱动, linux 的内核 4.0 以上或者或使用 3.10.0-514 或更高版本内核的 RHEL 或 CentOS 。
2. Union FS 的原理
docker 镜像由 多个只读层叠加面成 ,启动容器时, docker 会 加载只读镜像层并在 镜像栈顶部加一个读写层;
如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件版本仍然存在,只是已经被读写层中该文件的副本所隐藏,此即 “ 写时复制 (COW)” 机制 。

如果一个文件在最底层是可见的,如果在 layer1 上标记为删除,最高的层是用户看到的 Layer2 的层,在 layer0 上的文件,在 layer2 上可以删除,但是只是标记删除,用户是不可见的,总之在到达最顶层之前,把它标记来删除,对于最上层的用户是不可见的,当标记一删除,只有用户在最上层建一个同名一样的文件,才是可见的。
docker 镜像加载原理
boots(boot file system )主要包含 bootloader 和 Kernel, bootloader 主要是引导加kernel,Linux 刚启动时会加 bootfs 文件系统,在 Docker 镜像的最底层是 bootfs。这一层与我们典型的 Linux/Unix 系统是一样的,包含 boot 加載器和内核。当 boot 加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs 转交给内核,此时系统也会卸载 bootfs 。

rootfs ( root file system), 在 bootfs 之上。包含的就是典型 Linux 系统中的/dev,/proc,/bin,/etc 等标准目录和文件。 rootfs 就是各种不同的操作系统发行版,比如Ubuntu,Centos 等等。
典型的 Linux 在启动后,首先将 rootfs 置为 readonly, 进行一系列检查 , 然后将其切换为 “readwrite” 供用户使用。在 docker 中,起初也是将 rootfs 以 readonly 方式加载并检查,然而接下来利用 union mount 的将一个 readwrite 文件系统挂载在 readonly 的rootfs 之上,并且允许再次将下层的 file system 设定为 readonly 并且向上叠加, 这样一组 readonly 和一个 writeable 的结构构成一个 container 的运行目录, 每一个被称作一个 Layer。

下面的这张图片非常好的展示了组装的过程,每一个镜像层都是建立在另一个镜像层之上的,同时所有的镜像层都是只读的,只有每个容器最顶层的容器层才可以被用户直接读写,所有的容器都建立在一些底层服务( Kernel)上,包括命名空间、控制组、rootfs 等等,这种容器的组装方式提供了非常大的灵活性,只读的镜像层通过共享也能够减少磁盘的占用。