Device Mapper簡(jiǎn)介
DeviceMapper自Linux 2.6被引入熄捍。它在內(nèi)核中支持邏輯卷管理的通用設(shè)備映射機(jī)制荚醒,它為實(shí)現(xiàn)用于存儲(chǔ)資源管理的塊設(shè)備驅(qū)動(dòng)提供了一個(gè)高度模塊化的內(nèi)核和架構(gòu)千劈,包含三個(gè)重要的對(duì)象概念饰躲,MapperDevice翘贮,Mapping Table, Target device嘴纺。
Mapped Device 是一個(gè)邏輯抽象椎瘟,可以理解成為內(nèi)核向外提供的邏輯設(shè)備承璃,它通過(guò)Mapping Table描述的映射關(guān)系和Target Device建立映射。Target device表示的是Mapped Device所映射的物理空間段械蹋,對(duì)Mapped Device所表示邏輯設(shè)備來(lái)收出皇,就是該邏輯設(shè)備映射到的一個(gè)物理設(shè)備。
Mapping Table里有 Mapped Device 邏輯的起始地址哗戈、范圍郊艘、和表示在 Target Device 所在物理設(shè)備的地址偏移量以及Target 類(lèi)型等信息(注:這些地址和偏移量都是以磁盤(pán)的扇區(qū)為單位的,即 512 個(gè)字節(jié)大小唯咬,所以纱注,當(dāng)你看到128的時(shí)候,其實(shí)表示的是128*512=64K)胆胰。
DeviceMapper 中的邏輯設(shè)備Mapped Device不但可以映射一個(gè)或多個(gè)物理設(shè)備Target Device狞贱,還可以映射另一個(gè)Mapped Device,于是蜀涨,就是構(gòu)成了一個(gè)迭代或遞歸的情況瞎嬉,就像文件系統(tǒng)中的目錄里除了文件還可以有目錄,理論上可以無(wú)限嵌套下去厚柳。
devicemapper驅(qū)動(dòng)將每一個(gè)Docker鏡像和容器存儲(chǔ)在它自身的具有精簡(jiǎn)置備(thin-provisioned)氧枣、寫(xiě)時(shí)拷貝(copy-on-write)和快照功能(snapshotting)的虛擬設(shè)備上。由于Device Mapper技術(shù)是在塊(block)層面而非文件層面别垮,所以Docker Engine的devicemapper存儲(chǔ)驅(qū)動(dòng)使用的是塊設(shè)備來(lái)存儲(chǔ)數(shù)據(jù)而非文件系統(tǒng)便监。
Thin Provisioning 精簡(jiǎn)配置
Docker使用了Thin Provisioning的Snapshot的技術(shù)實(shí)現(xiàn)分層鏡像,
Thin Provisioning Snapshot 演示
首先碳想,我們需要先建兩個(gè)文件烧董,一個(gè)是data.img,一個(gè)是meta.data.img:
[root@localhost ~]# dd if=/dev/zero of=/tmp/data.img bs=1K count=1 seek=10M
1+0 records in
1+0 records out
1024 bytes (1.0 kB) copied, 0.000172451 s, 5.9 MB/s
[root@localhost ~]# dd if=/dev/zero of=/tmp/meta.data.img bs=1K count=1 seek=1G
1+0 records in
1+0 records out
1024 bytes (1.0 kB) copied, 0.000164882 s, 6.2 MB/s
注意命令中seek
選項(xiàng)胧奔,表示略過(guò)of
選項(xiàng)指定得輸出文件得前10M
個(gè)output的blocksize的空間后再寫(xiě)入內(nèi)容逊移。因?yàn)閎s是1個(gè)字節(jié),所以是10G的大小龙填,但其實(shí)在硬盤(pán)上沒(méi)有占用空間的螟左,占有空間只有1k的內(nèi)容啡浊。直到寫(xiě)入內(nèi)容時(shí)觅够,才會(huì)在硬盤(pán)上分配空間胶背。
用ls命令查看
[root@localhost tmp]# ls -lsh /tmp/data.img
4.0K -rw-r--r--. 1 root root 11G Jan 25 20:27 /tmp/data.img
[root@localhost tmp]# ls -lsh /tmp/meta.data.img
4.0K -rw-r--r--. 1 root root 1.1T Jan 25 20:27 /tmp/meta.data.img
創(chuàng)建loopback設(shè)備。
[root@localhost tmp]# losetup /dev/loop2015 /tmp/data.img
[root@localhost tmp]# losetup /dev/loop2016 /tmp/meta.data.img
[root@localhost tmp]# losetup -a
/dev/loop2015: [64768]:16788486 (/tmp/data.img)
/dev/loop2016: [64768]:16788157 (/tmp/meta.data.img)
為這個(gè)設(shè)備建一個(gè)Thin Provisioning的Pool喘先, 用dmsetup命令:
[root@localhost tmp]# dmsetup create test-thin-pool \
--table "0 20971522 thin-pool /dev/loop2016 /dev/loop2015 \
128 65536 1 skip_block_zeroing"
參數(shù)解釋如下(更多信息參看man pnage):
- dmsetup create 創(chuàng)建thin pool的命令
- test-thin-pool是自定義的一個(gè)pool名钳吟,不沖突就好。
- table是這個(gè)pool的參數(shù)設(shè)置
- 0代表起的sector位置
- 20971522代碼結(jié)句的sector號(hào)窘拯,前面說(shuō)過(guò)红且,一個(gè)sector是512字節(jié),所以涤姊,20971522個(gè)正好是10GB
- /dev/loop2016是meta文件的設(shè)備
- /dev/loop2015是data文件的設(shè)備
- 128是最小的可分配的sector數(shù)
- 65536是最少可用sector的water mark暇番,也就是一個(gè)threshold
- 1 代表有一個(gè)附加參數(shù)
- skip_block_zeroing是個(gè)附加參數(shù),表示略過(guò)用0填充的塊
然后思喊,就可以看到一個(gè)Device Mapper的設(shè)備:
[root@localhost tmp]# ll /dev/mapper/test-thin-pool
lrwxrwxrwx. 1 root root 7 Jan 25 20:30 /dev/mapper/test-thin-pool -> ../dm-2
接下來(lái)壁酬,創(chuàng)建一個(gè)Thin Provisioning的Volume:
[root@localhost tmp]# dmsetup message /dev/mapper/test-thin-pool 0 "create_thin 0"
[root@localhost tmp]# dmsetup create test-thin-volumn-001 --table "0 2097152 thin /dev/mapper/test-thin-pool 0"
期中:
- 第一個(gè)命令中的create_thin是關(guān)鍵字,后面的0表示這個(gè)Volume的device的id
- 第二個(gè)命令恨课,是真正的為這個(gè)Volumn創(chuàng)建一個(gè)可以mount的設(shè)備舆乔,名字叫test-thin-volumn-001。 2091512只有1GB剂公。
在mount前希俩,格式化一下:
[root@localhost tmp]# mkfs.ext4 /dev/mapper/test-thin-volumn-001
mke2fs 1.42.9 (28-Dec-2013)
Discarding device blocks: done
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=16 blocks, Stripe width=16 blocks
65536 inodes, 262144 blocks
13107 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=268435456
8 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376
Allocating group tables: done
Writing inode tables: done
Creating journal (8192 blocks): done
Writing superblocks and filesystem accounting information: done
可以掛載了
[root@localhost tmp]# mkdir /mnt/base
[root@localhost tmp]# mount /dev/mapper/test-thin-volumn-001 /mnt/base
[root@localhost tmp]# echo "hello, im am a base" > /mnt/base/id.txt
[root@localhost tmp]# cat /mnt/base/id.txt
hello, im am a base
創(chuàng)建snapshot:
[root@localhost tmp]# dmsetup message /dev/mapper/test-thin-pool 0 "create_snap 1 0"
[root@localhost tmp]# dmsetup create mysnap1 --table "0 2097152 thin /dev/mapper/test-thin-pool 1"
掛載snapshot:
[root@localhost tmp]# ll /dev/mapper/mysnap1
lrwxrwxrwx. 1 root root 7 Jan 25 20:37 /dev/mapper/mysnap1 -> ../dm-4
[root@localhost tmp]# mkdir /mnt/mysnap1
[root@localhost tmp]# mount /dev/mapper/mysnap1 /mnt/mysnap1/
[root@localhost tmp]# ls /mnt/mysnap1/
id.txt lost+found
[root@localhost tmp]# cat /mnt/mysnap1/id.txt
hello, im am a base
[root@localhost tmp]# echo >> i am snap1 >> /mnt/mysnap1/id.txt
[root@localhost tmp]# echo i am snap1 >> /mnt/mysnap1/id.txt
[root@localhost tmp]# cat /mnt/mysnap1/id.txt
hello, im am a base
am snap1
i am snap1
我們?cè)倏聪?mnt/base,沒(méi)有新加的內(nèi)容纲辽。
[root@localhost tmp]# cat /mnt/base/id.txt
hello, im am a base
我們能看到分層鏡像的樣子了颜武。
Docker存儲(chǔ)驅(qū)動(dòng)devicemapper
devicemapper是RHEL的Docker Engine的默認(rèn)存儲(chǔ)驅(qū)動(dòng),有兩種配置模式:loop-lvm和direct-lvm拖吼。
loop-lvm是默認(rèn)的模式鳞上,它使用OS層面離散的文件來(lái)構(gòu)建精簡(jiǎn)池(thin pool)。該模式主要是設(shè)計(jì)出來(lái)讓Docker能夠簡(jiǎn)單的被”開(kāi)箱即用(out-of-the-box)”而無(wú)需額外的配置绿贞。但如果是在生產(chǎn)環(huán)境的部署Docker因块,官方明文不推薦使用該模式。
direct-lvm是Docker推薦的生產(chǎn)環(huán)境的推薦模式籍铁,他使用塊設(shè)備來(lái)構(gòu)建精簡(jiǎn)池來(lái)存放鏡像和容器的數(shù)據(jù)涡上。
自動(dòng)配置
自動(dòng)配置docker的devicemapper的存儲(chǔ)驅(qū)動(dòng),需要一塊獨(dú)立的塊設(shè)備拒名,比如/dev/sdb吩愧。
[root@localhost ~]# fdisk -l
...
...
Disk /dev/sdb: 53.7 GB, 53687091200 bytes, 104857600 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x0007eabf
Device Boot Start End Blocks Id System
/dev/sdb1 2048 104857599 52427776 8e Linux LVM
全新安裝的docker,啟動(dòng)服務(wù)前增显,修改配置文件/etc/sysconfig/docker-storage-setup
DEVS="/dev/sdb"
VG=docker-vg
DATA_SIZE=100%FREE
說(shuō)明一下:
- DEVS是獨(dú)立的快設(shè)備名
- VG是vg的名字
- DATA_SIZE默認(rèn)50%雁佳,這里設(shè)定是100%FREE
啟動(dòng)docker后,devicemapper的存儲(chǔ)卷就創(chuàng)建成功了。
[root@node1 ~]# lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
docker-pool docker-vg twi-aot--- 49.89g 24.35 6.20
lv_01 root_vg01 -wi-ao---- <41.00g
手動(dòng)配置
如果沒(méi)有獨(dú)立的磁盤(pán)塊設(shè)備糖权,可以在系統(tǒng)中的磁盤(pán)設(shè)備上空閑的塊空間配置堵腹。
- 查看設(shè)備名,我們假定是/dev/xvdf
- 停止docker
# systemctl stop docker
- 安裝包
yum install device-mapper-persistent-data lvm2 -y
- 創(chuàng)建pv
# pvcreate /dev/xvdf
Physical volume "/dev/xvdf" successfully created.
- 創(chuàng)建
docker
vg
# vgcreate docker /dev/xvdf
Volume group "docker" successfully created
- 創(chuàng)建兩個(gè)lv
thinpool
和thinpoolmeta
# sudo lvcreate --wipesignatures y -n thinpool docker -l 95%VG
Logical volume "thinpool" created.
# sudo lvcreate --wipesignatures y -n thinpoolmeta docker -l 1%VG
Logical volume "thinpoolmeta" created.
- 把lv轉(zhuǎn)換成thin pool和metadata
# sudo lvconvert -y \
--zero n \
-c 512K \
--thinpool docker/thinpool \
--poolmetadata docker/thinpoolmeta
WARNING: Converting logical volume 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 to thin pool.
- 通過(guò)lvm profile配置自動(dòng)擴(kuò)容
# vi /etc/lvm/profile/docker-thinpool.profile
- 指定
thin_pool_autoextend_threshold
和thin_pool_autoextend_percent
值星澳。
當(dāng)磁盤(pán)使用率達(dá)到80%疚顷,增加20%的容量.
activation {
thin_pool_autoextend_threshold=80
thin_pool_autoextend_percent=20
}
- 應(yīng)用LVM profile.
sudo lvchange --metadataprofile docker-thinpool docker/thinpool
Logical volume docker/thinpool changed.
- 啟用LV的監(jiān)控
# sudo lvs -o+seg_monitor
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert Monitor
thinpool docker twi-a-t--- 95.00g 0.00 0.01 monitored
- 如果之前運(yùn)行過(guò)docker,先備份
# mv /var/lib/docker /var/lib/docker.bk
- 編輯
/etc/docker/daemon.json
配置devicemapper存儲(chǔ)驅(qū)動(dòng)需要的參數(shù)
{
"storage-driver": "devicemapper",
"storage-opts": [
"dm.thinpooldev=/dev/mapper/docker-thinpool",
"dm.use_deferred_removal=true",
"dm.use_deferred_deletion=true"
]
}
- 啟動(dòng)docker
# systemctl start docker
- 驗(yàn)證
# docker info
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 17.03.1-ce
Storage Driver: devicemapper
Pool Name: docker-thinpool
Pool Blocksize: 524.3 kB
Base Device Size: 10.74 GB
Backing Filesystem: xfs
Data file:
Metadata file:
Data Space Used: 19.92 MB
Data Space Total: 102 GB
Data Space Available: 102 GB
Metadata Space Used: 147.5 kB
Metadata Space Total: 1.07 GB
Metadata Space Available: 1.069 GB
Thin Pool Minimum Free Space: 10.2 GB
Udev Sync Supported: true
Deferred Removal Enabled: true
Deferred Deletion Enabled: true
Deferred Deleted Device Count: 0
Library Version: 1.02.135-RHEL7 (2016-11-16)
<output truncated>
Data file
和Metadata file
是空的腿堤,pool名字是docker-thinpool
笆檀。
device mapper在Docker中的性能表現(xiàn)
device mapper的性能主要受“需要時(shí)分配”策略和“寫(xiě)時(shí)復(fù)制”策略影響酗洒,下面分別介紹:
需要時(shí)分配(allocate-on-demand)
device mapperdriver通過(guò)allocate-on-demand策略為需要寫(xiě)入的數(shù)據(jù)分配數(shù)據(jù)塊寝蹈。也就是說(shuō)登淘,每當(dāng)容器中的進(jìn)程需要向容器寫(xiě)入數(shù)據(jù)時(shí)黔州,device mapper就從資源池中分配一些數(shù)據(jù)塊并將其映射到容器流妻。
當(dāng)容器頻繁進(jìn)行小數(shù)據(jù)的寫(xiě)操作時(shí),這種機(jī)制非常影響影響性能涣达。
一旦數(shù)據(jù)塊被分配給了容器度苔,對(duì)它進(jìn)行的讀寫(xiě)操作都直接對(duì)塊進(jìn)行操作了浑度。
寫(xiě)時(shí)復(fù)制(copy-on-write)
與aufs一樣,device mapper也支持寫(xiě)時(shí)復(fù)制策略甩骏。容器中第一次更新某個(gè)文件時(shí),device mapper調(diào)用寫(xiě)時(shí)復(fù)制策略饮笛,將數(shù)據(jù)塊從鏡像快照中復(fù)制到容器快照中缎浇。
device mapper的寫(xiě)時(shí)復(fù)制策略以64KB作為粒度,意味著無(wú)論是對(duì)32KB的文件還是對(duì)1GB大小的文件的修改都僅復(fù)制64KB大小的文件素跺。這相對(duì)于在文件層面進(jìn)行的讀操作具有很明顯的性能優(yōu)勢(shì)指厌。
但是踩验,如果容器頻繁對(duì)小于64KB的文件進(jìn)行改寫(xiě)箕憾,device mapper的性能是低于aufs的袭异。
存儲(chǔ)空間使用效率
device mapper不是最有效使用存儲(chǔ)空間的storage driver御铃,啟動(dòng)n個(gè)相同的容器就復(fù)制了n份文件在內(nèi)存中上真,這對(duì)內(nèi)存的影響很大羹膳。所以device mapper并不適合容器密度高的場(chǎng)景。
參考
本文參考和節(jié)選了以下文章:
- https://docs.docker.com/engine/userguide/storagedriver/device-mapper-driver/
- https://coolshell.cn/articles/17200.html
- https://github.com/torvalds/linux/blob/master/Documentation/device-mapper/thin-provisioning.txt
- http://blog.csdn.net/vchy_zhao/article/details/70238690
- http://blog.csdn.net/qq_26923057/article/details/52351731