1. 程式人生 > >Docker存儲方式選型建議

Docker存儲方式選型建議

減少 http 模式 研發 服務器 etc 原生 -h 設計

註:本文來源於網絡分享

【編者的話】Docker存儲方式提供管理分層鏡像和容器的可讀寫層的具體實現。最初Docker僅能在支持AUFS文件系統的Ubuntu 發行版上運行,但是由於AUFS未能加入Linux內核,為了尋求兼容性、擴展性,Docker在內部通過GraphDriver機制這種可擴展的 方式來實現對不同文件系統的支持。本次分享通過一次客戶實施案例深入的看看Docker的幾種存儲方式,並給出一些技術選型的建議。

Docker存儲方式:
  1. AUFS的介紹及分析
  2. Device mapper的介紹及分析
  3. OverlayFS的介紹及分析
  4. Btrfs的介紹及分析
  5. ZFS的介紹及分析

第一部分 問題診斷

事情從一次實施項目說起,我們需要幫助客戶將他們的應用容器化並在數人雲平臺上發布此應用。客戶的應用是傳統WAS應用。應用是通過WAS console界面進行手工部署,暫時無法通過Dockerfile進行自動化應用部署,最後的鏡像是通過Docker commit完成。鏡像啟動執行命令是startwas.sh,並通過tail將應用日誌輸出到標準輸出。 啟動容器,WAS Server啟動失敗,錯誤日誌如下:
技術分享圖片
WAS Server標準日誌文件startServer.log和native_stderr.log都沒有更加詳細的錯誤信息。最後功夫不負有心人, 在configuration目錄下找到可以定位的錯誤信息 =_=!:
技術分享圖片

文件訪問IO異常,查看相應目錄文件的屬性:
技術分享圖片
到現在為止,可以初步判斷是Docker存儲方式(storage drive)在鏡像容器分層管理上的問題。

當前宿主機是CentOS 7.2,內核3.10.0。並且查看當前宿主機信息是Docker 1.12.0,存儲方式是Overlay,宿主機的文件系統是XFS:
技術分享圖片
為了驗證我們的推斷,我們做了如下幾方面的嘗試:

嘗試1:使用數據卷掛載的方式,掛載整個washome目錄。(數據卷是Docker宿主機的目錄或文件,通過mount的方式加載到容器裏,不受存儲驅動的控制。)重新制作鏡像啟動容器,WAS Server能正常啟動。

嘗試2:改變Docker engine的存儲方式,改成Device mapper,重新拉取鏡像,並啟動容器,WAS Server能正常啟動。

那麽這個問題是普遍問題嗎?

嘗試3:在其他的宿主機上,啟動原鏡像,這個問題是無法復現的。

經過多次測試發現在相同內核、系統版本、Docker版本有些機器有問題有些機器沒有問題,最終發現是CentOS提供的新文件系統XFS和Overlay兼容問題導致。同時,我們從Docker社區找到相關問題的issue報告:https://github.com/docker/docker/issues/9572 這個問題的修復在內核 4.4.6以上。綜上所述,我們得到了一個結論,這個問題的根本原因是overlayFS在xfs上出現了兼容性的問題。

事情的起因到此為止,下面讓我們深入的看看Docker的幾種存儲方式,並給出一些技術選型的建議。

第二部分 概述

Docker在啟動容器的時候,需要創建文件系統,為rootfs提供掛載點。最底層的引導文件系統bootfs主要包含bootloader和kernel,bootloader主要是引導加載kernel,當kernel被加載到內存中後 bootfs就被umount了。 rootfs包含的就是典型 Linux 系統中的/dev,/proc,/bin,/etc等標準目錄和文件。

Docker模型的核心部分是有效利用分層鏡像機制,鏡像可以通過分層來進行繼承,基於基礎鏡像(沒有父鏡像),可以制作各種具體的應用鏡像。Docker 1.10引入新的可尋址存儲模型,使用安全內容哈希代替隨機的UUID管理鏡像。同時,Docker提供了遷移工具,將已經存在的鏡像遷移到新模型上。不同 Docker容器就可以共享一些基礎的文件系統層,同時再加上自己獨有的可讀寫層,大大提高了存儲的效率。其中主要的機制就是分層模型和將不同目錄掛載到同一個虛擬文件系統。
技術分享圖片
Docker存儲方式提供管理分層鏡像和容器的可讀寫層的具體實現。最初Docker僅能在支持AUFS文件系統的Ubuntu發行版上運行,但是由於AUFS未能加入Linux內核,為了尋求兼容性、擴展性,Docker在內部通過graphdriver機制這種可擴展的方式來實現對不同文件系統的支持。

