映象分層結構理論詳解1
映象分層結構理論
Docker映象
據Docker官網的技術文件描述,Image(映象)是Docker術語的一種,代表一個只讀的layer。而layer則具體代表Docker Container檔案系統中可疊加的一部分。
筆者如此介紹Docker映象,相信眾多Docker愛好者理解起來依舊是雲裡霧裡。那麼理解之前,先讓我們來認識一下與Docker映象相關的4個概念:rootfs、Union mount、image以及layer。
rootfs
Docker容器在執行時,其內部程序可見的檔案系統的視角,或者Docker容器的根目錄,rootfs 通常包含一個作業系統執行所需的檔案系統,例如可能包含典型的類 Unix 作業系統中的目錄系統,如 /dev、/proc、/bin、/etc、/lib、/usr、/tmp 及執行 docker 容器所需的配置檔案、工具等。
傳統來說,Linux系統在啟動核心時,核心首先會掛載一個只讀的rootfs,當系統檢查其是否完整後,再決定是否切換為讀寫模式。或者在最後rootfs之上,另行掛載一個檔案系統,並且忽略rootfs。
docker架構下沿用了linux中的rootfs的思想,當 docker daemon 為 docker 容器掛載 rootfs 時與傳統的linux核心極其類似,將其設定為只對模式,在rootfs掛載完成之後,和linux核心不一樣的是,docker daemon沒有將它切換為讀寫模組,而是利用mount技術在已有的只讀 rootfs 上再掛載一個讀寫的檔案系統,這個檔案系統中空無一物
如:
假設使用者已經通過Docker Registry拉取了centos的映象,並通過命令docker run –it centos /bin/bash
將其啟動執行。則Docker Daemon為其建立的rootfs以及容器可讀寫的檔案系統包括了一下目錄:
/bin /dev /home /lib64 /mnt /proc /srv /tmp /var/ boot
/lib /etc /media /opt /root /sbin /sys /usr...等
以上這些就是rootfs
該容器中的程序對rootfs中的內容只擁有讀許可權,對於read-write讀寫檔案系統中的內容既擁有讀許可權也擁有寫許可權。
容器雖然只有一個檔案系統,但該檔案系統由“兩層”組成,分別為讀寫檔案系統和只讀檔案系統。這樣的理解已然有些層級(layer)的意味。
簡單來講,可以將Docker 容器的檔案系統分為兩部分,而上面提到是Docker Daemon利用Mount的技術,將兩者掛載
這種mount技術就是union mount(聯合掛載)
union mount
代表一種檔案系統掛載的方式,允許同一時刻多種檔案系統掛載在一起,並以一種檔案系統的形式,呈現多種檔案系統內容合併後的目錄。
一般情況下,通過某種檔案系統掛載內容至掛載點的話,掛載點目錄中原先的內容將會被隱藏。而Union mount則不會將掛載點目錄中的內容隱藏,反而是將掛載點目錄中的內容和被掛載的內容合併,併為合併後的內容提供一個統一獨立的檔案系統視角。通常來講,被合併的檔案系統中只有一個會以讀寫(read-write)模式掛載,而其他的檔案系統的掛載模式均為只讀(read-only)。實現這種Union mount技術的檔案系統一般被稱為Union Filesystem,較為常見的有UnionFS、AUFS、OverlayFS等。
Docker實現容器檔案系統Union mount時,提供多種具體的檔案系統解決方案,如Docker早版本沿用至今的的AUFS,還有在docker 1.4.0版本中開始支援的OverlayFS等。
Docker實現容器檔案系統Union mount時,提供多種具體的檔案系統解決方案,如Docker早版本沿用至今的的AUFS,還有在docker 1.4.0版本中開始支援的OverlayFS等。
更深入的瞭解Union mount,可以使用AUFS檔案系統來看
可以暫且將該容器整個rootfs當成是一個檔案系統。上文也提到,掛載時讀寫(read-write)檔案系統中空無一物。既然如此,從使用者視角來看,容器內檔案系統和rootfs完全一樣,使用者完全可以按照往常習慣,無差別的使用自身視角下檔案系統中的所有內容;然而,從核心的角度來看,兩者在有著非常大的區別。追溯區別存在的根本原因,那就不得不提及AUFS等檔案系統的COW(copy-on-write)特性。
COW檔案系統和其他檔案系統最大的區別就是:從不覆寫已有檔案系統中已有的內容。由於通過COW檔案系統將兩個檔案系統(rootfs和read-write filesystem)合併,終端使用者視角為合併後的含有所有內容的檔案系統,然而在Linux核心邏輯上依然可以區別兩者,那就是使用者對原先rootfs中的內容擁有隻讀許可權,而對read-write filesystem中的內容擁有讀寫許可權。
既然對使用者而言,全然不知哪些內容只讀,哪些內容可讀寫,這些資訊只有核心在接管,那麼假設使用者需要更新其視角下的檔案/etc/hosts,而該檔案又恰巧是rootfs只讀檔案系統中的內容,核心是否會丟擲異常或者駁回使用者請求呢?答案是否定的。當此情形發生時,COW檔案系統首先不會覆寫read-only檔案系統中的檔案,即不會覆寫rootfs中/etc/hosts,其次反而會將該檔案拷貝至讀寫檔案系統中,即拷貝至讀寫檔案系統中的/etc/hosts,最後再對後者進行更新操作。如此一來,縱使rootfs與read-write filesystem中均由/etc/ hosts,諸如AUFS型別的COW檔案系統也能保證使用者視角中只能看到read-write filesystem中的/etc/hosts,即更新後的內容。
當然,這樣的特性同樣支援rootfs中檔案的刪除等其他操作。例如:使用者通過apt-get軟體包管理工具安裝Golang,所有與Golang相關的內容都會被安裝在讀寫檔案系統中,而不會安裝在rootfs。此時使用者又希望通過apt-get軟體包管理工具刪除所有關於MySQL的內容,恰巧這部分內容又都存在於rootfs中時,刪除操作執行時同樣不會刪除rootfs實際存在的MySQL,而是在read-write filesystem中刪除該部分內容,導致最終rootfs中的MySQL對容器使用者不可見,也不可訪。
一個最簡單的例子,現有三個檔案系統,一個有a檔案,一個有b檔案,一個有c檔案,通過union mount到一個目錄後,abc會出現在這個目錄中,並不會被覆蓋。
image
在linux的grub2檔案中有這麼兩個內容vmlinuz
和initramfs
,前者在強制更改root密碼,重啟linux系統時就是在vmlinuz處打斷系統啟動的,這是linux真正的核心,而initramfs,拆開來看,init用來初始化系統啟動,ram是代表記憶體,fs是檔案系統,開機時,initramfs是會被提前載入到計算機中的
linux啟動引導過程
首先BIOS進行對硬體的加電自檢,沒有問題之後,根據BIOS的啟動項找到第一個可啟動的裝置(硬碟),開始讀取硬碟中的MBR分割槽(記錄了作業系統在磁碟的哪一個位置)
知道了位置之後還是找不到系統,因為它讀不到系統所在的檔案系統vfs(預設可以讀到的檔案系統在/boot/grub2/i386-pc
目錄下記錄)。這個時候initramfs就有了作用,作為執行在記憶體上的一個假的作業系統(rootfs的原型),這個作業系統可以讀到真正系統所在的檔案系統 ,然後就可以找到作業系統,這個時候就會將initramfs解除安裝,最終就可以在系統之上執行各種程式軟體
而在容器中,不會去解除安裝rootfs,而是採用union mount的方式直接將其中的檔案直接掛載到真正作業系統上去用
雖然通過AUFS可以實現rootfs與read-write filesystem的合併,但是考慮到rootfs自身接近200MB的磁碟大小,如果以這個rootfs的粒度來實現容器的建立與遷移等,是否會稍顯笨重,同時也會大大降低映象的靈活性。而且,容器中擁有一個rootfs,那麼就要建立一個全新的rootfs,都是物理機的centos的rootfs和映象的centos的rootfs中有很多一致的內容。是可以共用的
Docker中image的概念,非常巧妙的解決了以上的問題。最為簡單的解釋image,就是 Docker容器中只讀檔案系統rootfs的一部分。換言之,實際上Docker容器的rootfs可以由多個image來構成。多個image構成rootfs的方式依然沿用Union mount技術。
多個Image構成rootfs的示意圖如圖(圖中,rootfs中每一層image中的內容劃分只為了闡述清楚rootfs由多個image構成,並不代表實際情況中rootfs中的內容劃分):
從上圖可以看出,舉例的容器rootfs包含4個image,其中每個image中都有一些使用者視角檔案系統中的一部分內容。4個image處於層疊的關係,除了最底層的image,每一層的image都疊加在另一個image之上。另外,每一個image均含有一個image ID,用以唯一的標記該image。
layer
Docker術語中,layer是一個與image含義較為相近的詞。容器映象的rootfs是容器只讀的檔案系統,rootfs又是由多個只讀的image構成。於是,rootfs中每個只讀的image都可以稱為一層layer。
除了只讀的image之外,Docker Daemon在建立容器時會在容器的rootfs之上,再mount一層read-write filesystem,而這一層檔案系統,也稱為容器的一層layer,常被稱為top layer。
因此,總結而言,Docker容器中的每一層只讀的image,以及最上層可讀寫的檔案系統,均被稱為layer。如此一來,layer的範疇比image多了一層,即多包含了最上層的read-write filesystem。
有了layer的概念,大家可以思考這樣一個問題:容器檔案系統分為只讀的rootfs,以及可讀寫的top layer,那麼容器執行時若在top layer中寫入了內容,那這些內容是否可以持久化,並且也被其它容器複用?
上文對於image的分析中,提到了image有複用的特性,既然如此,再提一個更為大膽的假設:容器的top layer是否可以轉變為image?
答案是肯定的。Docker的設計理念中,top layer轉變為image的行為(Docker中稱為commit操作),大大釋放了容器rootfs的靈活性。Docker的開發者完全可以基於某個映象建立容器做開發工作,並且無論在開發週期的哪個時間點,都可以對容器進行commit,將所有top layer中的內容打包為一個image,構成一個新的映象。Commit完畢之後,使用者完全可以基於新的映象,進行開發、分發、測試、部署等。不僅docker commit的原理如此,基於Dockerfile的docker build,其追核心的思想,也是不斷將容器的top layer轉化為image。