標籤:
Device Mapper 簡介
DeviceMapper自Linux 2.6被引入成為了Linux最重要的一個技術。它在核心中支援邏輯卷管理的通用裝置映射機制,它為實現用於儲存資源管理的塊裝置驅動提供了一個高度模組化的核心架構,它包含三個重要的對象概念,Mapped Device、Mapping Table、Target device。
Mapped Device 是一個邏輯抽象,可以理解成為核心向外提供的邏輯裝置,它通過Mapping Table描述的映射關係和 Target Device 建立映射。Target device 表示的是 Mapped Device 所映射的物理空間段,對 Mapped Device 所表示的邏輯裝置來說,就是該邏輯裝置映射到的一個物理裝置。
Mapping Table裡有Mapped Device 邏輯的起始地址、範圍、和表示在 Target Device 所在物理裝置的地址位移量以及Target 類型等資訊(註:這些地址和位移量都是以磁碟的扇區為單位的,即 512 個位元組大小,所以,當你看到128的時候,其實表示的是128*512=64K)。
DeviceMapper 中的邏輯裝置Mapped Device不但可以映射一個或多個物理裝置Target Device,還可以映射另一個Mapped Device,於是,就是構成了一個迭代或遞迴的情況,就像檔案系統中的目錄裡除了檔案還可以有目錄,理論上可以無限嵌套下去。
DeviceMapper在核心中通過一個一個模組化的 Target Driver 外掛程式實現對 IO 請求的過濾或者重新定向等工作,當前已經實現的外掛程式包括軟 Raid、加密、多重路徑、鏡像、快照等,這體現了在 Linux 核心設計中策略和機制分離的原則。如所示。,我們可以看到DeviceMapper只是一個架構,在這個架構上,我們可以插入各種各樣的策略(讓我不自然地想到了物件導向中的策略模式),在這諸多“外掛程式”中,有一個東西叫Thin Provisioning Snapshot,這是Docker使用DeviceMapper中最重要的模組。
圖片來源:http://people.redhat.com/agk/talks/FOSDEM_2005/
Thin Provisioning 簡介
Thin Provisioning要怎麼翻譯成中文,真是一件令人頭痛的事,我就不翻譯了。這個技術是虛擬化技術中的一種。它是什麼意思呢?你可以聯想一下我們電腦中的記憶體管理中用到的——“虛擬記憶體技術”——作業系統給每個進程N多N多用不完的內址地址(32位下,每個進程可以有最多2GB的記憶體空間),但是呢,我們知道,實體記憶體是沒有那麼多的,如果按照進程記憶體和實體記憶體一一映射來玩的話,那麼,我們得要多少的實體記憶體啊。所以,作業系統引入了虛擬記憶體的設計,意思是,我邏輯上給你無限多的記憶體,但是實際上是實報實銷,因為我知道你一定用不了那麼多,於是,達到了記憶體使用量率提高的效果。(今天雲端運算中很多所謂的虛擬化其實完全都是在用和“虛擬記憶體”相似的Thin Provisioning的技術,所謂的超配,或是超賣)
好了,話題拉回來,我們這裡說的是儲存。看下面兩個圖(圖片來源),第一個是Fat Provisioning,第二個是Thin Provisioning,其很好的說明了是個怎麼一回事(和虛擬記憶體是一個概念)
那麼,Docker是怎麼使用Thin Provisioning這個技術做到像UnionFS那樣的分層鏡像的呢?答案是,Docker使用了Thin Provisioning的Snapshot的技術。下面我們來介紹一下Thin Provisioning的Snapshot。
Thin Provisioning Snapshot 示範
下面,我們用一系列的命令來示範一下Device Mapper的Thin Provisioning Snapshot是怎麼玩的。
首先,我們需要先建兩個檔案,一個是data.img,一個是meta.data.img:
~hchen$ sudo dd if=/dev/zero of=/tmp/data.img bs=1K count=1 seek=10M1+0 records in1+0 records out1024 bytes (1.0 kB) copied, 0.000621428 s, 1.6 MB/s~hchen$ sudo dd if=/dev/zero of=/tmp/meta.data.img bs=1K count=1 seek=1G1+0 records in1+0 records out1024 bytes (1.0 kB) copied, 0.000140858 s, 7.3 MB/s
注意命令中seek選項,其表示為略過of選項指定的輸出檔案的前10G個output的bloksize的空間後再寫入內容。因為bs是1個位元組,所以也就是10G的尺寸,但其實在硬碟上是沒有佔有空間的,佔有空間只有1k的內容。當向其寫入內容時,才會在硬碟上為其分配空間。我們可以用ls命令看一下,實際分配了12K和4K。
~hchen$ sudo ls -lsh /tmp/data.img12K -rw-r--r--. 1 root root 11G Aug 25 23:01 /tmp/data.img~hchen$ sudo ls -slh /tmp/meta.data.img4.0K -rw-r--r--. 1 root root 101M Aug 25 23:17 /tmp/meta.data.img
然後,我們為這個檔案建立一個loopback裝置。(loop2015和loop2016是我亂取的兩個名字)
~hchen$ sudo losetup /dev/loop2015 /tmp/data.img~hchen$ sudo losetup /dev/loop2016 /tmp/meta.data.img~hchen$ sudo losetup -a/dev/loop2015: [64768]:103991768 (/tmp/data.img)/dev/loop2016: [64768]:103991765 (/tmp/meta.data.img)
現在,我們為這個裝置建一個Thin Provisioning的Pool,用dmsetup命令:
~hchen$ sudo dmsetup create hchen-thin-pool --table "0 20971522 thin-pool /dev/loop2016 /dev/loop2015 128 65536 1 skip_block_zeroing"
其中的參數解釋如下(更多資訊可參看Thin Provisioning的man page):
- dmsetup create是用來建立thin pool的命令
- hchen-thin-pool 是自訂的一個pool名,不衝突就好。
- –table是這個pool的參數設定
- 0代表起的sector位置
- 20971522代碼結句的sector號,前面說過,一個sector是512位元組,所以,20971522個正好是10GB
- /dev/loop2016是meta檔案的裝置(前面我們建好了)
- /dev/loop2015是data檔案的裝置(前面我們建好了)
- 128是最小的可分配的sector數
- 65536是最少可用sector的water mark,也就是一個threshold
- 1 代表有一個附加參數
- skip_block_zeroing是個附加參數,表示略過用0填充的塊
然後,我們就可以看到一個Device Mapper的裝置了:
~hchen$ sudo ll /dev/mapper/hchen-thin-poollrwxrwxrwx. 1 root root 7 Aug 25 23:24 /dev/mapper/hchen-thin-pool -> ../dm-4
接下來,我們的初始還沒有完成,還要建立一個Thin Provisioning 的 Volume:
~hchen$ sudo dmsetup message /dev/mapper/hchen-thin-pool 0 "create_thin 0"~hchen$ sudo dmsetup create hchen-thin-volumn-001 --table "0 2097152 thin /dev/mapper/hchen-thin-pool 0"
其中:
- 第一個命令中的create_thin是關鍵字,後面的0表示這個Volume的device 的 id
- 第二個命令,是真正的為這個Volumn建立一個可以mount的裝置,名字叫hchen-thin-volumn-001。2097152隻有1GB
好了,在mount前,我們還要格式化一下:
~hchen$ sudo mkfs.ext4 /dev/mapper/hchen-thin-volumn-001mke2fs 1.42.9 (28-Dec-2013)Discarding device blocks: doneFilesystem label=OS type: LinuxBlock size=4096 (log=2)Fragment size=4096 (log=2)Stride=16 blocks, Stripe width=16 blocks65536 inodes, 262144 blocks13107 blocks (5.00%) reserved for the super userFirst data block=0Maximum filesystem blocks=2684354568 block groups32768 blocks per group, 32768 fragments per group8192 inodes per groupSuperblock backups stored on blocks:32768, 98304, 163840, 229376Allocating group tables: doneWriting inode tables: doneCreating journal (8192 blocks): doneWriting superblocks and filesystem accounting information: done
好了,我們可以mount了(下面的命令中,我還建立了一個檔案)
~hchen$ sudo mkdir -p /mnt/base~hchen$ sudo mount /dev/mapper/hchen-thin-volumn-001 /mnt/base~hchen$ sudo echo "hello world, I am a base" > /mnt/base/id.txt~hchen$ sudo cat /mnt/base/id.txthello world, I am a base
好了,接下來,我們來看看snapshot怎麼搞:
~hchen$ sudo dmsetup message /dev/mapper/hchen-thin-pool 0 "create_snap 1 0"~hchen$ sudo dmsetup create mysnap1 --table "0 2097152 thin /dev/mapper/hchen-thin-pool 1"~hchen$ sudo ll /dev/mapper/mysnap1lrwxrwxrwx. 1 root root 7 Aug 25 23:49 /dev/mapper/mysnap1 -> ../dm-5
上面的命令中:
- 第一條命令是向hchen-thin-pool發一個create_snap的訊息,後面跟兩個id,第一個是新的dev id,第二個是要從哪個已有的dev id上做snapshot(0這個dev id是我們前面就建立了了)
- 第二條命令是建立一個mysnap1的device,並可以被mount。
下面我們來看看:
~hchen$ sudo mkdir -p /mnt/mysnap1~hchen$ sudo mount /dev/mapper/mysnap1 /mnt/mysnap1~hchen$ sudo ll /mnt/mysnap1/total 20-rw-r--r--. 1 root root 25 Aug 25 23:46 id.txtdrwx------. 2 root root 16384 Aug 25 23:43 lost+found~hchen$ sudo cat /mnt/mysnap1/id.txthello world, I am a base
我們來修改一下/mnt/mysnap1/id.txt,並加上一個snap1.txt的檔案:
~hchen$ sudo echo "I am snap1" >> /mnt/mysnap1/id.txt~hchen$ sudo echo "I am snap1" > /mnt/mysnap1/snap1.txt~hchen$ sudo cat /mnt/mysnap1/id.txthello world, I am a baseI am snap1~hchen$ sudo cat /mnt/mysnap1/snap1.txtI am snap1
我們再看一下/mnt/base,你會發現沒有什麼變化:
~hchen$ sudo ls /mnt/baseid.txt lost+found~hchen$ sudo cat /mnt/base/id.txthello world, I am a base
你是不是已經看到了分層鏡像的樣子了?
你還要吧繼續在剛才的snapshot上再建一個snapshot
~hchen$ sudo dmsetup message /dev/mapper/hchen-thin-pool 0 "create_snap 2 1"~hchen$ sudo dmsetup create mysnap2 --table "0 2097152 thin /dev/mapper/hchen-thin-pool 2"~hchen$ sudo ll /dev/mapper/mysnap2lrwxrwxrwx. 1 root root 7 Aug 25 23:52 /dev/mapper/mysnap1 -> ../dm-7~hchen$ sudo mkdir -p /mnt/mysnap2~hchen$ sudo mount /dev/mapper/mysnap2 /mnt/mysnap2~hchen$ sudo ls /mnt/mysnap2id.txt lost+found snap1.txt
好了,我相信你看到了分層鏡像的樣子了。
看完示範,我們再來補點理論知識吧:
- Snapshot來自LVM(Logic Volumn Manager),它可以在不中斷服務的情況下為某個device打一個快照。
- Snapshot是Copy-On-Write的,也就是說,只有發生了修改,才會對對應的記憶體進行拷貝。
另外,這裡有篇文章Storage thin provisioning benefits and challenges可以前往一讀。
Docker的DeviceMapper
上面基本上就是Docker的玩法了,我們可以看一下docker的loopback裝置:
~hchen $ sudo losetup -a/dev/loop0: [64768]:38050288 (/var/lib/docker/devicemapper/devicemapper/data)/dev/loop1: [64768]:38050289 (/var/lib/docker/devicemapper/devicemapper/metadata)
其中data 100GB,metadata 2.0GB
~hchen $ sudo ls -alhs /var/lib/docker/devicemapper/devicemapper506M -rw-------. 1 root root 100G Sep 10 20:15 data1.1M -rw-------. 1 root root 2.0G Sep 10 20:15 metadata
下面是相關的thin-pool。其中,有個當一大串hash串的device是正在啟動的容器:
~hchen $ sudo ll /dev/mapper/dock*lrwxrwxrwx. 1 root root 7 Aug 25 07:57 /dev/mapper/docker-253:0-104108535-pool -> ../dm-2lrwxrwxrwx. 1 root root 7 Aug 25 11:13 /dev/mapper/docker-253:0-104108535-deefcd630a60aa5ad3e69249f58a68e717324be4258296653406ff062f605edf -> ../dm-3
我們可以看一下它的device id(Docker都把它們記下來了):
~hchen $ sudo cat /var/lib/docker/devicemapper/metadata/deefcd630a60aa5ad3e69249f58a68e717324be4258296653406ff062f605edf{"device_id":24,"size":10737418240,"transaction_id":26,"initialized":false}
device_id是24,size是10737418240,除以512,就是20971520 個 sector,我們用這些資訊來做個snapshot看看(註:我用了一個比較大的dev id – 1024):
~hchen$ sudo dmsetup message "/dev/mapper/docker-253:0-104108535-pool" 0 "create_snap 1024 24"~hchen$ sudo dmsetup create dockersnap --table "0 20971520 thin /dev/mapper/docker-253:0-104108535-pool 1024"~hchen$ sudo mkdir /mnt/docker~hchen$ sudo mount /dev/mapper/dockersnap /mnt/docker/~hchen$ sudo ls /mnt/docker/id lost+found rootfs~hchen$ sudo ls /mnt/docker/rootfs/bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
我們在docker的容器裡用findmnt命令也可以看到相關的mount的情況(因為太長,下面只是摘要):
# findmntTARGET SOURCE / /dev/mapper/docker-253:0-104108535-deefcd630a60[/rootfs]/etc/resolv.conf /dev/mapper/centos-root[/var/lib/docker/containers/deefcd630a60/resolv.conf]/etc/hostname /dev/mapper/centos-root[/var/lib/docker/containers/deefcd630a60/hostname]/etc/hosts /dev/mapper/centos-root[/var/lib/docker/containers/deefcd630a60/hosts]
Device Mapper行不行?
Thin Provisioning的文檔中說,這還處理實驗階段,不要上Production.
These targets are very much still in the EXPERIMENTAL state. Please do not yet rely on them in production.
另外,Jeff Atwood在Twitter上發過這樣的一推
這個推指向的這個討論中,其中指向了這個code diff,基本上就是說,DeviceMapper這種東西問題太多了,我們應該把其加入黑名單。Doker的Founder也這樣回複到:
所以,如果你在使用loopback的devicemapper的話,當你的儲存出現了問題後,正確的解決方案是:
rm -rf /var/lib/docker
文章轉自:http://coolshell.cn/articles/17200.html
Docker基礎技術:DeviceMapper