简介: 本文介绍了Docker使用的联合文件系统(UnionFS),它通过分层存储和写时复制(CoW)机制,实现了容器的轻量级、高性能存储,支持了镜像继承、数据持久化和隔离性。
目录:
我们知道,Docker 主要是基于 Namespace、cgroups 和联合文件系统这三大核心技术实现的。
联合文件系统(Union File System,Unionfs)是一种分层的轻量级文件系统,它可以把多个目录内容联合挂载到同一目录下,从而形成一个单一的文件系统,这种特性可以让使用者像是使用一个目录一样使用联合文件系统。
那联合文件系统对于 Docker 是一个怎样的存在呢?它可以说是 Docker 镜像和容器的基础,因为它可以使 Docker 可以把镜像做成分层的结构,从而使得镜像的每一层可以被共享。例如两个业务镜像都是基于 CentOS 7 镜像构建的,那么这两个业务镜像在物理机上只需要存储一次 CentOS 7 这个基础镜像即可,从而节省大量存储空间。
联合文件系统在主机上使用多层目录存储,但最终呈现给用户的则是一个普通单层的文件系统,我们把多层以单一层的方式呈现出来的过程叫作联合挂载。
说到这儿,你有没有发现,联合文件系统只是一个概念,真正实现联合文件系统才是关键,那如何实现呢?其实实现方案有很多,Docker 中最常用的联合文件系统有三种:AUFS、Devicemapper 和 OverlayFS。
AUFS 目前并未被合并到 Linux 内核主线,因此只有 Ubuntu 和 Debian 等少数操作系统支持 AUFS。它在主机上使用多层目录存储。
相比对文件系统加锁的机制,Devicemapper 工作在块级别,因此可以实现同时修改和读写层中的多个块设备,比AUFS文件系统效率更高。
通常情况下, overlay2 会比 AUFS 和 Devicemapper 性能更好,而且更加稳定,因为 overlay2 在 inode 优化上更加高效。因此在生产环境中推荐使用 overlay2 作为 Docker 的文件驱动。
AUFS 是 Docker 最早使用的文件系统驱动,多用于 Ubuntu 和 Debian 系统中。在 Docker 早期,OverlayFS 和 Devicemapper 相对不够成熟,AUFS 是最早也是最稳定的文件系统驱动。 接下来,我们就看看如何配置 Docker 的 AUFS 模式。
AUFS 目前并未被合并到 Linux 内核主线,因此只有 Ubuntu 和 Debian 等少数操作系统支持 AUFS。你可以使用以下命令查看你的系统是否支持 AUFS.
grep aufs /proc/filesystemsnodev aufs执行以上命令后,如果输出结果包含aufs,则代表当前操作系统支持 AUFS。AUFS 推荐在 Ubuntu 或 Debian 操作系统下使用,如果你想要在 CentOS 等操作系统下使用 AUFS,需要单独安装 AUFS 模块(生产环境不推荐在 CentOS 下使用 AUFS),安装完成后使用上述命令输出结果中有aufs即可。
当确认完操作系统支持 AUFS 后,你就可以配置 Docker 的启动参数了。
先在 /etc/docker 下新建 daemon.json 文件,并写入以下内容:
{ "storage-driver": "aufs"}然后使用以下命令重启 Docker:
systemctl restart dockerDocker 重启以后使用docker info命令即可查看配置是否生效:
$ sudo docker infoClient: Debug Mode: falseServer: Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 1 Server Version: 19.03.12 Storage Driver: aufs Root Dir: /var/lib/docker/aufs Backing Filesystem: extfs Dirs: 1 Dirperm1 Supported: true可以看到 Storage Driver 已经变为 aufs,证明配置已经生效,配置生效后就可以使用 AUFS 为 Docker 提供联合文件系统了。
AUFS 是联合文件系统,意味着它在主机上使用多层目录存储,每一个目录在 AUFS 中都叫作分支,而在 Docker 中则称之为层(layer),但最终呈现给用户的则是一个普通单层的文件系统,我们把多层以单一层的方式呈现出来的过程叫作联合挂载。
如图所示,每一个镜像层和容器层都是 /var/lib/docker 下的一个子目录,镜像层和容器层都在 aufs/diff 目录下,每一层的目录名称是镜像或容器的 ID 值,联合挂载点在 aufs/mnt 目录下,mnt 目录是真正的容器工作目录。下面我们针对 aufs 文件夹下的各目录结构,在创建容器前后的变化做详细讲述。
当一个镜像未生成容器时,AUFS 的存储结构如下。
当一个镜像已经生成容器时,AUFS 存储结构会发生如下变化。
AUFS 的工作过程中对文件的操作分为读取文件和修改文件。下面我们分别来看下 AUFS 对于不同的文件操作是如何工作的。
当我们在容器中读取文件时,可能会有以下场景。• 文件在容器层中存在时:当文件存在于容器层时,直接从容器层读取。• 当文件在容器层中不存在时:当容器运行时需要读取某个文件,如果容器层中不存在时,则从镜像层查找该文件,然后读取文件内容。• 文件既存在于镜像层,又存在于容器层:当我们读取的文件既存在于镜像层,又存在于容器层时,将会从容器层读取该文件。(由于写时复制,所以此时肯定是修改过的文件才会复制到容器层,所以应该读取容器层的文件)
AUFS 对文件的修改采用的是写时复制的工作机制,这种工作机制可以最大程度节省存储空间。具体的文件操作机制如下。
AUFS 写时复制的操作将会复制整个文件,如果文件过大,将会大大降低文件系统的性能,因此当我们有大量文件需要被修改时,AUFS 可能会出现明显的延迟。好在,写时复制操作只在第一次修改文件时触发,对日常使用没有太大影响。
Devicemapper 是 Linux 内核提供的框架,从 Linux 内核 2.6.9 版本开始引入,Devicemapper 与 AUFS 不同,AUFS 是一种文件系统,而Devicemapper 是一种映射块设备的技术框架。
Devicemapper 提供了一种将物理块设备映射到虚拟块设备的机制,目前 Linux 下比较流行的 LVM (Logical Volume Manager 是 Linux 下对磁盘分区进行管理的一种机制)和软件磁盘阵列(将多个较小的磁盘整合成为一个较大的磁盘设备用于扩大磁盘存储和提供数据可用性)都是基于 Devicemapper 机制实现的。
Devicemapper 将主要的工作部分分为用户空间和内核空间。
这个架构类似于 C/S (客户端/服务区)架构的工作模式,客户端负责具体的规则定义和配置下发,服务端根据客户端配置的规则来执行具体的处理任务。
Devicemapper 的工作机制主要围绕三个核心概念。

