Docker镜像原理

Docker镜像原理

  • 一.操作系统基础
  • [二.Union FS(联合文件系统)](#二.Union FS(联合文件系统))
  • [三.Docker 分层存储实现原理](#三.Docker 分层存储实现原理)
  • [四.docker 镜像加载原理](#四.docker 镜像加载原理)
  • 五.实操

一.操作系统基础

操作系统由:进程调度子系统、进程通信子系统、内存管理子系统、设备管理子系统、文件管理子系统、网络通信子系统、作业控制子系统组成。

Linux 的文件管理子系统由 bootfs 和 rootfs 组成。

  • bootfs:要包含 bootloader 和 kernel, bootloader 主要是引导加载 kernel, Linux 刚
    启动时会加载 bootfs 文件系统,在 Docker 镜像的最底层是引导文件系统 bootfs。这
    一层与我们典型的 Linux/Unix 系统是一样的,包含 boot 加载器和内核。当 boot 加载
    完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs 转交给内核,此时
    系统也会卸载 bootfs。
  • 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),也叫隐式共享,是一种对可修改资源实现高效复制的资源管理技术。它的思想是,如果一个资源是重复的,但没有任何修改,这时候并不需要立即创建一个新的资源;这个资源可以被新旧实例共享。

创建新资源发生在第一次写操作,也就是对资源进行修改的时候。通过这种资源共享的方式,可以显著地减少未修改资源复制带来的消耗, 但是也会在进行资源修改的时候增加小部分的开销。

可以看到镜像分层结构有以下特性:

  • 镜像共享宿主机的 kernel
  • base 镜像是 linux 的最小发行版
  • 同一个 docker 主机支持不同的 Linux 发行版
  • 采用分层结构,可以上层引用下层,最大化的共享资源
  • 容器层位于可写层,采用 cow 技术进行修改,该层仅仅保持变化的部分,并不修改镜像下面的部分
  • 容器层以下都是只读层
  • docker 从上到下找文件

三.Docker 分层存储实现原理

1.分层存储实现方式

docker 镜像技术的基础是联合文件系统(UnionFS),其文件系统是分层的,Shell

目前 docker 支持的联合文件系统有很多种,包括: AUFS、 overlay、 overlay2、

DeviceMapper、 VSF 等

Linux 中各发行版实现的 UnionFS 各不相同,所以 docker 在不同 linux 发行版中使用

的也不同。通过 docker info 命令可以查看当前系统所使用哪种 UnionFS,常见的几种

发行版使用如下:

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 上可以删除,但是只是标记删除,用

户是不可见的,总之在到达最顶层之前,把它标记来删除,对于最上层的用户是不可

见的,当标记一删除,只有用户在最上层建一个同名一样的文件,才是可见的。

3.overlay2 实现

  1. 架构
    OverlayFS 将单个 Linux 主机上的两个目录合并成一个目录。这些目录被称为层,统一过程被称为联合挂载 OverlayFS 底层目录称为 lowerdir, 高层目录称为upperdir,合并统一视图称为 merged(图中可以看到三个层结构,即 lowerdir、 upperdir、 merged 层)
  2. 分层
  • lowerdir 层
    其中 lowerdir 是只读的镜像层(image layer),其中就包含 bootfs/rootfs 层,bootfs(boot file system)主要包含 bootloader 和 kernel, bootloader 主要是引导加载kernel,当 boot 成功 kernel 被加载到内存中, bootfs 就被 umount 了, rootfs(root file system)包含的就是典型 Linux 系统中的/dev、 /proc、 /bin、 /etc 等标准目录。lowerdir 是可以分很多层的,除了 bootfs/rootfs 层以外,还可以通过 Dockerfile 建立很多 image 层
  • upperdir 层
    upper 是容器的读写层,采用了 CoW(写时复制)机制,只有对文件进行修改才会将文件拷贝到 upper 层,之后所有的修改操作都会对 upper 层的副本进行修改。 upperdir 层是lowerdir 的上一层,只有这一层可读可写的,其实就是 Container 层,在启动一个容器的时候会在最后的 image 层的上一层自动创建,所有对容器数据的更改都会发生在这一层。
  • workdir 层
    它的作用是充当一个中间层的作用,每当对 upper 层里面的副本进行修改时,会先当到workdir,然后再从 workdir 移动 upper 层
  • merged 层
    是一个统一图层,从 mergedir 可以看到 lower,upper,workdir 中所有数据的整合,整个容器展现出来的就是 mergedir 层.merged 层就是联合挂载层,也就是给用户暴露的统一视觉,将 image 层和 container 层结合,就如最上边的图中描述一致,同一文件,在此层会展示离它最近的层级里的文件内容,或者可以理解为,只要 container 层中有此文件,便展示 container 层中的文件内容,若 container 层中没有,则展示image 层中的。
  1. 如何完成读写
    1、读:
    如果文件在 upperdir(容器)层,直接读取文件;如果文件不在 upperdir(容器)层,则从镜像层(lowerdir)读取;
    2、写:
    首次写入:如果 upperdir 中不存在, overlay 和 overlay2 执行 copy_up 操作,把文件从 lowdir 拷贝到 upperdir 中,由于 overlayfs 是文件级别的(即使只有很少的一点修改,也会产生 copy_up 的动作),后续对同一文件的再次写入操作将对已经复制到容器层的文件副本进行修改,这也就是常常说的写时复制(copy-on-write)。删除文件或目录:当文件被删除时,在容器层(upperdir)创建 whiteout 文件,镜像层(lowerdir)的文件是不会被删除的,因为它们是只读的,但 whiteout 文件会阻止它们显示,当目录被删除时,在容器层(upperdir)一个不透明的目录,这个和上边的 whiteout的原理一样,组织用户继续访问, image 层不会发生改变
    3、注意事项
    copy_up 操作只发生在文件首次写入,以后都是只修改副本。容器层的文件删除只是一个"障眼法",是靠 whiteout 文件将其遮挡,image 层并没有删除,这也就是为什么使用docker commit 提交保存的镜像会越来越大,无论在容器层怎么删除数据, image 层都不会改变。

四.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 等等,这种容器的组装方式提供了非常大的灵活性,只读的镜像层通过共享也能够减少磁盘的占用。

五.实操

1.镜像分层存储实战

tree 命令详解

Linux tree 命令用于以树状图列出目录的内容。

执行 tree 指令,它会列出指定目录下的所有文件,包括子目录里的文件。
安装

powershell 复制代码
ubuntu 安装
Shell
apt install tree -y

centos 安装
Shell
yum install tree -y

语法
Shell
tree [-aACdDfFgilnNpqstux][-I <范本样式>][-P <范本样式>][目录...]

常用参数说明:

  • -a 显示所有文件和目录。
  • -d 显示目录名称而非内容。
  • -D 列出文件或目录的更改时间。
  • -f 在每个文件或目录之前,显示完整的相对路径名称。
  • -i 不以阶梯状列出文件或目录名称。
  • -L level 限制目录显示层级。
  • -l 如遇到性质为符号连接的目录,直接列出该连接所指向的原始目录。
  • -P<范本样式> 只显示符合范本样式的文件或目录名称。







拉取镜像

通过 docker image history 查看如下,我们可以看到 dockerfile 和做出来的镜像是对

应的,而且不是每一层都占用空间的。

我们通过 inspcet 命令查看,该镜像的存储位置,可以看到 GraphDriver 也就是我们的存储驱动,是 overlay2 的存储驱动, nginx 的 overlay2 的四个目录也都显示出来了,我们知道 docker 的默认目录是/var/lib/docker,之所以在/data/var/lib/docker 下面是因为我们规划了磁盘,调整了默认的存储目录。

我们可以看到 lowerdir, upperdir,都位于/data/var/lib/docker/overlay2 下面,因为我们调整过默认存储位置所以对比默认的/var/lib/docker 多了/data,我们在这个目录下查找我们的 nginx,看下文件是如何被存储的。搜索后可以看到我们找到了多个nginx 文件,因为我们本地有多个 nginx 镜像所以搜到了多个 nginx 文件,通过lowerdir 的值我们可以确定有一个是和我们 nginx:1.12.1 的匹配上的,红色部分。

同样的方式我们通过 Dockerfile 发现, nginx 还存储了个 docker-entrypoint.sh,我们搜索这个文件,我们发现这个文件也被放到了 diff 目录下面,和我们的 lowerdir 中一个 layer 是对应的。查看内容我们确定并没有什么加密之类的,也就是是镜像的文件分层后放到了 diff 目录下面。

接下来我们进入 diff 的上一级目录查看,可以看到 link 文件,里面是每一个 diff 目录的短名称,或者说软链接。

通过遍历 l 目录我们会发现,整个 docker 的镜像的 diff 目录都被做了对应的软链

接,或者说起了个短名称。

每一个 diff 是一个层级的内容,层级的关系是存放到了 lower 文件中,里面存放着

父级的层级

最后我们查看下 mergeddir,发现 merged dir 是不存在的,当我们启动为容器的

时候才是有有效的。

2.overlay 文件系统工作实战

我们首先创建一个目录用来挂载我们的文件系统

我们创建文件系统的工作目录

准备一些文件

挂载 overlay 目录

通过 df -h 可以看到我们完成了挂载

我们此时看下目录结构,然后发现 merged 目录自动生成了 3 个文件,可以看到boteh,lower,upper 都在。 merged 目录其实就是用户看到的目录,用户的实际文件操作在这里进行。

编辑,merge 目录下编辑一下 in_lower.txt, upper 目录下就会马上出现一个 in_lower.txt,而且内容就是编辑后的。而 lower 目录下的 in_lower.txt 内容不变

如果我们删除 in_lower.txt, lower 目录里的"in_lower.txt"文件不会有变化,只是在

upper/ 目录中增加了一个特殊文件来告诉 OverlayFS, "in_lower.txt'这个文件不能出现

在 merged/ 里了,类似 AuFS 的 whiteout

注意到 upper 下 in_lower.txt 的文件类型没有 ,而是 c 不是-或者 d

相关推荐
草莓熊Lotso9 小时前
Linux 文件描述符与重定向实战:从原理到 minishell 实现
android·linux·运维·服务器·数据库·c++·人工智能
历程里程碑9 小时前
Linux22 文件系统
linux·运维·c语言·开发语言·数据结构·c++·算法
七夜zippoe17 小时前
CANN Runtime任务描述序列化与持久化源码深度解码
大数据·运维·服务器·cann
金刚猿17 小时前
01_虚拟机中间件部署_root 用户安装 docker 容器,配置非root用户权限
docker·中间件·容器
JH_Kong18 小时前
解决 WSL 中 Docker 权限问题:从踩坑到完整修复
docker·容器
忆~遂愿18 小时前
GE 引擎与算子版本控制:确保前向兼容性与图重写策略的稳定性
大数据·开发语言·docker
Fcy64819 小时前
Linux下 进程(一)(冯诺依曼体系、操作系统、进程基本概念与基本操作)
linux·运维·服务器·进程
袁袁袁袁满19 小时前
Linux怎么查看最新下载的文件
linux·运维·服务器
代码游侠19 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
Harvey90319 小时前
通过 Helm 部署 Nginx 应用的完整标准化步骤
linux·运维·nginx·k8s