共计 7769 个字符,预计需要花费 20 分钟才能阅读完成。
Device Mapper 是一个基于 kernel 的框架,它增强了很多 Linux 上的高级卷管理技术。Docker 的 devicemapper 驱动在镜像和容器管理上,利用了该框架的超配和快照功能。为了区别,本文使用 Device Mapper 指驱动中的框架,而 devicemapper 指 Docker 的存储驱动。
注意:商业支持的 Docker Engine(CS-Engine)建议在 RHEL 和 CentOS 上使用 devicemapper 存储驱动。
AUFS 之外的另一种选择
Docker 最初运行在 Ubuntu 和 Devian 上,并且使用 AUFS 作为存储后端。当 Docker 变得流行后,很多想使用它的公司正在使用 RHEL。不幸的是,因为 Linux 主线 kernel 并不包含 AUFS,所以 RHEL 并没有支持 AUFS。
为了改变这种情况,Red Hat 开发者研究将 AUFS 包含进 kernel 主线。最后,他们认为开发一种新的存储后端是更好的主意。此外,他们打算使用已经存在的 Device Mapper 技术作为新存储后端的基础。
Red Hat 与 Docker 公司合作贡献这个新驱动。因为这次合作,Docker Engine 被重新设计为存储驱动插件化。因此 devicemapper 成为 Docker 支持的第二个存储驱动。
Device Mapper 在 2.6.9 之后就被何如 Linux kernel 主线,也是 RHEL 家族发布包的核心部分。这意味着 devicemapper 存储驱动基于稳定的代码,有着大量的工作产品和极强的社区支持。
镜像分层和共享
devicemapper 驱动把每个镜像和容器都存储在它自己的虚拟设备中,这些设备是超配的 copy-on-write 快照设备。Device Mapper 技术工作在块级别而不是文件级别,也就是说 devicemapper 存储驱动的超配和 copy-on-write 操作直接操作块,而不是整个文件。
注意:快照也是使用的 thin 设备或者虚拟设备。
使用 devicemapper 时,Docker 如下创建镜像:
▶devicemapper 存储驱动创建一个 thin 池。
这个池是在块设备或者 loop mounted sparse file 上创建的。
▶然后创建一个基础设备。
该基础设备是一个带文件系统的 thin 设备。可以通过 docker info 命令中的 Backing filesystem 值,来查看后端使用的是哪种文件系统。
▶每个新的镜像(和镜像层)都是该基础设备的快照。
这些都是超配的 copy-on-write 快照。也就是说它们初始化的时候是空的,只有在有数据写向它们时,才会从池中消耗空间。
使用 devicemapper,容器层是基于镜像的快照。和镜像一样,容器快照也是超配的 copy-on-write 快照。容器快照保存了容器的所有更新,当向容器写数据时,devicemapper 按需从池中分配空间给容器层。
下图展示了一个 thin 池、基础设备和两个镜像。
如果你仔细观察图片,你会发现每个镜像层都是它下层的快照,镜像的最底层是池中基础设备的快照。这个基础设备是 Device Mapper 产生的,并不是一个 Docker 镜像层。
而容器又是镜像的一个快照,下图展示了两个容器在整个存储驱动的层次。
devicemapper 的读操作
下图显示了在一个容器中读取一个块(地址是 0x44f)的过程。
▶应用在容器中请求访问块 0x44f。
因为容器是镜像的一个 thin 快照,它没有实际的数据。但它有指针,指向镜像栈中数据所在的镜像快照。
▶存储驱动根据指针找到对应镜像层 a005 的快照块 0xf33。
▶devicemapper 拷贝镜像快照中块 0xf33 的数据到容器内存中。
▶容器驱动将数据返回给请求应用。
写操作
使用 devicemapper 驱动,写数据到容器是通过一个按需分配操作来完成的。更新已有的数据使用了 copy-on-write 操作。不过 Device Mapper 是基于块存储的技术,所以这些操作都发生在块的级别。
例如,要对容器中的一个大文件作小的修改,devicemapper 驱动不拷贝整个文件,它只拷贝要修改的内容对应的块,每个块大小为 64KB。
写数据
向容器写 55KB 的数据:
▶应用向容器发起写 56KB 数据的请求;
▶按需分配操作给容器快照分配了一个新的 64KB 的块。
如果写的数据大于 64KB,就需要分配多个块给容器快照。
▶数据写向新分配的块。
覆写已有数据
第一次修改已有的数据:
▶应用向容器发起修改数据的请求;
▶copy-on-write 操作定位到需要更新的块;
▶分配新的块给容器快照,并拷贝数据到这些块中;
▶修改的数据写向新分配的这些块。
容器中的应用并不会感知到这些按需分配和 copy-on-write 操作。然而,这些操作还是可能会对应用的读写带来一些延迟。
Docker 中配置 devicemapper
devicemapper 是部分 Linux 发行版的默认 Docker 存储驱动,这其中就包括 RHEL 和它的分支。当前,以前发行版支持该驱动:
●RHEL/CentOS/Fedora
●Ubuntu
●Debian
●Arch Linux
Docker host 使用 loop-lvm 的配置模式来运行 devicemapper。该模式使用系数文件来创建 thin 池,这些池用于镜像和容器快照。并且这些模式是开箱即用的,无需额外配置。不过,不建议在产品部署中使用 loop-lvm 模式。
可以通过 docker info 命令来查看是否使用了该模式。
$ sudo docker info
Containers: 0
Images: 0
Storage Driver: devicemapper
Pool Name: docker-202:2-25220302-pool
Pool Blocksize: 65.54 kB
Backing Filesystem: xfs
[...]
Data loop file: /var/lib/docker/devicemapper/devicemapper/data
Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata
Library Version: 1.02.93-RHEL7 (2015-01-28)
[...]
通过上面输出可得出,Docker host 使用 devicemapper 存储驱动。并且,还使用了 loop-lvm 模式,因为 /var/lib/docker/devicemapper/devicemapper 下有 Data loop file 和 Metadata loop file 这两个文件。这些都是 loopback 映射的稀疏文件。
在产品中配置 direct-lvm 模式
产品部署中应该使用 direct-lvm 模式,该模式使用块设备来创建 thin 池。下面的步骤描述了如何在 Docker host 使用 direct-lvm 模式得 devicemapper 存储驱动。
注意:如果你已经在 Docker host 上运行了 Docker daemon,并且有一些想保存的镜像,那么在执行以下步骤之前,把它们 push 到 Docker Hub,或者你的私有 Docker Registry。
以下步骤会创建一个逻辑卷,配置为 thin 池,用作后端的存储池。假设你有一个稀疏块设备 /dev/xvdf,并且该设备有足够的空间来完成这个任务。在你的环境中,设备标识符和卷的大小可能不同,在你执行下面过程时,你应该替换为适合你环境的值。另外,以下的步骤应该在 Docker Daemon 停止的时候来执行。
1) 进入 Docker host,并停止 Docker daemon;
2) 安装 LVM2 和 thin-provisioning-tools 安装包;
LVM2 安装包提供了用户空间的工具,用于管理逻辑卷。
thin-provisioning-tools 用于激活和管理池。
# on Ubuntu
$ sudo apt -y install lvm2
# On CentOS
$ sudo yum install -y lvm2
3) 创建一个物理卷 /dev/xvdf 来替换块设备;
$ pvcreate /dev/xvdf
4) 创建卷组 docker;
$ vgcreate docker /dev/xvdf
5) 创建名为 thinpool 和 thinpoolmeta 的虚拟卷;
在该示例中,数据大小是 docker 卷组大小的 95%,留下这些空闲空间,是用于数据或元数据的自动扩容。
$ lvcreate --wipesignatures y -n thinpool docker -l 95%VG
$ lvcreate --wipesignatures y -n thinpoolmeta docker -l 1%VG
6) 把池转换为 thin 池;
$ lvconvert -y --zero n -c 512K --thinpool docker/thinpool --poolmetadata docker/thinpoolmeta
7) 通过 lvm 文件来配置 thin 池的自动扩容;
$ vi /etc/lvm/profile/docker-thinpool.profile
8) 指定 thin_pool_autoextend_threshold 值;
该值是 lvm 尝试扩容到可用空间时,当前已空间使用量的百分比(100= 禁止)。
thin_pool_autoextend_threshold = 80
9) 修改 thin_pool_autoextend_percent;
该值是 thin 池要扩容的百分比(100= 禁止)。
thin_pool_autoextend_percent = 20
10) 检查上面的步骤,你的 docker-thinpool.profile 文件应该是类似下面的内容:
一个 /etc/lvm/profile/docker-thinpool.profile 示例文件:
activation {
thin_pool_autoextend_threshold=80
thin_pool_autoextend_percent=20
}
11) 应用新的 lvm profile 文件;
$ lvchange --metadataprofile docker-thinpool docker/thinpool
12) 确认 lv 是否被修改;
$ lvs -o+seg_monitor
13) 如果之前 Docker daemon 被启动过,那么需要将之前的 graph driver 目录给挪走;
移动 graph driver 会删除所有的镜像容器和卷。下面的命令将 /var/lib/docker 的内容移动到另一个目录中。
$ mkdir /var/lib/docker.bk
$ mv /var/lib/docker/* /var/lib/docker.bk
14) 使用特殊的 devicemapper 选项来配置 Docker daemon;
有两种方法来配置 Docker daemon 的 devicemapper 存储驱动。你可以运行 daemon 时加上以下参数:
--storage-driver=devicemapper \
--storage-opt=dm.thinpooldev=/dev/mapper/docker-thinpool \
--storage-opt=dm.use_deferred_removal=true \
--storage-opt=dm.use_deferred_deletion=true
也可以在 daemon 配置文件中配置,如默认的配置文件 /etc/docker/daemon.json 中,可如下配置:
{"storage-driver": "devicemapper",
"storage-opts": ["dm.thinpooldev=/dev/mapper/docker-thinpool",
"dm.use_deferred_removal=true",
"dm.use_deferred_deletion=true"
]
}
注意:总是使用 dm.use_deferred_removal=true 和 dm.use_deferred_deletion=true 选项,以防止无意地泄露映射资源信息。
15)(可选的)如果使用了 systemd,并且修改了 daemon 配置文件,需要重载 systemd 信息;
$ systemctl daemon-reload
16) 重启 Docker daemon。
$ systemctl start docker
当你启动 Docker daemon 后,确保一直监控者 thin 池和卷组的可用空间。当卷组自动扩容时,可能会占满所有空间。可以使用 lvs 或 lvs - a 命令来监控逻辑卷,可以查看到数据和元数据的大小。另外,还可以使用 vgs 命令来监控卷组的可用空间。
当到达阈值后,日志会显示 thin 池的自动扩容信息,可使用以下命令来查看日志:
$ journalctl -fu dm-event.service
当你确认你的配置文件无误时,就可以删除之前的备份目录了。
$ rm -rf /var/lib/docker.bk
你还可以使用 dm.min_free_space 参数。该值保证党可用空间到达或者接近最小值时,操作失败会有提示。可以查看更多的驱动选项。
检查 devicemapper 结构体
可以通过 lsblk 命令查看 devicemapper 存储驱动创建的 pool 相关的设备文件。
$ sudo lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 G 0 disk
└─xvda1 202:1 0 G 0 part /
xvdf 202:80 0 0G 0 disk
├─vg--docker-data 253:0 0 0G 0 lvm
│ └─docker-202:1-1032-pool 253:2 0 0G 0 dm
└─vg--docker-metadata 253:1 0 G 0 lvm
└─docker-202:1-1032-pool 253:2 0 0G 0 dm
下图显示了上面示例的镜像信息。
图中,pool 的名称叫做 Docker-202:1-1032-pool,横跨 data 和 metadata 两个设备。devicemapper 的 pool 名称格式为:
Docker-MAJ:MIN-INO-pool
MAJ,MIN 和 INFO 分别表示 major、minor 设备号,和 inode 号。
有两个主要的目录。/var/lib/docker/devicemapper/mnt 目录包含镜像和容器层的映射点。/var/lib/docker/devicemapper/metadata 目录为每个镜像层和容器快照都对应了一个文件,文件以 JSON 格式保存每个快照的元数据。
Device Mapper 和 Docker 性能
理解按需分配和 copy-on-write 可以让你对容器性能有个整体的了解。
按需分配性能影响
devicemapper 存储驱动在按需分配操作时会给容器分配一个新的块。也就是说每次应用向容器某个位置写数据时,一个或多个块就会从池里面分配,并且映射给容器。
所有的块都是 64KB 大小,一个小于 64KB 的写请求也会导致分配 64KB 的块,大于 64KB 的写请求就要求多个 64KB 的块。这会影响容器的性能,尤其容器有很多小的写请求时。然而,一旦一个块分配给容器后,后续的读写都会直接操作这个新分配的块。
Copy-on-write 性能影响
每次容器第一次更新已有数据时,devicemapper 存储驱动都会执行 copy-on-write 操作,该操作会将数据从镜像快照拷贝到容器快照。这在容器性能上有着明显的影响。
所有的 copy-on-write 操作都有 64KB 的粒度。因此,要修改 1GB 文件的 32KB 内容时,只需要拷贝 64KB 大小的块到容器即可。如果要拷贝整个大文件到容器层时,该特性较于文件层的 copy-on-write 操作有着明显的性能优势。
然而,实际中,如果容器执行大量的小的写请求时(<64KB),devicemapper 性能差于 AUFS。
其他 device mapper 性能注意事项
还有一些点会影响 devicemapper 存储驱动的性能。
▶模式。Docker 运行 devicemapper 存储驱动的默认模式是 loop-lvm。该模式使用稀疏文件,并且性能堪忧。在产品中不建议使用该模式,产品环境中建议使用 direct-lvm,这样存储驱动就可以直接写向块设备。
▶高速存储。为了更好的性能,可以将 Data 文件和 Metadata 文件放在高速存储(如 SSD)上。当然,也可以直接连接到 SAN 或 NAS array 上。
▶内存使用。devicemapper 不是内存最高效的 Docker 存储驱动。启动同一个容器的 n 份拷贝需要将其文件大小的 n 份拷贝加载到内存中,这对于 Docker host 的内存有一定影响。因此,devicemapper 存储驱动可能不是 Pass 或其他高密度用例的最优方案。
最后一点,数据卷提供了更好和可预测的性能。因此,应该将负载高的写请求写到数据卷中。
更多 Docker 相关教程见以下内容:
Docker 安装应用(CentOS 6.5_x64) http://www.linuxidc.com/Linux/2014-07/104595.htm
Ubuntu 14.04 安装 Docker http://www.linuxidc.com/linux/2014-08/105656.htm
Ubuntu 使用 VNC 运行基于 Docker 的桌面系统 http://www.linuxidc.com/Linux/2015-08/121170.htm
阿里云 CentOS 6.5 模板上安装 Docker http://www.linuxidc.com/Linux/2014-11/109107.htm
Ubuntu 15.04 下安装 Docker http://www.linuxidc.com/Linux/2015-07/120444.htm
在 Ubuntu Trusty 14.04 (LTS) (64-bit)安装 Docker http://www.linuxidc.com/Linux/2014-10/108184.htm
在 Ubuntu 15.04 上如何安装 Docker 及基本用法 http://www.linuxidc.com/Linux/2015-09/122885.htm
Ubuntu 16.04 上 Docker 使用手记 http://www.linuxidc.com/Linux/2016-12/138490.htm
Docker 的详细介绍:请点这里
Docker 的下载地址:请点这里
本文永久更新链接地址:http://www.linuxidc.com/Linux/2017-04/142522.htm