1、分層的鏡像
我們可以去下載一個鏡像,注意觀察下載的日志輸出,可以看到Docker的鏡像是一層一層的在下載。
思考:為什么Docker鏡像要采用這種分層的結構呢?
最大的好處,我覺得莫過於是資源共享了!
比如有多個鏡像都從相同的Base
鏡像構建而來,那么宿主機只需在磁盤上保留一份Base
鏡像,同時內存中也只需要加載一份Base
鏡像,這樣就可以為所有的容器服務了,而且鏡像的每一層都可以被共享。
查看鏡像分層的方式可以通過docker image inspect 鏡像名
命令,如下:
[root@192 /]# docker image inspect redis:latest
[
{
"Id": "sha256:621ceef7494adfcbe0e523593639f6625795cc0dc91a750629367a8c7b3ccebb",
"RepoTags": [
"redis:latest"
],
... # 省略
... # 省略
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:cb42413394c4059335228c137fe884ff3ab8946a014014309676c25e3ac86864",
"sha256:8e14cb7841faede6e42ab797f915c329c22f3b39026f8338c4c75de26e5d4e82",
"sha256:1450b8f0019c829e638ab5c1f3c2674d117517669e41dd2d0409a668e0807e96",
"sha256:f927192cc30cb53065dc266f78ff12dc06651d6eb84088e82be2d98ac47d42a0",
"sha256:a24a292d018421783c491bc72f6601908cb844b17427bac92f0a22f5fd809665",
"sha256:3480f9cdd491225670e9899786128ffe47054b0a5d54c48f6b10623d2f340632"
]
},
... # 省略
}
]
可以看到RootFS
屬性中的Layers
參數中,每一行代表一個鏡像層,每個一鏡像層都包含着一些操作步驟,最終合並成一個我們需要的系統環境和服務。
2、加深理解
所有的Docker鏡像都起始於一個基礎鏡像層,當進行修改或增加新的內容時,就會在當前鏡像層之上,創建新的鏡像層。
舉一個簡單的例子,假如基於Ubuntu Linux 16.04
創建一個新的鏡像,這就是新鏡像的第一層;如果在該鏡像中添加Python包,就會在基礎鏡像層之上創建第二個鏡像層;如果繼續添加一個安全補丁,就會創建第三個鏡像層。
該鏡像當前已經包含3個鏡像層,如下圖所示(這只是一個用於演示的很簡單的例子)。
在添加額外的鏡像層的同時,鏡像始終保持是當前所有鏡像的組合,理解這一點非常重要。下圖中舉了一個簡單的例子,每個鏡像層包含3個文件,而整體的鏡像包含了來自兩個鏡像層的6個文件。
上圖中的鏡像層跟之前圖中的略有區別,主要目的是便於展示文件。
下圖中展示了一個稍微復雜的三層鏡像,在外部看來整個鏡像只有6個文件,這是因為最上層中的文件7是文件5的一個更新版本。
這種情況下,上層鏡像層中的文件覆蓋了底層鏡像層中的文件。這樣就使得文件的更新版本作為一個新鏡像層添加到鏡像當中。
Docker通過存儲引擎(新版本采用快照機制)的方式來實現鏡像層堆棧,並保證多鏡像層對外展示為統一的文件系統。
Linux上可用的存儲引擎有AUFS
,Overlay2
,Device Mapper
,Btrfs
以及ZFS
。顧名思義,每種存儲引擎都基於Linux中對應的文件系統或者塊設備技術,並且每種存儲引擎都有其獨有的性能特點。
Docker在Windows上僅支持windowsfilter
一種存儲引擎,該引擎基於NTFS
文件系統之上實現了分層和Cow
。
如上邊的三層鏡像,Docker最終會把所有鏡像層堆疊並合並,對外提供統一的視圖,如下圖。
3、特別說明
Docker鏡像都是只讀的,當容器啟動時,一個新的可寫層被加載到鏡像的頂部,只有這個頂部是可寫的。
而這一層就是我們通常說的容器層,容器之下的都叫鏡像層。
如下圖:
下面這張圖更加形象: