什麼是 image?簡單的解釋,就是一個唯讀的範本, docker 可以讀取這個範本然後產生出一個 container 來運行。
一個 Docker Image 的內部是由多個 Layer 所一層一層疊加起來的。每一層 layer 裡面,各自會有各自不同儲存的設定跟檔案。例如第一層 layer 可能是只儲存一些環境設定,第二層 layer 有服務執行相關的檔案……等。最後這些各層不同的 layer 合起來,就是一個 image 檔案。
這些不同的 layer 可以從 docker image inspect [image_name] 指令來觀察到,下完這個指令後出現的描述內容,可以很容易找到 RootFS底下的Layers欄位,裡面就會列出所有的 layer。
在每個 layer 後面都有不同的 hash 值,這可以讓不同的 image 檔案在 container 跑起來時,docker 會幫忙是否已經有同樣的 layer 檔案,有的話就可以拿來重覆使用。
這麼多層的 layer 之所以可以重疊在一起呈現,是因為有個叫 union mount 的東東在背後運作。

這時候可以發揮一下想像力,假設電腦裡有2個資料夾,但我們要把他合併(union),然後只用1個資料夾來做呈現。這時候所謂的聯集做法,就是會把FolderA + FolderB二邊都有的資料加起來,最後合併完一起顯示在 merged 資料夾。
--FolderA
|-file1.txt
|-file2.txt
--FolderB
|-file2.txt
|-file3.txt
--FolderUnion
|-file1.txt
|-file2.txt(會被後蓋前)
|-file3.txt
union mount 是一種概念,就像 SQL 裡的 union 一樣,它不是什麼規格的定義或規範。只要能實作出把二個東西合併(聯集)起來,都算是一種 union mount 的實作。像我在 AWS EC2 測試用所開的 ubuntu 系統,看到的就是 overlayFS。假如是在 windows or mac 上看到的應該就又會不同。
在 union mount 跑起來的時候,可以預期會有 lower, upper & merged 三個部份(還有個 work,但先不提)。
lower 層:可以解讀成是唯讀來源。這個來源可以有很多個,在 image 檔裡 lower 層就是剛才提到的很多層的 layer。lower 層的特性是一定是唯讀的,不管對 mount 後的資料做任何異動,都不會影響到 lower 層裡的資料。
upper 層:可以解讀成是可修改來源。在 image 檔裡 upper 層就是 container 跑起來的時候產生的。upper 層的特性是,可以對裡面的資料做任何異動,但這些異動都只會影響到 upper 層,決對不會影響到 lower 層。所以當我們在跑一個 container 所傳入的參數設定,其實都會是在 upper 層裡面,不會去動到 lower 層。
merged 層:可以解讀成是合併後的結果。剛才的 lower + upper 二個所聯集後的結果,也就是我們在 container 裡所看到的環境跟資料了。
merged 層是聯集後所顯示的結果,也就是我們看得到絕對唯讀的 lower 層檔案,甚至還能對它進行修改。但這樣說又有點矛盾,因為剛才一直在提 lower 層一定是是唯讀的。
這時候就有另外一個機制叫 copy-on-write。白話就是……寫入的時候再複製。
但簡單來說,在 container 的環境裡修改檔案,理論上就只有二個情境。
改到 lower 層的檔案:要修改的檔案在 upper 層沒有,它只存在 lower 層裡。這時候就會觸發 copy-on-write 的機制,也就是會把 lower 層裡的檔案複製一份到 upper 層,然後所有的檔案異動都是在 upper 層發生。這樣就可以保證 lower 層的檔案不會被異動到, upper 層的檔案也不會被其他 container 影響到。但這裡就要注意,從 lower 層複製到 upper 層,代表原本的檔案會在,upper 層也會有一個相同的檔案,所以一個檔案會放二邊。當要被異動的這個動案有點大的時候,時間跟空間的成本就會稍微高一些。
改到 upper 層的檔案:要修改的檔案 upper 層裡面有(不管 lower 層有沒有),這時候就沒有什麼 copy-on-write 的機制,直接所有的異動都直接發生在 upper 層。
除了 copy-on-write,upper 層還另一個機制叫 whiteout,概念比較像是刪除註記。
在 merged 層除了剛才的修改動作外,也有機會會有刪除的動作。刪除動作基本上跟 copy-on-write 一樣,大致上就二種情境。
刪除 lower 層的檔案:要刪除的檔案在 upper 層沒有,它只存在 lower 層裡。這時候就會觸發 whiteout 的機制,也就是會在 upper 層建立一個 whiteout 檔案(很像是註記一樣),這個 whiteout 檔案會把 lower 層的檔案遮起來,讓它在 merged 層時不會顯示。但實際上 lower 層的檔案還是存在(絕對唯讀),只是在 merged 層時看不到而已。
刪除 upper 層的檔案:要刪除的檔案在 upper 層裡,這時候不會觸發 whiteout 機制,會實際把 upper 層的檔案直接刪除。
初次研究 docker image 的內部原理,發現真的是蠻聰明的……。照著 instruction 來自己 commit 一個 image 出來,再 inspect 他的變化,也多出了不少的靈感。期待在玩 Dockerfile 的時候可以有其他更多的靈感~~