Docker有如下幾種不同的drivers:
  • AUFS
  • Device mapper
  • Btrfs
  • OverlayFS
  • ZFS

第三部分 方案分析

AUFS

AUFS(AnotherUnionFS)是一種Union FS,是文件級的存儲驅動。所謂 UnionFS 就是把不同物理位置的目錄合並mount到同一個目錄中。簡單來說就是支持將不同目錄掛載到同一個虛擬文件系統下的文件系統。這種文件系統可以一層一層地疊加修改文件。無論底下有多少層都是只讀的,只有最上層的文件系統是可寫的。當需要修改一個文件時,AUFS創建該文件的一個副本,使用CoW將文件從只讀層復制到可寫層進行修改,結果也保存在可寫層。在Docker中,底下的只讀層就是image,可寫層就是Container。結構如下圖所示:
技術分享圖片

例子

運行一個實例應用是刪除一個文件/etc/shadow,看AUFS的結果:
# docker run centos rm /etc/shadow
# ls -la /var/lib/docker/aufs/diff/$(docker ps --no-trunc -lq)/etc

total 8
drwxr-xr-x 2 root root 4096 Sep  2 18:35 .
drwxr-xr-x 5 root root 4096 Sep  2 18:35 ..
-r--r--r-- 2 root root    0 Sep  2 18:35 .wh.shadow

目錄結構
  • 容器掛載點(只有容器運行時才被加載)
    /var/lib/docker/aufs/mnt/$CONTAINER_ID/
    
  • 分支(和鏡像不同的文件,只讀活著讀寫)
    /var/lib/docker/aufs/diff/$CONTAINER_OR_IMAGE_ID/
    
  • 鏡像索引表(每個鏡像引用鏡像名)
    /var/lib/docker/aufs/layers/
    

其他

AUFS 文件系統可使用的磁盤空間大小
# df -h /var/lib/docker/

Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        20G  4.0G   15G  22% /


系統掛載方式

啟動的Docker
docker ps

CONTAINER ID        IMAGE                        COMMAND                CREATED             STATUS              PORTS                      NAMES
3f2e9de1d9d5        mesos/bamboo:v0.1c           "/usr/bin/bamboo-hap   5 days ago          Up 5 days                                      mesos-20150825-162813-1248613158-5050-1-S0.88c909bc-6301-423a-8283-5456435f12d3
dc9a7b000300        mesos/nginx:base             "/bin/sh -c nginx"     7 days ago          Up 7 days           0.0.0.0:31967->80/tcp      mesos-20150825-162813-1248613158-5050-1-S0.42667cb2-1134-4b1a-b11d-3c565d4de418
1b466b5ad049        mesos/marathon:omega.v0.1    "/usr/bin/dataman_ma   7 days ago          Up 16 hours                                    dataman-marathon
0a01eb99c9e7        mesos/nginx:base             "/bin/sh -c nginx"     7 days ago          Up 7 days           0.0.0.0:31587->80/tcp      mesos-20150825-162813-1248613158-5050-1-S0.4f525828-1217-4b3d-a169-bc0eb901eef1
c2fb2e8bd482        mesos/dns:v0.1c              "/usr/bin/dataman_me   7 days ago          Up 7 days                                      mesos-20150825-162813-1248613158-5050-1-S0.82d500eb-c3f0-4a00-9f7b-767260d1ee9a
df102527214d        mesos/zookeeper:omega.v0.1   "/data/run/dataman_z   8 days ago          Up 8 days                                      dataman-zookeeper
b076a43693c1        mesos/slave:omega.v0.1       "/usr/sbin/mesos-sla   8 days ago          Up 8 days                                      dataman-slave
e32e9fc9a788        mesos/master:omega.v0.1      "/usr/sbin/mesos-mas   8 days ago          Up 8 days                                      dataman-master
c8454c90664e        shadowsocks_server           "/usr/local/bin/ssse   9 days ago          Up 9 days           0.0.0.0:57980->57980/tcp   shadowsocks
6dcd5bd46348        registry:v0.1                "docker-registry"      9 days ago          Up 9 days           0.0.0.0:5000->5000/tcp     dataman-registry

對照系統掛載點
grep aufs /proc/mounts

/dev/mapper/ubuntu--vg-root /var/lib/docker/aufs ext4 rw,relatime,errors=remount-ro,data=ordered 0 0
none /var/lib/docker/aufs/mnt/6dcd5bd463482edf33dc1b0324cf2ba4511c038350e745b195065522edbffb48 aufs rw,relatime,si=d9c018051ec07f56,dio,dirperm1 0 0
none /var/lib/docker/aufs/mnt/c8454c90664e9a2a2abbccbe31a588a1f4a5835b5741a8913df68a9e27783170 aufs rw,relatime,si=d9c018051ba00f56,dio,dirperm1 0 0
none /var/lib/docker/aufs/mnt/e32e9fc9a788e73fc7efc0111d7e02e538830234377d09b54ffc67363b408fca aufs rw,relatime,si=d9c018051b336f56,dio,dirperm1 0 0
none /var/lib/docker/aufs/mnt/b076a43693c1d5899cda7ef8244f3d7bc1d102179bc6f5cd295f2d70307e2c24 aufs rw,relatime,si=d9c018051bfecf56,dio,dirperm1 0 0
none /var/lib/docker/aufs/mnt/df102527214d5886505889b74c07fda5d10b10a4b46c6dab3669dcbf095b4154 aufs rw,relatime,si=d9c01807933e1f56,dio,dirperm1 0 0
none /var/lib/docker/aufs/mnt/c2fb2e8bd4822234633d6fd813bf9b24f9658d8d97319b1180cb119ca5ba654c aufs rw,relatime,si=d9c01806c735ff56,dio,dirperm1 0 0
none /var/lib/docker/aufs/mnt/0a01eb99c9e702ebf82f30ad351d5a5a283326388cd41978cab3f5c5b7528d94 aufs rw,relatime,si=d9c018051bfebf56,dio,dirperm1 0 0
none /var/lib/docker/aufs/mnt/1b466b5ad049d6a1747d837482264e66a87871658c1738dfd8cac80b7ddcf146 aufs rw,relatime,si=d9c018052b2b1f56,dio,dirperm1 0 0
none /var/lib/docker/aufs/mnt/dc9a7b000300a36c170e4e6ce77b5aac1069b2c38f424142045a5ae418164241 aufs rw,relatime,si=d9c01806d9ddff56,dio,dirperm1 0 0
none /var/lib/docker/aufs/mnt/3f2e9de1d9d51919e1b6505fd7d3f11452c5f00f17816b61e6f6e97c6648b1ab aufs rw,relatime,si=d9c01806c708ff56,dio,dirperm1 0 0

分析
  1. 雖然AUFS是Docker第一版支持的存儲方式,但到現在還沒有加入內核主線( CentOS無法直接使用)。
  2. 從原理分析看,AUFS mount()方法很快,所以創建容器很快;讀寫訪問都具有本機效率;順序讀寫和隨機讀寫的性能大於KVM;並且Docker的AUFS可以有效的使用存儲和內存 。
  3. AUFS性能穩定,並且有大量生產部署及豐富的社區支持。
  4. 不支持rename系統調用,執行“copy”和“unlink”時,會導致失敗。
  5. 當寫入大文件的時候(比如日誌或者數據庫等)動態mount多目錄路徑的問題,導致branch越多,查找文件的性能也就越慢。(解決辦法:重要數據直接使用 -v 參數掛載。)

Device mapper

Device mapper是Linux內核2.6.9後支持的,提供的一種從邏輯設備到物理設備的映射框架機制,在該機制下,用戶可以很方便的根據自己的需要制定實現存儲資源的管理策略。Docker的Device mapper利用Thin provisioning snapshot管理鏡像和容器。

Thin-provisioning Snapshot

Snapshot是Lvm提供的一種特性,它可以在不中斷服務運行的情況下為the origin(original device)創建一個虛擬快照(Snapshot)。Thin-Provisioning是一項利用虛擬化方法減少物理存儲部署的技術。Thin-provisioning Snapshot是結合Thin-Provisioning和Snapshoting兩種技術,允許多個虛擬設備同時掛載到一個數據卷以達到數據共享的目的。Thin-Provisioning Snapshot的特點如下:
  1. 可以將不同的snaptshot掛載到同一個the origin上,節省了磁盤空間。
  2. 當多個Snapshot掛載到了同一個the origin上,並在the origin上發生寫操作時,將會觸發COW操作。這樣不會降低效率。
  3. Thin-Provisioning Snapshot支持遞歸操作,即一個Snapshot可以作為另一個Snapshot的the origin,且沒有深度限制。
  4. 在Snapshot上可以創建一個邏輯卷,這個邏輯卷在實際寫操作(COW,Snapshot寫操作)發生之前是不占用磁盤空間的。

