其實映像檔真的很像千層派。
我們可以先透過 docker image history
這個指令來感受一下千層派是什麼概念,就先以 nginx 這個映像檔來試試看吧。
$ docker image history nginx:latest
IMAGE CREATED CREATED BY SIZE COMMENT
b692a91e4e15 4 weeks ago /bin/..... 0B
<missing> 4 weeks ago /bin/..... 0B
<missing> 4 weeks ago /bin/..... 0B
<missing> 4 weeks ago /bin/..... 0B
<missing> 4 weeks ago /bin/..... 4.61kB
<missing> 4 weeks ago /bin/..... 1.04kB
<missing> 4 weeks ago /bin/..... 1.96kB
<missing> 4 weeks ago /bin/..... 1.2kB
<missing> 4 weeks ago /bin/..... 61.1MB
<missing> 4 weeks ago /bin/..... 0B
<missing> 4 weeks ago /bin/..... 0B
<missing> 4 weeks ago /bin/..... 0B
<missing> 4 weeks ago /bin/..... 0B
<missing> 4 weeks ago CMD ["bash"] 0B
<missing> 4 weeks ago ADD file 80.4MB
可以看到每一個層級由下而上堆疊起來,到最後形程一個映像檔並且賦予 ID b692a91e4e1...
但這個歷史紀錄並不是來自容器的歷史紀錄,而是這個映像檔在建構時本身的歷史紀錄。
而在每一層都帶有不同的指令,有些是執行指令,有些是加入檔案,所以才會有 0B 的層級,也有 80.4MB 的層級。
因為指令長度的關係,上面有許多指令被我用 … 的方式隱藏起來,但在自己的終端機中可以看到除了 Dockerfile 中的指令 ADD、COPY
等等,也會看到一些 Linux 的指令,像是 addgroup --system
等等,而這些都可以說是映像檔的餡料,每一層都有它的意義存在,每一層也都會影像著下一層。
也可以嘗試著用 docker image history
的指令去看看像是 redis、postgres 等等的服務有沒有什麼有趣的細節。
映像檔快取的秘密
在映像檔中,每一層都有獨一無二的 SHA ( Secure Hash Algorithm ) 所計算出來的 ID,其實就是幫助 Docker 去辨認是不是已經有一模一樣的映像層。
舉例來說,我想要自製一個帶有 PHP 程式語言環境的映像檔,我們就使用 PHP 的映像檔當做基底,如下圖,以 PHP 做基底。
接著我們會利用 Dockerfile 在這個第一層之上增加新的映像層,像是加入環境變數來設定,或是 COPY 本機的資料到映像檔內等等,如下圖所示
接著我們又想要做另外一個也是以 PHP 為基礎的映像檔,Docker 將會利用前面提到的獨一無二的 ID 辨識出已經有相同的 PHP 映像檔在本機之中,並利用其已存在的特性加快映像檔的建置,也一併減少了整體電腦耗費的硬體,這也是整個 Docker 最基本的快取機制。
現在對於上面的指令 ENV、COPY、RUN
不熟都沒關係,要先了解到映像檔是透過一層一層的堆疊而成,而每一個動作都會形成一個映像層,這是很重要的概念,之後會反覆的利用這個概念來加快建置的速度和大小。
我們再舉一個例子,相同的一個應用程式,可能會因為部署環境的不同而分成 Staging 以及 Production 兩種版本,並 COPY
不同的設定檔案進而建置兩個不同的映像檔。
如下圖所示,雖然是不同的兩個版本,但利用辨識 SHA 密碼的手段,乍看之下需要 8 個映像層的建置,最後其實只建造了 5 個。
映像檔 History 中的 missing
現在了解了映像檔層層堆疊的概念後,回到剛剛我們查看映像檔歷史紀錄的圖片上,可以由下往上看,就像圖解映像檔概念一樣,每一個歷史紀錄都代表了一個映像層。
而我們也可以透過歷史紀錄看到每一層最後一次更動的時間是什麼時候,而最前面的 missing 其實不是錯誤訊息,也不是有什麼檔案缺失,只是這些都是屬於同一個映像檔的一部分,才用這種方式表示,雖然我自己認為這樣會造成一些誤會,但沒辦法,這是 Docker 公司的作法。
結語
今天介紹了映像檔的快取秘密,明天將會介紹映像檔的唯讀性。