Docker?進(jìn)階之鏡像分層方案詳解
導(dǎo)讀
可以想象,像 ubuntu等基礎(chǔ)鏡像,體積必然不小。那么,思考以下幾個(gè)問(wèn)題:
- 我們基于同一個(gè)鏡像(ubuntu 18.4)啟動(dòng)了兩個(gè)容器,會(huì)占用兩倍磁盤(pán)空間嗎?
- 我們?cè)谌萜鲀?nèi)修改或者新建了某個(gè)文件,要修改原鏡像嗎?
- 我們基于某鏡像(ubuntu 18.04)新建一個(gè)鏡像(myubuntu),需要將原鏡像文件全部拷貝到新鏡像中嗎?
首先,讓我們嘗試思考下,如果我們?nèi)プ?,該如何高效的解決這些問(wèn)題?
- 方案一,…
- 方案二,…
- 方案三,…
入門(mén)圖解
創(chuàng)建測(cè)試鏡像
我們創(chuàng)建一個(gè)最簡(jiǎn)單的鏡像:
1.構(gòu)建測(cè)試鏡像v1.0:docker build -t image_test:1.0 .
FROM alpine:3.15.0 #除了繼承基礎(chǔ)鏡像,啥也不做
2.構(gòu)建測(cè)試鏡像v2.0:docker build -t image_test:2.0 .
FROM alpine:3.15.0 RUN dd if=/dev/zero of=file1 bs=10M count=1 #添加一個(gè)10M的文件file1
3.構(gòu)建測(cè)試鏡像v3.0:docker build -t image_test:3.0 .
FROM alpine:3.15.0 RUN dd if=/dev/zero of=file1 bs=10M count=1 #添加一個(gè)10M的文件file1 RUN dd if=/dev/zero of=file2 bs=10M count=1 #添加一個(gè)10M的文件file2
這樣本地就構(gòu)建了3個(gè)測(cè)試鏡像:
查看鏡像
我們有2種方法查看鏡像:
使用docker inspect
:獲取鏡像的元數(shù)據(jù)使用docker history
:查看鏡像的構(gòu)建歷史 使用docker inspect
使用docker inspect
查看鏡像的元數(shù)據(jù)。
其中Parent
可以看到父鏡像, Layers
這一項(xiàng)下面可以看到鏡像的所有層。
使用docker history
使用docker history
可以看到鏡像的構(gòu)建歷史。
我們每一行列出了鏡像包含的層。
鏡像分層圖
根據(jù)上面的docker history
命令,我們可以輕松的畫(huà)出三個(gè)鏡像的分層圖:
從上面的圖可以看到,我們的鏡像是分層的,我們的Dockerfile中新增一條指令,就會(huì)新增一層!
如果我們將多個(gè)命令合成一個(gè),那么也只會(huì)生成一層。修改一下上面的image_test:3.0,把兩條RUN合并成一條:
FROM alpine:3.15.0 RUN dd if=/dev/zero of=file1 bs=10M count=1 && \ dd if=/dev/zero of=file2 bs=10M count=1
使用docker history
查看image_test:4.0,可以看到,只有2層了!
鏡像分層的好處
知道了鏡像是分層的,那么我們是不是好奇為啥要這么設(shè)計(jì)呢?
試想一下我們?nèi)绻环謱訒?huì)有什么問(wèn)題?
以拉取鏡像為例!
拉取鏡像的鏡像很大,比如Redis的鏡像有100多M
第一次我們拉取6.2版本的Redis,下載了完成的100M到本地,下次我要下載6.2.6版本的,是不是又得下載100M。
盡管可能兩個(gè)版本之間就改了幾行配置文件。
這樣是非常低效的。如果能只下載有差異的部分就好了!
這個(gè)痛點(diǎn),也就是鏡像分層要解決的問(wèn)題。實(shí)際上,Docker也是這么實(shí)現(xiàn)的。
第一次下載redis:6.2時(shí),因?yàn)橹皼](méi)有下載過(guò),所以下載了所有的層,總共113M。網(wǎng)絡(luò)慢點(diǎn)的話還是需要花一些時(shí)間的!
第二次下載redis:7.0-rc,就變得快了很多!因?yàn)榍懊?層是redis:6.2是一樣的,這些層已經(jīng)下載過(guò)了!
如果版本2是基于版本1的基礎(chǔ)上,那么版本2不需要copy一份全量的數(shù)據(jù),只需一份和版本1差異化的增量數(shù)據(jù)即可!
這樣的最終好處是,可以體現(xiàn)在以下方面:
- 拉取更快:因?yàn)榉謱恿耍恍枥”镜夭淮嬖诘膶蛹纯桑?/li>
- 存儲(chǔ)更少:因?yàn)楣餐膶又恍璐鎯?chǔ)一份即可!
- 運(yùn)行時(shí)存儲(chǔ)更少:容器運(yùn)行時(shí)可以共享相同的層!
對(duì)于第3點(diǎn),多個(gè)基于相同鏡像運(yùn)行的容器,都可以直接使用相同的鏡像層,每個(gè)容器只需一個(gè)自己的可寫(xiě)層即可:
Docker鏡像加載原理
下面這張圖想必各位是不陌生了,再往下還有一張。那我們要如何在這么多不陌生的人里面脫穎而出呢?就看誰(shuí)能把這兩張圖說(shuō)出花來(lái)了哈。
bootfs(boot file system) 主要包含 bootloader 和 kernel, bootloader 主要是引導(dǎo)加載 kernel,Linux 剛啟動(dòng)時(shí)會(huì)加載 bootfs 文件系統(tǒng),在Docker 鏡像的最底層是引導(dǎo)文件系統(tǒng) bootfs。這一層與我們典型的 Linux/Unix 系統(tǒng)是一樣的,包含 boot 加載器和內(nèi)核。當(dāng) boot 加載完成之后整個(gè)內(nèi)核就都在內(nèi)存中了,此時(shí)內(nèi)存的使用權(quán)已由 bootfs 轉(zhuǎn)交給內(nèi)核,此時(shí)系統(tǒng)也會(huì)卸載 bootfs。
rootfs (root file system) ,在 bootfs 之上。包含的就是典型 Linux 系統(tǒng)中的 /dev, /proc, /bin, /etc 等標(biāo)準(zhǔn)目錄和文件。rootfs 就是各種不同的操作系統(tǒng)發(fā)行版,比如 Ubuntu,Centos 等等。
對(duì)于一個(gè)精簡(jiǎn)的 OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序庫(kù)就可以了,因?yàn)榈讓又苯佑?Host 的 kernel,自己只需要提供 rootfs 就行了。由此可見(jiàn)對(duì)于不同的 linux 發(fā)行版, bootfs 基本是一致的, rootfs 會(huì)有差別, 因此不同的發(fā)行版可以公用 bootfs。
Docker鏡像層都是只讀的,容器層是可寫(xiě)的。 當(dāng)容器啟動(dòng)時(shí),一個(gè)新的可寫(xiě)層被加載到鏡像的頂部。 這一層通常被稱作“容器層”,“容器層”之下的都叫“鏡像層”。所有對(duì)容器的改動(dòng),無(wú)論添加、刪除、還是修改文件都只會(huì)發(fā)生在容器層中。
那么,先讓我們來(lái)重新認(rèn)識(shí)一下與 Docker 鏡像相關(guān)的 4 個(gè)概念:rootfs、Union mount、image 以及 layer。
rootfs
Rootfs:代表一個(gè) Docker Container 在啟動(dòng)時(shí)(而非運(yùn)行后)其內(nèi)部進(jìn)程可見(jiàn)的文件系統(tǒng)視角,或者是 Docker Container 的根目錄。當(dāng)然,該目錄下含有 Docker Container 所需要的系統(tǒng)文件、工具、容器文件等。
傳統(tǒng)來(lái)說(shuō),Linux 操作系統(tǒng)內(nèi)核啟動(dòng)時(shí),內(nèi)核首先會(huì)掛載一個(gè)只讀(read-only)的 rootfs,當(dāng)系統(tǒng)檢測(cè)其完整性之后,決定是否將其切換為讀寫(xiě)(read-write)模式,或者最后在 rootfs 之上另行掛載一種文件系統(tǒng)并忽略 rootfs。Docker 架構(gòu)下,依然沿用 Linux 中 rootfs 的思想。當(dāng) Docker Daemon 為 Docker Container 掛載 rootfs 的時(shí)候,與傳統(tǒng) Linux 內(nèi)核類似,將其設(shè)定為只讀(read-only)模式。在 rootfs 掛載完畢之后,和 Linux 內(nèi)核不一樣的是,Docker Daemon 沒(méi)有將 Docker Container 的文件系統(tǒng)設(shè)為讀寫(xiě)(read-write)模式,而是利用 Union mount 的技術(shù),在這個(gè)只讀的 rootfs 之上再掛載一個(gè)讀寫(xiě)(read-write)的文件系統(tǒng),掛載時(shí)該讀寫(xiě)(read-write)文件系統(tǒng)內(nèi)空無(wú)一物。
正如 read-only 和 read-write 的含義那樣,該容器中的進(jìn)程對(duì) rootfs 中的內(nèi)容只擁有讀權(quán)限,對(duì)于 read-write 讀寫(xiě)文件系統(tǒng)中的內(nèi)容既擁有讀權(quán)限也擁有寫(xiě)權(quán)限。容器雖然只有一個(gè)文件系統(tǒng),但該文件系統(tǒng)由“兩層”組成,分別為讀寫(xiě)文件系統(tǒng)和只讀文件系統(tǒng)。
Union mount
Union mount:代表一種文件系統(tǒng)掛載的方式,允許同一時(shí)刻多種文件系統(tǒng)掛載在一起,并以一種文件系統(tǒng)的形式,呈現(xiàn)多種文件系統(tǒng)內(nèi)容合并后的目錄。
一般情況下,通過(guò)某種文件系統(tǒng)掛載內(nèi)容至掛載點(diǎn)的話,掛載點(diǎn)目錄中原先的內(nèi)容將會(huì)被隱藏。而 Union mount 則不會(huì)將掛載點(diǎn)目錄中的內(nèi)容隱藏,反而是將掛載點(diǎn)目錄中的內(nèi)容和被掛載的內(nèi)容合并,并為合并后的內(nèi)容提供一個(gè)統(tǒng)一獨(dú)立的文件系統(tǒng)視角。通常來(lái)講,被合并的文件系統(tǒng)中只有一個(gè)會(huì)以讀寫(xiě)(read-write)模式掛載,而其他的文件系統(tǒng)的掛載模式均為只讀(read-only)。實(shí)現(xiàn)這種 Union mount 技術(shù)的文件系統(tǒng)一般被稱為 Union Filesystem,較為常見(jiàn)的有 UnionFS、AUFS、OverlayFS 等。
Docker 實(shí)現(xiàn)容器文件系統(tǒng) Union mount 時(shí),提供多種具體的文件系統(tǒng)解決方案,如 Docker 早版本沿用至今的的 AUFS,還有在 docker 1.4.0 版本中開(kāi)始支持的 OverlayFS 等。
AUFS 掛載 Ubuntu 文件系統(tǒng)示意圖
使用鏡像 ubuntu 創(chuàng)建的容器中,可以暫且將該容器整個(gè) rootfs 當(dāng)成是一個(gè)文件系統(tǒng)。上文也提到,掛載時(shí)讀寫(xiě)(read-write)文件系統(tǒng)中空無(wú)一物。既然如此,從用戶視角來(lái)看,容器內(nèi)文件系統(tǒng)和 rootfs 完全一樣,用戶完全可以按照往常習(xí)慣,無(wú)差別的使用自身視角下文件系統(tǒng)中的所有內(nèi)容;然而,從內(nèi)核的角度來(lái)看,兩者在有著非常大的區(qū)別。追溯區(qū)別存在的根本原因,那就不得不提及 AUFS 等文件系統(tǒng)的 COW(copy-on-write)特性。
COW 文件系統(tǒng)和其他文件系統(tǒng)最大的區(qū)別就是:從不覆寫(xiě)已有文件系統(tǒng)中已有的內(nèi)容。由于通過(guò) COW 文件系統(tǒng)將兩個(gè)文件系統(tǒng)(rootfs 和 read-write filesystem)合并,最終用戶視角為合并后的含有所有內(nèi)容的文件系統(tǒng),然而在 Linux 內(nèi)核邏輯上依然可以區(qū)別兩者,那就是用戶對(duì)原先 rootfs 中的內(nèi)容擁有只讀權(quán)限,而對(duì) read-write filesystem 中的內(nèi)容擁有讀寫(xiě)權(quán)限。
既然對(duì)用戶而言,全然不知哪些內(nèi)容只讀,哪些內(nèi)容可讀寫(xiě),這些信息只有內(nèi)核在接管,那么假設(shè)用戶需要更新其視角下的文件 /etc/hosts,而該文件又恰巧是 rootfs 只讀文件系統(tǒng)中的內(nèi)容,內(nèi)核是否會(huì)拋出異?;蛘唏g回用戶請(qǐng)求呢?答案是否定的。當(dāng)此情形發(fā)生時(shí),COW 文件系統(tǒng)首先不會(huì)覆寫(xiě) read-only 文件系統(tǒng)中的文件,即不會(huì)覆寫(xiě) rootfs 中 /etc/hosts,其次反而會(huì)將該文件拷貝至讀寫(xiě)文件系統(tǒng)中,即拷貝至讀寫(xiě)文件系統(tǒng)中的 /etc/hosts,最后再對(duì)后者進(jìn)行更新操作。如此一來(lái),縱使 rootfs 與 read-write filesystem 中均由 /etc/hosts,諸如 AUFS 類型的 COW 文件系統(tǒng)也能保證用戶視角中只能看到 read-write filesystem 中的 /etc/hosts,即更新后的內(nèi)容。
當(dāng)然,這樣的特性同樣支持 rootfs 中文件的刪除等其他操作。例如:用戶通過(guò) apt-get 軟件包管理工具安裝 Golang,所有與 Golang 相關(guān)的內(nèi)容都會(huì)被安裝在讀寫(xiě)文件系統(tǒng)中,而不會(huì)安裝在 rootfs。此時(shí)用戶又希望通過(guò) apt-get 軟件包管理工具刪除所有關(guān)于 MySQL 的內(nèi)容,恰巧這部分內(nèi)容又都存在于 rootfs 中時(shí),刪除操作執(zhí)行時(shí)同樣不會(huì)刪除 rootfs 實(shí)際存在的 MySQL,而是在 read-write filesystem 中刪除該部分內(nèi)容,導(dǎo)致最終 rootfs 中的 MySQL 對(duì)容器用戶不可見(jiàn),也不可訪。
掌握 Docker 中 rootfs 以及 Union mount 的概念之后,再來(lái)理解 Docker 鏡像,就會(huì)變得水到渠成。
image
Docker 中 rootfs 的概念,起到容器文件系統(tǒng)中基石的作用。對(duì)于容器而言,其只讀的特性,也是不難理解。神奇的是,實(shí)際情況下 Docker 的 rootfs 設(shè)計(jì)與實(shí)現(xiàn)比上文的描述還要精妙不少。
繼續(xù)以 ubuntu 為例,雖然通過(guò) AUFS 可以實(shí)現(xiàn) rootfs 與 read-write filesystem 的合并,但是考慮到 rootfs 自身接近 200MB 的磁盤(pán)大小,如果以這個(gè) rootfs 的粒度來(lái)實(shí)現(xiàn)容器的創(chuàng)建與遷移等,是否會(huì)稍顯笨重,同時(shí)也會(huì)大大降低鏡像的靈活性。而且,若用戶希望擁有一個(gè) ubuntu 的 rootfs,那么是否有必要?jiǎng)?chuàng)建一個(gè)全新的 rootfs,畢竟 ubuntu 和 ubuntu 的 rootfs 中有很多一致的內(nèi)容。
Docker 中 image 的概念,非常巧妙的解決了以上的問(wèn)題。最為簡(jiǎn)單的解釋 image,就是 Docker 容器中只讀文件系統(tǒng) rootfs 的一部分。換言之,實(shí)際上 Docker 容器的 rootfs 可以由多個(gè) image 來(lái)構(gòu)成。多個(gè) image 構(gòu)成 rootfs 的方式依然沿用 Union mount 技術(shù)。
多個(gè) Image 構(gòu)成 rootfs 的示意圖。圖中,rootfs 中每一層 image 中的內(nèi)容劃分只為了闡述清楚 rootfs 由多個(gè) image 構(gòu)成,并不代表實(shí)際情況中 rootfs 中的內(nèi)容劃分。
從上圖可以看出,舉例的容器 rootfs 包含 4 個(gè) image,其中每個(gè) image 中都有一些用戶視角文件系統(tǒng)中的一部分內(nèi)容。4 個(gè) image 處于層疊的關(guān)系,除了最底層的 image,每一層的 image 都疊加在另一個(gè) image 之上。另外,每一個(gè) image 均含有一個(gè) image ID,用以唯一的標(biāo)記該 image。
基于以上的概念,Docker Image 中又抽象出兩種概念:Parent Image 以及 Base Image。除了容器 rootfs 最底層的 image,其余 image 都依賴于其底下的一個(gè)或多個(gè) image,而 Docker 中將下一層的 image 稱為上一層 image 的 Parent Image。imageID_0 是 imageID_1 的 Parent Image,imageID_2 是 imageID_3 的 Parent Image,而 imageID_0 沒(méi)有 Parent Image。對(duì)于最下層的 image,即沒(méi)有 Parent Image 的鏡像,在 Docker 中習(xí)慣稱之為 Base Image。
通過(guò) image 的形式,原先較為臃腫的 rootfs 被逐漸打散成輕便的多層。Image 除了輕便的特性,同時(shí)還有上文提到的只讀特性,如此一來(lái),在不同的容器、不同的 rootfs 中 image 完全可以用來(lái)復(fù)用。
多 image 組織關(guān)系與復(fù)用關(guān)系如圖下圖(圖中鏡像名稱的舉例只為將 image 之間的關(guān)系闡述清楚,并不代表實(shí)際情況中相應(yīng)名稱 image 之間的關(guān)系):
多 image 組織關(guān)系示意圖
圖中共羅列了 11 個(gè) image,這 11 個(gè) image 之間的關(guān)系呈現(xiàn)一副森林圖。森林中含有兩棵樹(shù),左邊樹(shù)中包含 5 個(gè)節(jié)點(diǎn),即含有 5 個(gè) image;右邊樹(shù)中包含 6 個(gè)節(jié)點(diǎn),即含有 6 個(gè) image。圖中,有些 image 標(biāo)記了紅色字段,意味該 image 代表某一種容器鏡像 rootfs 的最上層 image。如圖中的 ubuntu:14.04,代表 imageID_3 為該類型容器 rootfs 的最上層,沿著該節(jié)點(diǎn)找到樹(shù)的根節(jié)點(diǎn),可以發(fā)現(xiàn)路徑上還有 imageID_2,imageID_1 和 imageID_0。特殊的是,imageID_2 作為 imageID_3 的 Parent Image,同時(shí)又是容器鏡像 ubuntu:12.04 的 rootfs 中的最上層,可見(jiàn)鏡像 ubuntu:14.04 只是在鏡像 ubuntu:12.04 之上,再另行疊加了一層。因此,在下載鏡像 ubuntu:12.04 以及 ubuntu:14.04 時(shí),只會(huì)下載一份 imageID_2、imageID_1 和 imageID_0,實(shí)現(xiàn) image 的復(fù)用。同時(shí),右邊樹(shù)中 mysql:5.6、mongo:2.2、debian:wheezy 和 debian:jessie 也呈現(xiàn)同樣的關(guān)系。
layer
Docker 術(shù)語(yǔ)中,layer 是一個(gè)與 image 含義較為相近的詞。容器鏡像的 rootfs 是容器只讀的文件系統(tǒng),rootfs 又是由多個(gè)只讀的 image 構(gòu)成。于是,rootfs 中每個(gè)只讀的 image 都可以稱為一層 layer。
除了只讀的 image 之外,Docker Daemon 在創(chuàng)建容器時(shí)會(huì)在容器的 rootfs 之上,再 mount 一層 read-write filesystem,而這一層文件系統(tǒng),也稱為容器的一層 layer,常被稱為 top layer。
因此,總結(jié)而言,Docker 容器中的每一層只讀的 image,以及最上層可讀寫(xiě)的文件系統(tǒng),均被稱為 layer。如此一來(lái),layer 的范疇比 image 多了一層,即多包含了最上層的 read-write filesystem。
有了 layer 的概念,大家可以思考這樣一個(gè)問(wèn)題:容器文件系統(tǒng)分為只讀的 rootfs,以及可讀寫(xiě)的 top layer,那么容器運(yùn)行時(shí)若在 top layer 中寫(xiě)入了內(nèi)容,那這些內(nèi)容是否可以持久化,并且也被其它容器復(fù)用?
上文對(duì)于 image 的分析中,提到了 image 有復(fù)用的特性,既然如此,再提一個(gè)更為大膽的假設(shè):容器的 top layer 是否可以轉(zhuǎn)變?yōu)?image?
答案是肯定的。Docker 的設(shè)計(jì)理念中,top layer 轉(zhuǎn)變?yōu)?image 的行為(Docker 中稱為 commit 操作),大大釋放了容器 rootfs 的靈活性。Docker 的開(kāi)發(fā)者完全可以基于某個(gè)鏡像創(chuàng)建容器做開(kāi)發(fā)工作,并且無(wú)論在開(kāi)發(fā)周期的哪個(gè)時(shí)間點(diǎn),都可以對(duì)容器進(jìn)行 commit,將所有 top layer 中的內(nèi)容打包為一個(gè) image,構(gòu)成一個(gè)新的鏡像。Commit 完畢之后,用戶完全可以基于新的鏡像,進(jìn)行開(kāi)發(fā)、分發(fā)、測(cè)試、部署等。不僅 docker commit 的原理如此,基于 Dockerfile 的 docker build,其追核心的思想,也是不斷將容器的 top layer 轉(zhuǎn)化為 image。
爽,酣暢淋漓啊這波讀下來(lái)!??!
Docker 鏡像下載
Docker Image 作為 Docker 生態(tài)中的精髓,下載過(guò)程中需要 Docker 架構(gòu)中多個(gè)組件的協(xié)作。Docker 鏡像的下載流程如圖:
1、docker client發(fā)送鏡像的tag到registry。
2、registry根據(jù)鏡像tag,得到鏡像的manifest文件,返回給docker client。
3、docker client拿到manifest文件后,根據(jù)其中的config的digest,也就是image ID,檢查下鏡像在本地是否存在。
4、如果鏡像不存在,則下載config文件,并根據(jù)config文件中的diff_ids得到鏡像每一層解壓后的digest。
5、然后根據(jù)每層解壓后的digest文件,檢查本地是否存在,如果不存在,則通過(guò)manifest文件中的6、layer的digest下載該層并解壓,然后校驗(yàn)解壓后digest是否匹配。
7、下載完所有層后,鏡像就下載完畢。
鏡像存儲(chǔ)
Docker Daemon 執(zhí)行鏡像下載任務(wù)時(shí),從 Docker Registry 處下載指定鏡像之后,仍需要將鏡像合理地存儲(chǔ)于宿主機(jī)的文件系統(tǒng)中。更為具體而言,存儲(chǔ)工作分為兩個(gè)部分:
(1) 存儲(chǔ)鏡像內(nèi)容;
(2) 在 graph 中注冊(cè)鏡像信息。
說(shuō)到鏡像內(nèi)容,需要強(qiáng)調(diào)的是,每一層 layer 的 Docker Image 內(nèi)容都可以認(rèn)為有兩個(gè)部分組成:鏡像中每一層 layer 中存儲(chǔ)的文件系統(tǒng)內(nèi)容,這部分內(nèi)容一般可以認(rèn)為是未來(lái) Docker 容器的靜態(tài)文件內(nèi)容;另一部分內(nèi)容指的是容器的 json 文件,json 文件代表的信息除了容器的基本屬性信息之外,還包括未來(lái)容器運(yùn)行時(shí)的動(dòng)態(tài)信息,包括 ENV 等信息。
存儲(chǔ)鏡像內(nèi)容,意味著 Docker Daemon 所在宿主機(jī)上已經(jīng)存在鏡像的所有內(nèi)容,除此之外,Docker Daemon 仍需要對(duì)所存儲(chǔ)的鏡像進(jìn)行統(tǒng)計(jì)備案,以便用戶在后續(xù)的鏡像管理與使用過(guò)程中,可以有據(jù)可循。為此,Docker Daemon 設(shè)計(jì)了 graph,使用 graph 來(lái)接管這部分的工作。graph 負(fù)責(zé)記錄有哪些鏡像已經(jīng)被正確存儲(chǔ),供 Docker Daemon 調(diào)用。
鏡像在遠(yuǎn)端倉(cāng)庫(kù)存儲(chǔ)
在本地起一個(gè)registry服務(wù),然后推送三個(gè)鏡像到鏡像倉(cāng)庫(kù)??梢缘玫絩egistry中的文件內(nèi)容如下所示。registry中包含三個(gè)鏡像: xxx/library/debian:latest,xxx/repo:tag和xxx/busybox:v1
└── registry └── v2 ├── blobs │ └── sha256 │ ├── 0d │ │ └── 0d96da54f60b86a4d869d44b44cfca69d71c4776b81d361bc057d6666ec0d878 │ │ └── data │ ├── 34 │ │ └── 34efe68cca33507682b1673c851700ec66839ecf94d19b928176e20d20e02413 │ │ └── data │ ... └── repositories ├── busybox │ ├── _layers │ │ └── sha256 │ │ ├── 7138284460ffa3bb6ee087344f5b051468b3f8697e2d1427bac1a20c8d168b14 │ │ │ └── link │ │ └── e685c5c858e36338a47c627763b50dfe6035b547f1f75f0d39753db71e319016 │ │ └── link │ ├── _manifests │ │ ├── revisions │ │ │ └── sha256 │ │ │ └── 34efe68cca33507682b1673c851700ec66839ecf94d19b928176e20d20e02413 │ │ │ └── link │ │ └── tags │ │ └── v1 │ │ ├── current │ │ │ └── link │ │ └── index │ │ └── sha256 │ │ └── 34efe68cca33507682b1673c851700ec66839ecf94d19b928176e20d20e02413 │ │ └── link │ └── _uploads ├── library │ └── debian │ ├── _layers │ │ └── sha256 │ │ ├── 41c22baa66ecf728c1ea0c5405ebe72c5b2606ef66b4565a209e23e1ab05fe80 │ │ │ └── link │ │ ├── 67283bbdd4a0dd32f555b4279fd546b3c69251342f0c6715b075cc72049d28a1 │ │ │ └── link │ │ ... │ ├── _manifests │ │ ├── revisions │ │ │ └── sha256 │ │ │ └── 57c1e4ff150e2782a25c8cebb80b574f81f06b74944caf972f27e21b76074194 │ │ │ └── link │ │ └── tags │ │ └── latest │ │ ├── current │ │ │ └── link │ │ └── index │ │ └── sha256 │ │ └── 57c1e4ff150e2782a25c8cebb80b574f81f06b74944caf972f27e21b76074194 │ │ └── link │ └── _uploads └── repo ├── _layers │ └── sha256 │ ├── 0d96da54f60b86a4d869d44b44cfca69d71c4776b81d361bc057d6666ec0d878 │ │ └── link │ ├── 3790aef225b922bc97aaba099fe762f7b115aec55a0083824b548a6a1e610719 │ │ └── link │ ... ├── _manifests │ ├── revisions │ │ └── sha256 │ │ └── 36cb5b157911061fb610d8884dc09e0b0300a767a350563cbfd88b4b85324ce4 │ │ └── link │ └── tags │ └── tag │ ├── current │ │ └── link │ └── index │ └── sha256 │ └── 36cb5b157911061fb610d8884dc09e0b0300a767a350563cbfd88b4b85324ce4 │ └── link └── _uploads
對(duì)上面的文件結(jié)構(gòu)進(jìn)行整理,可以得到如下圖所示的結(jié)構(gòu):
registry有兩個(gè)目錄,分別為blobs和repositories,其中blobs保存的是鏡像的manifest文件、config文件和layer文件內(nèi)容,文件名字均為data,每個(gè)文件可能是manifest、config、layer中的一種。repositories保存的是鏡像的repo、tag、layer摘要等信息。其中的_manifests文件夾下包含著鏡像的 tags 和 revisions 信息,每一個(gè)鏡像的每一個(gè) tag 對(duì)應(yīng) tag 名相同的目錄。每個(gè) tag名目錄下面有 current 目錄和 index 目錄, current 目錄下的 link 文件保存了該 tag 目前的 manifest 文件的 sha256 編碼,對(duì)應(yīng)在 blobs 中的 sha256 目錄下的 data 文件,而 index 目錄則列出了該 tag 歷史上傳的所有版本的 sha256 編碼信息。_revisions 目錄里存放了該 repository 歷史上上傳版本的所有 sha256 編碼信息。
本地鏡像存儲(chǔ)
鏡像在本地存儲(chǔ)目錄為/var/lib/docker/image/overlay2,查看下面的文件結(jié)構(gòu):
tree -L 4 /var/lib/docker/image/overlay2/ /var/lib/docker/image/overlay2/ ├── distribution │ ├── diffid-by-digest │ │ └── sha256 │ │ ├── 0240c3db9dedbfe40ec02d465375aa5b059bf8ac78dc249d1f1c91b9429fce44 │ │ ├── 41c22baa66ecf728c1ea0c5405ebe72c5b2606ef66b4565a209e23e1ab05fe80 │ │ ├── 4cdd12619cf5ed0ae43b41cd51f26fbdbd1f5ded860e4188822ec29158218263 │ │ ├── ... │ └── v2metadata-by-diffid │ └── sha256 │ ├── 00188c48b6d80656e2344142a77bccf6927123e7492baf43df68e280b2baf7f2 │ ├── 04fefa2a1a8fefaafde3b966f11d547e3bbaa2bb36bf90c58e33c1d305052fa9 │ ├── ... ├── imagedb │ ├── content │ │ └── sha256 │ │ ├── 7138284460ffa3bb6ee087344f5b051468b3f8697e2d1427bac1a20c8d168b14 │ │ ├── ... │ └── metadata │ └── sha256 │ ├── b8604a3fe8543c9e6afc29550de05b36cd162a97aa9b2833864ea8a5be11f3e2 │ └── dabbfbe0c57b6e5cd4bc089818d3f664acfad496dc741c9a501e72d15e803b34 ├── layerdb │ ├── mounts │ │ ├── 2d534be7517fb3efd9c14248eefdb4781924095fe304f5aa0c848f2e76c6bf08 │ │ │ ├── init-id │ │ │ ├── mount-id │ │ │ └── parent │ │ ├──... │ ├── sha256 │ │ ├── 0e16a5a61bcb4e6b2bb2d746c2d6789d6c0b66198208b831f74b52198d744189 │ │ │ ├── cache-id │ │ │ ├── diff │ │ │ ├── parent │ │ │ ├── size │ │ │ └── tar-split.json.gz │ │ ├── 0ee0aa554b8be64c963aaaf162df152784d868d21a7414146cb819a93e4bdb9e │ │ │ ├── cache-id │ │ │ ├── diff │ │ │ ├── parent │ │ │ ├── size │ │ │ └── tar-split.json.gz │ │ ├── ... │ └── tmp └── repositories.json
對(duì)上面的文件結(jié)構(gòu)進(jìn)行整理,可以得到如下圖所示的結(jié)構(gòu):
到此這篇關(guān)于Docker 進(jìn)階之鏡像分層詳解的文章就介紹到這了,更多相關(guān)Docker鏡像分層內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
聊聊docker?單機(jī)部署redis集群的問(wèn)題
這篇文章主要介紹了docker?單機(jī)部署redis集群,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03docker服務(wù)關(guān)停的實(shí)現(xiàn)步驟
在我看來(lái)所謂的優(yōu)雅關(guān)閉,就是在系統(tǒng)關(guān)閉時(shí),預(yù)留一些時(shí)間,讓你有機(jī)會(huì)來(lái)善后一些事情,本文就來(lái)介紹一下docker服務(wù)如何正確關(guān)停,感興趣的可以了解一下2023-10-10Docker安裝RabbitMQ AMQP協(xié)議及重要角色
這篇文章主要為大家介紹了Docker安裝RabbitMQ AMQP協(xié)議和主要角色詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05Docker中關(guān)于Namespace隔離機(jī)制全面解析
為了更好地理解容器的運(yùn)行原理,本篇文章將會(huì)以?Linux?宿主機(jī)為例,介紹容器的底層技術(shù),包括容器的命名空間、控制組、聯(lián)合文件系統(tǒng)等,需要的朋友可以參考下2022-06-06使用Jenkins+docker打包部署后端服務(wù)的實(shí)現(xiàn)
本文主要介紹了使用Jenkins+docker打包部署后端服務(wù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08使用Docker配置redis sentinel哨兵的方法步驟
本文主要介紹了Docker配置redis sentinel哨兵的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07