相比AUFS和OverlayFS是文件級存儲,Device mapper是塊級存儲,所有的操作都是直接對塊進行操作,而不是文件。Device mapper驅動會先在塊設備上創建一個資源池,然後在資源池上創建一個帶有文件系統的基本設備,所有鏡像都是這個基本設備的快照,而容器則是鏡像的快照。所以在容器裏看到文件系統是資源池上基本設備的文件系統的快照,並沒有為容器分配空間。當要寫入一個新文件時,在容器的鏡像內為其分配新的塊並寫入數據,這個叫用時分配。當要修改已有文件時,再使用CoW為容器快照分配塊空間,將要修改的數據復制到在容器快照中新的塊裏再進行修改。Device mapper 驅動默認會創建一個100G的文件包含鏡像和容器。每一個容器被限制在10G大小的卷內,可以自己配置調整。結構如下圖所示:
技術分享圖片
可以通過"docker info"或通過dmsetup ls獲取想要的更多信息。查看Docker的Device mapper的信息:
技術分享圖片

分析
  1. Device mapper文件系統兼容性比較好,並且存儲為一個文件,減少了inode消耗。
  2. 每次一個容器寫數據都是一個新塊,塊必須從池中分配,真正寫的時候是稀松文件,雖然它的利用率很高,但性能不好,因為額外增加了VFS開銷。
  3. 每個容器都有自己的塊設備時,它們是真正的磁盤存儲,所以當啟動N個容器時,它都會從磁盤加載N次到內存中,消耗內存大。
  4. Docker的Device mapper默認模式是loop-lvm,性能達不到生產要求。在生產環境推薦direct-lvm模式直接寫原塊設備,性能好。

OverlayFS

Overlay是Linux內核3.18後支持的,也是一種Union FS,和AUFS的多層不同的是Overlay只有兩層:一個Upper文件系統和一個Lower文件系統,分別代表Docker的鏡像層和容器層。當需要修改一個文件時,使用CoW將文件從只讀的Lower復制到可寫的Upper進行修改,結果也保存在Upper層。在Docker中,底下的只讀層就是image,可寫層就是Container。結構如下圖所示:
技術分享圖片

分析
  1. 從kernel 3.18進入主流Linux內核。設計簡單,速度快,比AUFS和Device mapper速度快。在某些情況下,也比Btrfs速度快。是Docker存儲方式選擇的未來。因為OverlayFS只有兩層,不是多層,所以OverlayFS “copy-up”操作快於AUFS。以此可以減少操作延時。
  2. OverlayFS支持頁緩存共享,多個容器訪問同一個文件能共享一個頁緩存,以此提高內存使用。
  3. OverlayFS消耗inode,隨著鏡像和容器增加,inode會遇到瓶頸。Overlay2能解決這個問題。在Overlay下,為了解決inode問題,可以考慮將/var/lib/docker掛在單獨的文件系統上,或者增加系統inode設置。
  4. 有兼容性問題。open(2)只完成部分POSIX標準,OverlayFS的某些操作不符合POSIX標準。例如: 調用fd1=open("foo", O_RDONLY) ,然後調用fd2=open("foo", O_RDWR) 應用期望fd1 和fd2是同一個文件。然後由於復制操作發生在第一個open(2)操作後,所以認為是兩個不同的文件。
  5. 不支持rename系統調用,執行“copy”和“unlink”時,將導致失敗。

Btrfs

Btrfs被稱為下一代寫時復制文件系統,並入Linux內核,也是文件級級存儲,但可以像Device mapper一直接操作底層設備。Btrfs利用 Subvolumes和Snapshots管理鏡像容器分層。Btrfs把文件系統的一部分配置為一個完整的子文件系統,稱之為Subvolume,Snapshot是Subvolumn的實時讀寫拷貝,chunk是分配單位,通常是1GB。那麽采用 Subvolume,一個大的文件系統可以被劃分為多個子文件系統,這些子文件系統共享底層的設備空間,在需要磁盤空間時便從底層設備中分配,類似應用程序調用 malloc()分配內存一樣。為了靈活利用設備空間,Btrfs 將磁盤空間劃分為多個chunk 。每個chunk可以使用不同的磁盤空間分配策略。比如某些chunk只存放metadata,某些chunk只存放數據。這種模型有很多優點,比如Btrfs支持動態添加設備。用戶在系統中增加新的磁盤之後,可以使用Btrfs的命令將該設備添加到文件系統中。Btrfs把一個大的文件系統當成一個資源池,配置成多個完整的子文件系統,還可以往資源池裏加新的子文件系統,而基礎鏡像則是子文件系統的快照,每個子鏡像和容器都有自己的快照,這些快照則都是subvolume的快照。
技術分享圖片

