docker Device Mapper 简介
1. 简介
Device Mapper
是 linux 的内核用来将块设备映射到虚拟块设备的 framework,它支持许多高级卷管理技术。docker 的 devicemapper 存储驱动程序利用此框架的自动精简配置(thin provisioning
) 和快照功能来管理 docker 镜像和容器。本文将 Device Mapper
存储驱动称为 devicemapper
,将它的内核框架称为 Device Mapper
。
Device Mapper
不同于 AUFS
、ext4
、NFS
等,因为它并不是一个文件系统(File System),而是 Linux 内核映射块设备的一种技术框架。提供的一种从逻辑设备(虚拟设备)到物理设备的映射框架机制,在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略。
当前比较流行的 Linux 下的逻辑卷管理器如 LVM2
(Linux Volume Manager 2 version)、EVMS
(Enterprise Volume Management System)、dmraid
(Device Mapper Raid Tool)等都是基于该机制实现的。
值得一提的是 Device Mapper 工作在块级别(block
),并不
工作在文件级别(file)。Device Mapper 自 Linux 2.6.9 后编入 Linux 内核,所有基于 Linux 内核 2.6.9 以后的发行版都内置 Device Mapper,但你需要进行一些额外的配置才能在 docker 中使用它。比如在 RHEL 和 CentOS 系统中,docker 默认使用的存储驱动是 overlay。
devicemapper
存储驱动使用专用于 docker 的块设备,它运行在块级别上而不是文件级别。使用块设备比直接使用文件系统性能更好,通过向 Docker 的宿主机添加物理存储可以扩展块设备的存储空间。
2. 用户空间和内核空间
Device Mapper
主要分为用户空间部分和内核空间部分
用户空间相关部分主要负责配置具体的策略和控制逻辑,比如逻辑设备和哪些物理设备建立映射,怎么建立这些映射关系等,包含 device mapper 库和 dmsetup 工具。对用户空间创建删除 device mapper 设备的操作进行封装。
内核中主要提供完成这些用户空间策略所需要的机制,负责具体过滤和重定向 IO 请求。通过不同的驱动插件,转发 IO 请求至目的设备上。附上 Device Mapper 架构图。
3. Device Mapper 技术分析
Device Mapper
作为 Linux 块设备映射技术框架,向外部提供逻辑设备。包含三个重要概念,映射设备(mapped device
),映射表(map table
),目标设备(target device
)。
映射设备即对外提供的逻辑设备,映射设备向下寻找必须找到支撑的目标设备。
映射表存储映射设备和目标设备的映射关系。
目标设备可以是映射设备或者物理设备,如果目标设备是一块映射设备,则属于嵌套,理论上可以无限迭代下去。
简而言之,Device Mapper 对外提供一个虚拟设备供使用,而这块虚拟设备可以通过映射表找到相应的地址,该地址可以指向一块物理设备,也可以指向一个虚拟设备。
映射表,是由用户空间创建,传递到内核空间。映射表里有映射设备逻辑的起始地址、范围、和表示在目标设备所在物理设备的地址偏移量以及Target 类型等信息(注:这些地址和偏移量都是以磁盘的扇区为单位的,即 512 个字节大小,所以,当你看到 128 的时候,其实表示的是 128*512=64K)。
映射驱动在内核空间是插件,Device Mapper
在内核中通过一个一个模块化的 Target Driver 插件实现对 IO 请求的过滤或者重新定向等工作,当前已经实现的插件包括软 Raid
、加密、多路径、镜像、快照等,这体现了在 Linux 内核设计中策略和机制分离的原则。Device Mapper
中的 IO 流处理,从虚拟设备(逻辑设备)根据映射表并指定特定的映射驱动转发到目标设备上。
4. Docker 中的 Device Mapper 核心技术
Docker
的 devicemapper
驱动有三个核心概念,copy on-write
(写复制),thin-provisioning
(精简配置)。snapshot
(快照),首先简单介绍一下这三种技术。
CoW(copy on write)
写复制:一些文件系统提供的写时复制策略。
aufs
的 cow 原理如下:
当容器需要修改一个文件,而该文件位于低层 branch 时,顶层 branch 会直接复制低层 branch 的文件至顶层再进行修改,而低层的文件不变,这种方式即是 CoW 技术(写复制)。
当容器删除一个低层 branch 文件时,只是在顶层 branch 对该文件进行重命名并隐藏,实际并未删除文件,只是不可见。
下图所示,容器层所见 file1 文件为镜像层文件,当需要修改 file1 时,会从镜像层把文件复制到容器层,然后进行修改,从而保证镜像层数据的完整性和复用性。
下图所示,当需要删除 file1 时,由于 file1 是镜像层文件,容器层会创建一个 .wh 前置的隐藏文件,从而实现对 file1 的隐藏,实际并未删除 file1,从而保证镜像层数据的完整性和复用性。devicemapper
支持在块级别(block
)写复制。
Snapshot
(快照技术):关于指定数据集合的一个完全可用拷贝,该拷贝包括相应数据在某个时间点(拷贝开始的时间点)的映像。快照可以是其所表示的数据的一个副本,也可以是数据的一个复制品。而从具体的技术细节来讲,快照是指向保存在存储设备中的数据的引用标记或指针。
Thin-provisioning
(精简配置),直译为精简配置。Thin-provisioning
是动态分配,需要多少分配多少,区别于传统分配固定空间从而造成的资源浪费。
它是什么意思呢?你可以联想一下我们计算机中的内存管理中用到的——“虚拟内存技术”——操作系统给每个进程 N 多 N 多用不完的内址地址(32 位下,每个进程可以有最多 2GB 的内存空间),但是呢,我们知道,物理内存是没有那么多的,如果按照进程内存和物理内存一一映射来玩的话,那么,我们得要多少的物理内存啊。所以,操作系统引入了虚拟内存的设计,意思是,我逻辑上给你无限多的内存,但是实际上是实报实销,因为我知道你一定用不了那么多,于是,达到了内存使用率提高的效果。(今天云计算中很多所谓的虚拟化其实完全都是在用和“虚拟内存”相似的 Thin Provisioning 的技术,所谓的超配,或是超卖)。
好了,话题拉回来,我们这里说的是存储。看下面两个图,第一个是 Fat Provisioning
,第二个是 Thin Provisioning,其很好的说明了是个怎么一回事(和虚拟内存是一个概念)。
下图中展示了某位用户向服务器管理员请求分配 10TB 的资源的情形。实际情况中这个数值往往是峰值,根据使用情况,分配 2TB 就已足够。因此,系统管理员准备 2TB 的物理存储,并给服务器分配 10TB 的虚拟卷。服务器即可基于仅占虚拟卷容量 1/5 的现有物理磁盘池开始运行。这样的“始于小”方案能够实现更高效地利用存储容量。
那么,Docker 是怎么使用 Thin Provisioning
这个技术做到像 UnionFS 那样的分层镜像的呢?答案是,Docker 使用了 Thin Provisioning
的 Snapshot
的技术。