最近在官網上看了下devicemapper的說明文檔,這里扼要總結1下。
期初docker是運行在Debian和Ubuntu系統上的,使用的是AUFS方式的存儲。后來很多企業希望在Redhat系linux上運行docker,并決定基于現有的Device Mapper技術開發1個新的存儲方案,這就是devicemapper。
Device Mapper技術不是依照文件級別(file level),而是按塊級別(block level)工作的,所以devicemapper也屬于塊級存儲方案,有著thin provisioning和copy-on-write的特點。
使用devicemapper存儲生成鏡像大致依照下面的流程:
首先,devicemapper驅動從塊裝備創建1個小的存儲池(a thin pool)
然后,創建1個帶有文件系統,如extfs等,的基礎裝備(base device)
以后,每一個新的鏡像(或鏡像層)都是base device的1個快照(snapshot)
devicemapper存儲方式下,容器層都是從鏡像生成的快照,快照里存儲著所有對容器的更新。當數據被寫入容器的時候,devicemapper按需從池中分配空間。
下面是官方文檔中的1個說明圖:
從上圖可以看出,每一個鏡像層都是它下面1層的快照。每一個鏡像的最下面1層的鏡像則是池中base device的快照。需要注意的是,base device屬于Device Mapper的1部份,其實不是docker的鏡像層。
官網上讀操作的說明圖以下:
1)利用要求讀取容器中的0x44f塊區
由于容器是鏡像的1個簡單快照,并沒有數據只有1個指針,指向鏡像層存儲數據的地方。
2)存儲驅動根據指針,到鏡像快照的a005e鏡像層尋覓0xf33塊區
3)devicemapper從鏡像快照拷貝0xf33的內容到容器的內存中
4)存儲驅動最后將數據返回給要求的利用
當對容器中的大文件做1個小的改動的時候,devicemapper不會復制這全部文件,而是只拷貝被修改的塊區。每一個塊區的大小為64KB。
寫新數據的情況
例如,寫56KB大小的新數據到1個容器:
1)利用發出1個要寫56KB的新數據到容器的要求
2)根據按需分配,將分配1個新的64KB的塊區給容器的快照
如果寫操作的數據大于64KB的話,將分配多個塊區
3)以后,數據將被寫入到新分配的塊區中
覆寫已有的數據
每當容器第1次更新已有的數據時:
1)利用發出1個修改容器中數據的要求
2)copy-on-write操作將定位到需要更新的塊區
3)然后分配新的空塊區給容器快照,并復制數據到新分配的塊區
4)接著,在有復制數據的新塊區中進行數據修改
容器中的利用對產生的allocate-on-demand操作和copy-on-write操作是無感的
Redhat系的linux發行版都采取devicemapper作為docker的默許存儲驅動。目前,Debian,Ubuntu和Arch Linux也支持devicemapper。
devicemapper默許使用的是loop-lvm模式,這個模式使用sparse files來創建供鏡像和容器快照使用的thin pool。但是,生產環境不要使用loop-lvm模式,官方建議使用direct-lvm模式。direct-lvm模式使用塊裝備(block devices)來創建thin pool。
假定/dev/mapper/docker-thinpool是已建好的lvm的邏輯卷,可以配置docker 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
官方說設置dm.use_deferred_removal=true和dm.use_deferred_deletion=true選項可以避免unintentionally leaking mount points(沒太明白甚么意思,總之配了比不配好吧)。
使用lsblk命令可以查看裝備文件和devicemapper在裝備文件上創建的pool:
對應上面接口的層次圖以下:
可以看出,名為Docker⑵02:1⑴032-pool的pool橫跨在data和metadata裝備之上。pool的命名規則為:
Docker-主裝備號:2級裝備號-inode號-pool
docker 1.10和以后的版本,在/var/lib/docker目錄下不在采取鏡像層ID來關聯目錄名了,有另外兩個比較重要的目錄:
/var/lib/docker/devicemapper/mnt 包括鏡像和容器層的掛載目錄;
/var/lib/docker/devicemapper/metadata 目錄包括每一個鏡像層和容器快照的json格式的文件。
另外,當數據的邏輯卷要滿的時候,可以給pool進行擴容,具體操作看官網。
每當利用有新數據要寫入容器時,都要從pool中去定位空的塊區并映照給容器。由于所有塊區都是64KB的,小于64KB的數據也會分配1個塊區;大于64B的數據則會分配多個塊區。所以,特別是當產生很多小的寫操作時,就會比較影響容器的性能。
每當容器第1次更新已有的數據時,devicemapper存儲驅動都要履行copy-on-write操作。這個操作是從鏡像快照復制數據到容器快照,這對容器性能還是有比較明顯的性能影響的。當容器產生很多小64KB的寫操作時,devicemapper的性能會比AUFS要差。
1)所使用的mode
默許情況下,devicemapper使用的是loop-lvm模式,這類模式使用的是sparse files,性能比較低。生產環境建議使用direct-lvm模式,這類模式存儲驅動直接寫數據到塊裝備。
2)使用高速存儲
如果希望更好的性能,可以將Data file和Metadata file放到SSD這樣的高速存儲上。
3)內存使用
devicemapper其實不是1個有效使用內存的存儲驅動。當1個容器運行n個時,它的文件也會被拷貝n份到內存中,這對docker宿主機的內存使用會造成明顯影響。因此,devicemapper存儲驅動可能其實不是PaaS和其它高密度使用型的最好選擇。
對寫操作較大的,可以采取掛載data volumes。使用data volumes可以繞過存儲驅動,從而不受thin provisioning和copy-on-write產生的負責影響。
下一篇 數據結構--排序算法總結