分析
  1. Btrfs是替換Device mapper的下一代文件系統, 很多功能還在開發階段,還沒有發布正式版本,相比EXT4或其它更成熟的文件系統,它在技術方面的優勢包括豐富的特征,如:支持子卷、快照、文件系統內置壓縮和內置RAID支持等。
  2. 不支持頁緩存共享,N個容器訪問相同的文件需要緩存N次。不適合高密度容器場景。
  3. 當前Btrfs版本使用“small writes”,導致性能問題。並且需要使用Btrfs原生命令btrfs filesys show替代df。
  4. Btrfs使用“journaling”寫數據到磁盤,這將影響順序寫的性能。
  5. Btrfs文件系統會有碎片,導致性能問題。當前Btrfs版本,能通過mount時指定autodefrag 做檢測隨機寫和碎片整理。

ZFS

ZFS文件系統是一個革命性的全新的文件系統,它從根本上改變了文件系統的管理方式,ZFS 完全拋棄了“卷管理”,不再創建虛擬的卷,而是把所有設備集中到一個存儲池中來進行管理,用“存儲池”的概念來管理物理存儲空間。過去,文件系統都是構建在物理設備之上的。為了管理這些物理設備,並為數據提供冗余,“卷管理”的概念提供了一個單設備的映像。而ZFS創建在虛擬的,被稱為“zpools”的存儲池之上。每個存儲池由若幹虛擬設備(virtual devices,vdevs)組成。這些虛擬設備可以是原始磁盤,也可能是一個RAID1鏡像設備,或是非標準RAID等級的多磁盤組。於是zpool上的文件系統可以使用這些虛擬設備的總存儲容量。Docker的ZFS利用snapshots和clones,它們是ZFS的實時拷貝,snapshots是只讀的,clones是讀寫的,clones從snapshot創建。

下面看一下在Docker裏ZFS的使用。首先從zpool裏分配一個ZFS文件系統給鏡像的基礎層,而其他鏡像層則是這個ZFS文件系統快照的克隆,快照是只讀的,而克隆是可寫的,當容器啟動時則在鏡像的最頂層生成一個可寫層。如下圖所示:
技術分享圖片

分析
  1. ZFS同 Btrfs類似是下一代文件系統。ZFS在Linux(ZoL)port是成熟的,但不推薦在生產環境上使用Docker的 ZFS存儲方式,除非你有ZFS文件系統的經驗。
  2. 警惕ZFS內存問題,因為,ZFS最初是為了有大量內存的Sun Solaris服務器而設計 。
  3. ZFS的“deduplication”特性,因為占用大量內存,推薦關掉。但如果使用SAN,NAS或者其他硬盤RAID技術,可以繼續使用此特性。
  4. ZFS caching特性適合高密度場景。
  5. ZFS的128K塊寫,intent log及延遲寫可以減少碎片產生。
  6. 和ZFS FUSE實現對比,推薦使用Linux原生ZFS驅動。

第四部分 總結

另外,下圖列出Docker各種存儲方式的優點缺點:
技術分享圖片
以上是五種Docker存儲方式的介紹及分析,以此為理論依據,選擇自己的Docker存儲方式。同時可以做一些驗證測試:如IO性能測試,以此確定適合自己應用場景的存儲方式。同時,有兩點值得提出:
  1. 使用SSD(Solid State Devices)存儲,提高性能。
  2. 考慮使用數據卷掛載提高性能。

分享人範彬,數人雲架構師,曾在惠普CMS研發中心及畢益輝WebLogic組工作多年,在Java,J2EE,SOA,企業應用等方面的研發工作 中積累了豐富的經驗。對 Docker,Mesos 有所研究,熟悉和熱愛雲計算、分布式等領域相關技術。 DockOne每周都會組織定向的技術分享,

Docker存儲方式選型建議