目錄linux
在說docker的文件系統以前,咱們須要先想清楚一個問題。咱們知道docker的啓動是依賴於image,docker在啓動以前,須要先拉取image,而後啓動。多個容器可使用同一個image啓動。那麼問題來了:這些個容器是共用一個image,仍是各自將這個image複製了一份,而後各自獨立運行呢?nginx
咱們假設每一個容器都複製了一份這個image,而後各自獨立運行,那麼就意味着,啓動多少個容器,就須要複製多少個image,毫無疑問這是對空間的一種巨大浪費。事實上,在容器的設計當中,經過同一個Image啓動的容器,所有都共享這個image,而並不複製。那麼問題又隨之而來:既然全部的容器都共用這一個image,那麼豈不是我在任意一個容器中所作的修改,在其餘容器中均可見?若是我一個容器要將一個配置文件修改爲A,而另外一個容器一樣要將這個文件修改爲B,兩個容器豈不是會產生衝突?docker
咱們把上面的問題放一放,先來看下面一個拉取鏡像的示例:apache
root@ubuntu:~# docker pull nginx Using default tag: latest latest: Pulling from library/nginx be8881be8156: Pull complete 32d9726baeef: Pull complete 87e5e6f71297: Pull complete Digest: sha256:6ae5dd1664d46b98257382fd91b50e332da989059482e2944aaa41ae6cf8043a Status: Downloaded newer image for nginx:latest
上面的示例是從docker官方鏡像倉庫拉取一個nginx:latest鏡像,能夠看到在拉取鏡像時,是一層一層的拉取的。事實上鏡像也是這麼一層一層的存儲在磁盤上的。一般一個應用鏡像包含多層,以下:ubuntu
咱們首先須要明確一點,鏡像是隻讀的。每一層都只讀。在上圖上,咱們能夠看到,在內核之上,最底層首先是一個基礎鏡像層,這裏是一個ubuntu的基礎鏡像,由於鏡像的只讀特性,若是咱們想要在這個ubuntu的基礎鏡像上安裝一個emacs編輯器,則只能在基礎鏡像之上,在構建一層新的鏡像層。一樣的道理,若是想要在當前的emacs鏡像層之上添加一個apache,則只能在其上再構建一個新的鏡像層。而這便是鏡像的分層特性。緩存
咱們剛剛在說鏡像的分層特性的時候說到鏡像是隻讀的。而事實上當咱們使用鏡像啓動一個容器的時候,咱們實際上是能夠在容器裏隨意讀寫的,從結果上看,彷佛與鏡像的只讀特性相悖。併發
咱們繼續看上面的圖,其實能夠看到在鏡像的最上層,還有一個讀寫層。而這個讀寫層,即在容器啓動時爲當前容器單獨掛載。每個容器在運行時,都會基於當前鏡像在其最上層掛載一個讀寫層。而用戶針對容器的全部操做都在讀寫層中完成。一旦容器銷燬,這個讀寫層也隨之銷燬。app
知識點: 容器=鏡像+讀寫層框架
而咱們針對這個讀寫層的操做,主要基於兩種方式:寫時複製和用時分配。編輯器
全部驅動都用到的技術——寫時複製(CoW)。CoW就是copy-on-write,表示只在須要寫時纔去複製,這個是針對已有文件的修改場景。好比基於一個image啓動多個Container,若是爲每一個Container都去分配一個image同樣的文件系統,那麼將會佔用大量的磁盤空間。而CoW技術可讓全部的容器共享image的文件系統,全部數據都從image中讀取,只有當要對文件進行寫操做時,才從image裏把要寫的文件複製到本身的文件系統進行修改。因此不管有多少個容器共享同一個image,所作的寫操做都是對從image中複製到本身的文件系統中的複本上進行,並不會修改image的源文件,且多個容器操做同一個文件,會在每一個容器的文件系統裏生成一個複本,每一個容器修改的都是本身的複本,相互隔離,相互不影響。使用CoW能夠有效的提升磁盤的利用率。
用時分配是用在本來沒有這個文件的場景,只有在要新寫入一個文件時才分配空間,這樣能夠提升存儲資源的利用率。好比啓動一個容器,並不會爲這個容器預分配一些磁盤空間,而是當有新文件寫入時,才按需分配新空間。
接下來咱們說一說,這些分層的鏡像是如何在磁盤中存儲的。
docker提供了多種存儲驅動來實現不一樣的方式存儲鏡像,下面是經常使用的幾種存儲驅動:
下面說一說AUFS、OberlayFS及Devicemapper,更多的存儲驅動說明可參考:http://dockone.io/article/1513
AUFS(AnotherUnionFS)是一種Union FS,是文件級的存儲驅動。AUFS是一個能透明覆蓋一個或多個現有文件系統的層狀文件系統,把多層合併成文件系統的單層表示。簡單來講就是支持將不一樣目錄掛載到同一個虛擬文件系統下的文件系統。這種文件系統能夠一層一層地疊加修改文件。不管底下有多少層都是隻讀的,只有最上層的文件系統是可寫的。當須要修改一個文件時,AUFS建立該文件的一個副本,使用CoW將文件從只讀層複製到可寫層進行修改,結果也保存在可寫層。在Docker中,底下的只讀層就是image,可寫層就是Container。結構以下圖所示:
Overlay是Linux內核3.18後支持的,也是一種Union FS,和AUFS的多層不一樣的是Overlay只有兩層:一個upper文件系統和一個lower文件系統,分別表明Docker的鏡像層和容器層。當須要修改一個文件時,使用CoW將文件從只讀的lower複製到可寫的upper進行修改,結果也保存在upper層。在Docker中,底下的只讀層就是image,可寫層就是Container。目前最新的OverlayFS爲Overlay2。結構以下圖所示:
Device mapper是Linux內核2.6.9後支持的,提供的一種從邏輯設備到物理設備的映射框架機制,在該機制下,用戶能夠很方便的根據本身的須要制定實現存儲資源的管理策略。前面講的AUFS和OverlayFS都是文件級存儲,而Device mapper是塊級存儲,全部的操做都是直接對塊進行操做,而不是文件。Device mapper驅動會先在塊設備上建立一個資源池,而後在資源池上建立一個帶有文件系統的基本設備,全部鏡像都是這個基本設備的快照,而容器則是鏡像的快照。因此在容器裏看到文件系統是資源池上基本設備的文件系統的快照,並無爲容器分配空間。當要寫入一個新文件時,在容器的鏡像內爲其分配新的塊並寫入數據,這個叫用時分配。當要修改已有文件時,再使用CoW爲容器快照分配塊空間,將要修改的數據複製到在容器快照中新的塊裏再進行修改。Device mapper 驅動默認會建立一個100G的文件包含鏡像和容器。每個容器被限制在10G大小的卷內,能夠本身配置調整。結構以下圖所示:
存儲驅動 | 特色 | 優勢 | 缺點 | 適用場景 |
---|---|---|---|---|
AUFS | 聯合文件系統、未併入內核主線、文件級存儲 | 做爲docker的第一個存儲驅動,已經有很長的歷史,比較穩定,且在大量的生產中實踐過,有較強的社區支持 | 有多層,在作寫時複製操做時,若是文件比較大且存在比較低的層,可能會慢一些 | 大併發但少IO的場景 |
overlayFS | 聯合文件系統、併入內核主線、文件級存儲 | 只有兩層 | 無論修改的內容大小都會複製整個文件,對大文件進行修改顯示要比小文件消耗更多的時間 | 大併發但少IO的場景 |
Devicemapper | 併入內核主線、塊級存儲 | 塊級不管是大文件仍是小文件都只複製須要修改的塊,並非整個文件 | 不支持共享存儲,當有多個容器讀同一個文件時,須要生成多個複本,在不少容器啓停的狀況下可能會致使磁盤溢出 | 適合io密集的場景 |
Btrfs | 併入linux內核、文件級存儲 | 能夠像devicemapper同樣直接操做底層設備,支持動態添加設備 | 不支持共享存儲,當有多個容器讀同一個文件時,須要生成多個複本 | 不適合在高密度容器的paas平臺上使用 |
ZFS | 把全部設備集中到一個存儲池中來進行管理 | 支持多個容器共享一個緩存塊,適合內存大的環境 | COW使用碎片化問題更加嚴重,文件在硬盤上的物理地址會變的再也不連續,順序讀會變的性能比較差 | 適合paas和高密度的場景 |
AUFS和Overlay都是聯合文件系統,但AUFS有多層,而Overlay只有兩層,因此在作寫時複製操做時,若是文件比較大且存在比較低的層,則AUSF可能會慢一些。並且Overlay併入了linux kernel mainline,AUFS沒有。目前AUFS已基本被淘汰
OverlayFS是文件級存儲,Device mapper是塊級存儲,當文件特別大而修改的內容很小,Overlay無論修改的內容大小都會複製整個文件,對大文件進行修改顯示要比小文件要消耗更多的時間,而塊級不管是大文件仍是小文件都只複製須要修改的塊,並非整個文件,在這種場景下,顯然device mapper要快一些。由於塊級的是直接訪問邏輯盤,適合IO密集的場景。而對於程序內部複雜,大併發但少IO的場景,Overlay的性能相對要強一些。