Devicemapper 三个核心概念之间的关系如图 1,映射设备通过映射表关联到具体的物理目标设备。事实上,映射设备不仅可以通过映射表关联到物理目标设备,也可以关联到虚拟目标设备,然后虚拟目标设备再通过映射表关联到物理目标设备。
Devicemapper 在内核中通过很多模块化的映射驱动(target driver)插件实现了对真正 IO 请求的拦截、过滤和转发工作,比如 Raid、软件加密、瘦供给(Thin Provisioning)等。其中瘦供给模块是 Docker 使用 Devicemapper 技术框架中非常重要的模块,下面我们来详细了解下瘦供给(Thin Provisioning)。
瘦供给的意思是动态分配,这跟传统的固定分配不一样。传统的固定分配是无论我们用多少都一次性分配一个较大的空间,这样可能导致空间浪费。而瘦供给是我们需要多少磁盘空间,存储驱动就帮我们分配多少磁盘空间。
这种分配机制就好比我们一群人围着一个大锅吃饭,负责分配食物的人每次都给你一点分量,当你感觉食物不够时再去申请食物,而当你吃饱了就不需要再去申请食物了,从而避免了食物的浪费,节约的食物可以分配给更多需要的人。
你知道 Docker 是如何使用瘦供给来做到像 AUFS 那样分层存储文件的吗?答案就是: Docker 使用了瘦供给的快照(snapshot)技术。
什么是快照(snapshot)技术?
简单来说,快照是数据在某一个时间点的存储状态。快照的主要作用是对数据进行备份,当存储设备发生故障时,可以使用已经备份的快照将数据恢复到某一个时间点,而基于Devicemapper的Docker 中的数据分层存储也是基于快照实现的。
当 Docker 使用 Devicemapper 作为文件存储驱动时,Docker 将镜像和容器的文件存储在瘦供给池(thinpool)中,并将这些内容挂载在 /var/lib/docker/devicemapper/ 目录下。
这些目录储存 Docker 的容器和镜像相关数据,目录的数据内容和功能说明如下。
Devicemapper 使用专用的块设备实现镜像的存储,并且像 AUFS 一样使用了写时复制的技术来保障最大程度节省存储空间,所以 Devicemapper 的镜像分层也是依赖快照来实现的。
Devicemapper 的每一镜像层都是其下一层的快照,最底层的镜像层是我们的瘦供给池,通过这种方式实现镜像分层有以下优点:
当我们需要读取数据时,如果数据存在底层快照中,则向底层快照查询数据并读取。当我们需要写数据时,则向瘦供给池动态申请存储空间生成读写层,然后把数据复制到读写层进行修改。Devicemapper 默认每次申请的大小是 64K 或者 64K 的倍数,因此每次新生成的读写层的大小都是 64K 或者 64K 的倍数。
以下是一个运行中的 Ubuntu 容器示意图。
这个 Ubuntu 镜像一共有四层,每一层镜像都是下一层的快照,镜像的最底层是基础设备的快照。当容器运行时,容器是基于镜像的快照。综上,Devicemapper 实现镜像分层的根本原理就是快照。
OverlayFS 的发展分为两个阶段。2014 年,OverlayFS 第一个版本被合并到 Linux 内核 3.18 版本中,此时的 OverlayFS 在 Docker 中被称为overlay文件驱动。由于第一版的overlay文件系统存在很多弊端(例如运行一段时间后Docker 会报 "too many links problem" 的错误), Linux 内核在 4.0 版本对overlay做了很多必要的改进,此时的 OverlayFS 被称之为overlay2。
因此,在 Docker 中 OverlayFS 文件驱动被分为了两种,一种是早期的overlay,不推荐在生产环境中使用,另一种是更新和更稳定的overlay2,推荐在生产环境中使用。下面的内容我们主要围绕overlay2展开。
overlay2虽然很好,但是它的使用是有一定条件限制的。
xfs_info / | grep ftypenaming =version 2 bsize=4096 ascii-ci=0, ftype=1, parent=0当输出结果中有 ftype=1 时,表示 d_type 已经开启。如果你的输出结果为 ftype=0,则需要重新格式化磁盘目录,命令如下:
mkfs.xfs -f -n ftype=1 /path/to/disk另外,在生产环境中,推荐挂载 /var/lib/docker 目录到单独的磁盘或者磁盘分区,这样可以避免该目录写满影响主机的文件写入,并且把挂载信息写入到 /etc/fstab,防止机器重启后挂载信息丢失。
挂载配置中推荐开启 pquota,这样可以防止某个容器写文件溢出导致整个容器目录空间被占满。写入到 /etc/fstab 中的内容如下:
$UUID /var/lib/docker xfs defaults,pquota 0 0其中 UUID 为 /var/lib/docker 所在磁盘或者分区的 UUID 或者磁盘路径。如果你的操作系统无法满足上面的任何一个条件,那我推荐你使用 AUFS 或者 Devicemapper 作为你的 Docker 文件系统驱动。
通常情况下, overlay2 会比 AUFS 和 Devicemapper 性能更好,而且更加稳定,因为 overlay2 在 inode 优化上更加高效。因此在生产环境中推荐使用 overlay2 作为 Docker 的文件驱动。
overlay2 和 AUFS 类似,它将所有目录称之为层(layer),overlay2 的目录是镜像和容器分层的基础,而把这些层统一展现到同一的目录下的过程称为联合挂载(union mount)。overlay2 把目录的下一层叫作lowerdir,上一层叫作upperdir,联合挂载后的结果叫作merged。
总体来说,overlay2 是这样储存文件的: overlay2将镜像层和容器层都放在单独的目录,并且有唯一 ID,每一层仅存储发生变化的文件,最终使用联合挂载技术将容器层和镜像层的所有文件统一挂载到容器中,使得容器中看到完整的系统文件。
overlay2 文件系统最多支持 128 个层数叠加,也就是说你的 Dockerfile 最多只能写 128 行,不过这在日常使用中足够了。
overlay2 的工作过程中对文件的操作分为读取文件和修改文件。
容器内进程读取文件分为以下三种情况。
overlay2 对文件的修改采用的是写时复制的工作机制,这种工作机制可以最大程度节省存储空间。具体的文件操作机制如下。
overlay2 写时复制的操作将会复制整个文件,如果文件过大,将会大大降低文件系统的性能,因此当我们有大量文件需要被修改时,overlay2 可能会出现明显的延迟。好在,写时复制操作只在第一次修改文件时触发,对日常使用没有太大影响。
overlay2 目前已经是 Docker 官方推荐的文件系统了,也是目前安装 Docker 时默认的文件系统,因为 overlay2 在生产环境中不仅有着较高的性能,它的稳定性也极其突出。但是 overlay2 的使用还是有一些限制条件的,例如要求 Docker 版本必须高于 17.06.02,内核版本必须高于 4.0 等。因此,在生产环境中,如果你的环境满足使用 overlay2 的条件,请尽量使用 overlay2 作为 Docker 的联合文件系统。
作者简介:我是明哥,一个从286时代一路玩到现在,依然对计算机上头的40岁老玩家。从小通过玩游戏喜欢上了计算机,没想到从沉迷游戏的少年,到守护系统运行稳定的运维主管,变的是角色,不变的是对计算机世界最纯粹的好奇与热爱。这个公众号是我的“学习笔记”。我会在这里分享:如何高效阅读、如何构建知识体系、如何把知识变成收入等等内容。
往期推荐:
Docker学习笔记:什么是容器技术?容器核心技术都有什么?
Docker学习笔记:什么是容器技术?容器平台技术都包含有什么内容?
Docker学习笔记:容器是什么?为什么需要容器?Docker对比虚拟机
【Docker学习笔记】:容器技术的内核基石-Linux Namespace技术
【软件工具分享】VMware Workstation Pro 17 25H2 下载和安装及汉化指南
【软件工具分享】VMware Workstation Pro 17 虚拟网络配置 6000字图文教程一文教会如何配置网络
【Docker学习笔记】VMware workstation -CentOS Stream 10 详细安装教程(图文详解版)
【Docker学习笔记】VMware workstation Ubuntu Server 24.04 图文详细安装教程
如果觉得内容对你有帮助,请务必点个【关注】和【赞】和【转发】,支持 明哥持续创作,并且不会错过后续内容!