Dockerfile由多條指令構成,隨着深入研究Dockerfile與鏡像的關系,很快大家就會發現,Dockerfile中的每一條指令都會對應於Docker鏡像中的一層。
繼續以如下Dockerfile為例:
FROM ubuntu:14.04 ADD run.sh / VOLUME /data CMD ["./run.sh"]
通過docker build以上Dockerfile的時候,會在Ubuntu:14.04鏡像基礎上,添加三層獨立的鏡像,依次對應於三條不同的命令。鏡像示意圖如下:
Dockerfile中命令與鏡像層一一對應,那么是否意味着docker build完畢之后,鏡像的總大小=每一層鏡像的大小總和呢?答案是肯定的。依然以上圖為例:如果ubuntu:14.04鏡像的大小為200MB,而run.sh的大小為5MB,那么以上三層鏡像從上到下,每層大小依次為0、0以及5MB,那么最終構建出的鏡像大小的確為0+0+5+200=205MB。
雖然最終鏡像的大小是每層鏡像的累加,但是需要額外注意的是:Docker鏡像的大小並不等於容器中文件系統內容的大小(不包括掛載文件,/proc、/sys等虛擬文件)。個中緣由,就和聯合文件系統有很大的關系了。
假設本地鏡像存儲中只有一個ubuntu:14.04的鏡像,我們以兩個Dockerfile來說明鏡像復用:
FROM ubuntu:14.04 RUN apt-get update FROM ubuntu:14.04 ADD compressed.tar /
假設最終docker build構建出來的鏡像名分別為image1和image2,由於兩個Dockerfile均基於ubuntu:14.04,因此,image1和image2這兩個鏡像均復用了鏡像ubuntu:14.04。 假設RUN apt-get update修改的文件系統內容為20MB,最終本地三個鏡像的大小關系應該如下:
ubuntu:14.04: 200MB
image1:200MB(ubuntu:14.04)+20MB=220MB
image2:200MB(ubuntu:14.04)+100MB=300MB
如果僅僅是單純的累加三個鏡像的大小,那結果應該是:200+220+300=720MB,但是由於鏡像復用的存在,實際占用的磁盤空間大小是:200+20+100=320MB,足足節省了400MB的磁盤空間。在此,足以證明鏡像復用的巨大好處。
下面具體分析一下docker image 文件系統:
docker支持多種graphDriver,包括vfs、devicemapper、overlay、overlay2、aufs等等,其中最常用的就是aufs了,但隨着linux內核3.18把overlay納入其中,overlay的地位變得更重目前docker默認的存儲類型就是overlay2,docker版本是1.8,如下
docker默認的存儲目錄是/var/lib/docker,我們
cat 573040df70596555bbbbd2bb113272101b4d7c873b8eed075fcbc0a951636094 | python -mjson.tool
echo -n "sha256:a2ae92ffcd29f7ededa0320f4a4fd709a723beae9a4e681696874932db7aee2c sha256:0eb22bfb707db44a8e5ba46a21b2ac59c83dfa946228f04be511aba313bdc090" |sha256sum -
這個時候,你能看到4cbc0ad7007fe8c2dfcf2cdc82fdb04f35070f0e2a04d5fa35093977a3cc1693這個layer層的目錄了吧?依次類推,我們就能找出所有的layerID的組合。
但是上面我們也說了,/var/lib/docker/image/overlay2/layerdb存的只是元數據,那么真實的rootfs到底存在哪里呢?其中cache-id就是我們關鍵所在了。我們打印一擦cat /var/lib/docker/image/overlay2/layerdb/sha256/4cbc0ad7007fe8c2dfcf2cdc82fdb04f35070f0e2a04d5fa35093977a3cc1693/cache-id:
引申一下:
docker save:
Produces a tarred repository to the standard output stream. Contains all parent layers, and all tags + versions, or specified repo:tag
, for each argument provided.
docker load:
如果load前有相同的layer層,實際還會導入這么多嗎,例如多個jar程序,都是基於java鏡像構建。
Docker的“層”解釋了為什么Docker鏡像只在第一次下載時那么慢,而之后的鏡像都很快,並且明明每份鏡像看起來都幾百兆,但是最終機器上的硬盤缺沒有占用那么多的原因。更小的磁盤空間、更快的加載速度,讓Docker的復用性有了非常顯著的提升。
參考:
https://blog.51cto.com/u_12182612/2476386
https://blog.csdn.net/shlazww/article/details/47375009
https://www.cnblogs.com/powertoolsteam/p/14954314.html