Device Mapper 介绍
Device Mapper 是 Linux2.6 内核中支持逻辑卷管理的通用设备映射机制,它为实现用于存储资源管理的块设备驱动提供了一个高度模块化的内核架构。整个 device mapper 机制由两部分组成:
- 内核部分:主要提供完成存储策略所需要的机制
- 用户空间部分:device mapper 库以及它提供的 dmsetup 工具
内核部分包含三个重要的对象概念,Mapped Device、Mapping Table、Target device。
-
Mapped Device:是一个逻辑抽象,可以理解成为内核向外提供的逻辑设备,它通过Mapping Table描述的映射关系和 Target Device 建立映射;
-
Mapping Table:存有 Mapped Device 逻辑的起始地址、范围、和表示在 Target Device 所在物理设备的地址偏移量以及Target 类型等信息;
-
Target device:表示的是 Mapped Device 所映射的物理空间段,对 Mapped Device 所表示的逻辑设备来说,就是该逻辑设备映射到的一个物理设备;
Device Mapper在内核中通过一个一个模块化的 Target Driver 插件实现对 IO 请求的过滤或者重新定向等工作,当前已经实现的插件包括软 Raid、加密、多路径、镜像、快照等,在这诸多“插件”中,有一个东西叫Thin Provisioning Snapshot
,这是Docker使用Device Mapper中最重要的模块,称为devicemapper存储驱动
,它利用Device Mapper框架的精简置备和快照功能来管理镜像和容器。
Thin Provisioning Snapshot 简介
先说一下 Thin Provisioning
技术,这个技术是虚拟化技术中的一种,实现对存储空间的”超卖“能力。
而Docker使用了Thin Provisioning的Snapshot
的技术,在 docker 启动时,会在精简池(Thin Pool)中创建一个具有文件系统的 Base 设备,每一个新镜像
(和镜像数据层)是这个 base 设备的一个快照,而容器
数据层又是从其镜像创建的快照。
快照是写时复制(CoW)策略的实现,这意味着给定的文件或目录只有在容器被修改或删除时才会被复制到容器的可写层。
配置 direct-lvm 模式用于生产环境
docker默认安装时,devicemapper存储驱动程序配置的是loop-lvm模式,该模式使用两个稀疏文件作为回环设备,来存储镜像和容器的数据和元数据:
/var/lib/docker/devicemapper/devicemapper/data
/var/lib/docker/devicemapper/devicemapper/metadata
由于回环设备速度慢且资源密集,生产环境建议使用direct-lvm 模式。
做以下步骤(转自)前,请先停止docker。
1.由于使用的是虚拟机,先创建个新的分区(或其他块设备):
$ fdisk /dev/sda
按提示创建一个新的分区: /dev/sda4
2.通过pvcreate
命令在 /dev/sda4块设备上创建物理卷pv:
$ pvcreate /dev/sda4
Physical volume "/dev/sda4" successfully created.
3.通过 vgcreate 命令在同一个设备上创建名为 docker的卷组vg:
$ vgcreate docker /dev/sda4
Volume group "docker" successfully created
4.通过 lvcreate 命令创建两个名为 thinpool 和 thinpoolmeta 的逻辑卷lv:
最后一个参数指定可用空间的大小,以便在空间不足时自动扩展数据或元数据。这些是推荐值。
$ lvcreate --wipesignatures y -n thinpool docker -l 95%VG
Logical volume "thinpool" created.
$ lvcreate --wipesignatures y -n thinpoolmeta docker -l 1%VG
Logical volume "thinpoolmeta" created.
5.通过 lvconvert 命令把thinpool
数据卷和thinpoolmeta
元数据卷换为精简池(Thin Pool):
$ lvconvert -y \
--zero n \
-c 512K \
--thinpool docker/thinpool \
--poolmetadata docker/thinpoolmeta
Thin pool volume with chunk size 512.00 KiB can address at most 126.50 TiB of data.
WARNING: Converting docker/thinpool and docker/thinpoolmeta to thin pool's data and metadata volumes with metadata wiping.
THIS WILL DESTROY CONTENT OF LOGICAL VOLUME (filesystem etc.)
Converted docker/thinpool and docker/thinpoolmeta to thin pool.
6.通过 lvm 配置文件配置精简池的自动扩展:
$ vi /etc/lvm/profile/docker-thinpool.profile
指定 thin_pool_autoextend_threshold 和 thin_pool_autoextend_percent 的值。
-
thin_pool_autoextend_threshold 是触发 lvm 尝试自动扩展可用空间的空间使用率百分比(100 = 禁用,不推荐)。
-
thin_pool_autoextend_percent 是触发自动扩展时会扩展的大小(0 = 禁用)。
下面的例子在磁盘使用率达到 80% 时自动增加 20% 的容量:
activation {
thin_pool_autoextend_threshold=80
thin_pool_autoextend_percent=20
}
7.使用 lvchange 命令应用 LVM 配置文件:
$ lvchange --metadataprofile docker-thinpool docker/thinpool
Logical volume docker/thinpool changed.
8.启用对主机上逻辑卷的监视:
没有这一步,即使存在 LVM 配置文件,也不会自动扩展。
$ lvs -o+seg_monitor
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert Monitor
thinpool docker twi-a-t--- <19.00g 0.00 0.03 monitored
9.如果之前在这个主机上运行过 Docker,或 /var/lib/docker/ 文件已经存在,移除文件以便让 Docker 使用新的 LVM pool 来存储镜像和容器:
$ mkdir /var/lib/docker.bk
$ mv /var/lib/docker/* /var/lib/docker.bk
10.编辑 /etc/docker/daemon.json 并配置 devicemapper存储驱动程序所需的选择。增加下面的内容:
{
"storage-driver": "devicemapper",
"storage-opts": [
"dm.thinpooldev=/dev/mapper/docker-thinpool",
"dm.use_deferred_removal=true",
"dm.use_deferred_deletion=true"
]
}
11.启动 Docker:
$ systemctl start docker
12.通过 docker info 命令验证 Docker 使用了新的配置:
$ docker info
Client:
Debug Mode: false
Server:
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 19.03.8
Storage Driver: devicemapper
Pool Name: docker-thinpool
Pool Blocksize: 524.3kB
Base Device Size: 10.74GB
Backing Filesystem: xfs
Udev Sync Supported: true
Data Space Used: 20.45MB
Data Space Total: 20.4GB
Data Space Available: 20.38GB
Metadata Space Used: 61.44kB
Metadata Space Total: 213.9MB
Metadata Space Available: 213.8MB
Thin Pool Minimum Free Space: 2.039GB
......
如果 Docker 配置正确,Pool Name 应该是 docker-thinpool
。
13.可以使用lsblk命令从操作系统角度查看设备和对应的池:
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 41G 0 disk
├─sda1 8:1 0 300M 0 part /boot
├─sda2 8:2 0 2G 0 part [SWAP]
├─sda3 8:3 0 17.7G 0 part /
└─sda4 8:4 0 20G 0 part
├─docker-thinpool_tmeta 253:0 0 204M 0 lvm
│ └─docker-thinpool 253:2 0 19G 0 lvm
└─docker-thinpool_tdata 253:1 0 19G 0 lvm
└─docker-thinpool 253:2 0 19G 0 lvm
sr0 11:0 1 1024M 0 rom
dmsetup 工具
命令参考:dmsetup - Unix, Linux Command
常用参数:
dmsetup info [device_name]
: 输出所有目前配置的 Device Mapper 设备信息dmsetup ls
: 命令列出映射的设备的设备名称列表dmsetup status [device_name]
: 输出结果是所有目前配置的设备映射器设备信息dmsetup message device_name sector message
: 给设备发送消息dmsetup create device_name --table "0 20971520 thin 253:3 185"
: 创建dm设备dmsetup remove device_name
: 移除dm设备
dmsetup 工具的使用
查看运行容器、镜像的device信息
获取容器的DeviceName:
$ docker inspect ba3ee5be798c | grep DeviceName
"DeviceName": "docker-253:2-2016050192-752dc1a74ce95c22dae84e257aa02119bb1da1feb1a6ff372d4ad9db08cf7bd4",
其中docker-253:2-2016050192-752dc1a74ce95c22dae84e257aa02119bb1da1feb1a6ff372d4ad9db08cf7bd4
即为dm设备的名字。
查看Mapping Table:
$ dmsetup table | grep docker-253:2-2016050192-752dc1a74ce95c22dae84e257aa02119bb1da1feb1a6ff372d4ad9db08cf7bd4
docker-253:2-2016050192-752dc1a74ce95c22dae84e257aa02119bb1da1feb1a6ff372d4ad9db08cf7bd4: 0 20971520 thin 253:3 163
或
dmsetup table docker-253:2-2016050192-752dc1a74ce95c22dae84e257aa02119bb1da1feb1a6ff372d4ad9db08cf7bd4
0 20971520 thin 253:3 163
其中0 20971520 thin 253:3 163
的解释为:
logical_start_sector num_sectors target_type target_args
开始扇区 扇区数 设备类型 设备参数
0 20971520 thin 253:3 163
20971520
为扇区数,一个扇区大小为512kb,总共为10g
即:20971520个扇区 = 20971520*512kb = 10,737,418,240kb = 10 * 2^30 kb = 10g
取出镜像的文件
查看镜像的Device信息
$ docker inspect centos | grep Device
"DeviceId": "13",
"DeviceName": "docker-8:3-36654382-89b4c8a4c8d56fde69622bc6e8df5f402eebf4b0f026966b56c65de7cd4517c0",
"DeviceSize": "10737418240"
使用dmsetup ls
命令查看docker-thinpool
设备的主设备号和次设备号
$ dmsetup ls
centos-dm (253:4)
docker-thinpool_tdata (253:1)
docker-thinpool_tmeta (253:0)
docker-thinpool (253:2)
得到主设备号和次设备号为253:2
生成Mapping Table:0 20971520 thin 253:2 13
- 0 : 开始扇区
- 20971520 : 删除数,由DeviceSize除以每个扇区的大小得到,即 10737418240/512 = 20971520
- thin : 设备类型
- 253:2 13 : 设备参数,由
[主设备号]:[次设备号] [DeviceId]
组成
创建dm设备:
dmsetup create centos-dm --table "0 20971520 thin 253:2 13"
再次使用dmsetup ls
命令,发现新增的名为centos-dm
的dm设备:
$ dmsetup ls
centos-dm (253:4)
docker-thinpool_tdata (253:1)
docker-thinpool_tmeta (253:0)
docker-thinpool (253:2)
查看/dev/mapper目录,发现新增了1个链接文件,链接到dm-4设备
$ ll /dev/mapper
lrwxrwxrwx. 1 root root 7 Apr 26 02:55 centos-dm -> ../dm-4
把dm-4设备挂载出来
$ mount /dev/dm-4 /tmp/test
查看挂载:
$ mount | grep centos-dm
/dev/mapper/centos-dm on /tmp/test type xfs (rw,relatime,seclabel,attr2,inode64,sunit=1024,swidth=1024,noquota)
查看镜像文件:
$ ll /tmp/test
total 8
-rw-------. 1 root root 64 Apr 23 01:05 id
drwxr-xr-x. 14 root root 4096 Apr 23 01:05 rootfs
rootfs目录中即为镜像里的文件:
$ ll /tmp/test/rootfs/
lrwxrwxrwx. 1 root root 7 Apr 6 2017 bin -> usr/bin
drwxr-xr-x. 47 root root 4096 Apr 6 2017 etc
drwxr-xr-x. 2 root root 6 Nov 5 2016 home
lrwxrwxrwx. 1 root root 7 Apr 6 2017 lib -> usr/lib
lrwxrwxrwx. 1 root root 9 Apr 6 2017 lib64 -> usr/lib64
drwx------. 2 root root 6 Apr 6 2017 lost+found
drwxr-xr-x. 2 root root 6 Nov 5 2016 media
drwxr-xr-x. 2 root root 6 Nov 5 2016 mnt
取出运行中/停掉的容器中的内容
使用docker inspect
命令查看容器的device信息:
$ docker inspect {containerId} | grep Device
"DeviceId": "21",
"DeviceName": "docker-8:3-36654382-804429bdfdec16a1a3d6f3e5fbbb3dcbc4494997b8d392c83a37aead924a398c",
"DeviceSize": "10737418240"
之后的步骤与取出镜像的文件
的方法相同。
给运行中的容器进行快照
使用docker inspect
命令查看容器的DeviceName和DeviceId:
$ docker inspect {containerId} | grep Device
"DeviceId": "21",
"DeviceName": "docker-8:3-36654382-804429bdfdec16a1a3d6f3e5fbbb3dcbc4494997b8d392c83a37aead924a398c",
"DeviceSize": "10737418240"
向dm设备发送create_snap
的消息:
dmsetup message /dev/mapper/docker-thinpool 0 "create_snap 22 21"
其中:
- 21 : 为容器的DeviceId
- 22 : 为自定的目标DeviceId
使用dmsetup create
命令创建dm设备:
$ dmsetup create dockersnap --table "0 20971520 thin /dev/mapper/docker-thinpool 22"
使用dmsetup ls
命令,发现新增的名为dockersnap
的dm设备:
$ dmsetup ls
centos-dm (253:4)
docker-thinpool_tdata (253:1)
docker-thinpool_tmeta (253:0)
docker-thinpool (253:2)
docker-8:3-36654382-804429bdfdec16a1a3d6f3e5fbbb3dcbc4494997b8d392c83a37aead924a398c (253:3)
dockersnap (253:5)
接下来即可使用之前的步骤mount出来。
devicemapper存储驱动下统计容器使用量
由于容器启动后会对镜像进行一次快照,并mount到一个挂载点(在/var/lib/docker/devicemapper/mnt
目录中),可以考虑使用df
命令结果里的Used
字段:
$ df -hT
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda3 xfs 18G 4.5G 14G 26% /
devtmpfs devtmpfs 1.9G 0 1.9G 0% /dev
tmpfs tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs tmpfs 1.9G 9.1M 1.9G 1% /run
tmpfs tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
/dev/sda1 xfs 297M 144M 154M 49% /boot
tmpfs tmpfs 378M 0 378M 0% /run/user/0
/dev/dm-3 xfs 10G 57M 10G 1% /var/lib/docker/devicemapper/mnt/804429bdfdec16a1a3d6f3e5fbbb3dcbc4494997b8d392c83a37aead924a398c
可以看到/dev/dm-3
对应的挂载点/var/lib/docker/devicemapper/mnt/804429bdfdec16a1a3d6f3e5fbbb3dcbc4494997b8d392c83a37aead924a398c
,其Used
值为57M。
挂载点路径中有一串很长的hash串,这个串与DeviceName
中hash串相对应:
$ docker inspect 17a65165d9b6 | grep DeviceName
"DeviceName": "docker-8:3-36654382-804429bdfdec16a1a3d6f3e5fbbb3dcbc4494997b8d392c83a37aead924a398c",
由于格式化后的xfs
文件系统自身的metadata也会占用一定空间,所以这个Used
值往往比mount出的目录要大(使用du
命令进行统计):
$ cd \
/var/lib/docker/devicemapper/mnt/804429bdfdec16a1a3d6f3e5fbbb3dcbc4494997b8d392c83a37aead924a398c
$ du -sh *
4.0K id
24M rootfs
可见mount出的目录的总大小为24M,并不与Used
值相同。
当然,这个差异也与df
与du
命令的统计方法有关。
注意:在启动容器时使用-v
参数挂载出来的目录或文件,都不包含在df
与du
命令的统计结果中。