深刻理解Docker鏡像大小
都說容器大法好,可是假設沒有Docker鏡像,Docker該是多無趣啊。
是否還記得第一個接觸Docker的時候,你從Docker Hub下拉的那個鏡像呢?在那個處女鏡像的基礎上。你執行了容器生涯的處女容器。鏡像的基石作用已經非常明顯。在Docker的世界裏,能夠說是:No Image,No Container。
再進一步思考Docker鏡像,大家可能非常快就會聯想到下面幾類鏡像:
1.系統級鏡像:如Ubuntu鏡像。CentOS鏡像以及Debian容器等;
2.工具棧鏡像:如Golang鏡像。Flask鏡像,Tomcat鏡像等;
3.服務級鏡像:如MySQL鏡像,MongoDB鏡像。RabbitMQ鏡像等;
4.應用級鏡像:如WordPress鏡像,DockerRegistry鏡像等。
鏡像林林總總。想要執行Docker容器。必須要有Docker鏡像;想要有Docker鏡像,必須要先下載Docker鏡像。既然涉及到下載Docker鏡像,自然會存在Docker鏡像存儲。談到Docker鏡像存儲,那我們首先來聊聊Docker鏡像大小方面的知識。
下面將從三個角度來分析Docker鏡像的大小問題:Dockerfile與鏡像
、聯合文件系統
以及鏡像共享關系
。
Dockerfile與鏡像
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鏡像中。有不少層大小都為0。
那些鏡像層大小不為0
的情況,歸根結底的原因是:構建Docker鏡像時,對當前的文件系統造成了改動更新。
而改動更新的情況主要有兩種:
1.ADD或COPY命令:ADD或者COPY的作用是在docker build構建鏡像時向容器中加入內容。僅僅要內容加入成功,當前構建的那層鏡像就是加入內容的大小,如以上命令ADD run.sh /
。新構建的那層鏡像大小為文件run.sh的大小。
2.RUN命令:RUN命令的作用是在當前空的鏡像層內執行一條命令,倘若執行的命令須要更新磁盤文件。那麽全部的更新內容都在存儲在當前鏡像層中。
舉例說明:RUN echo DaoCloud
命令不涉及文件系統內容的改動,故命令執行完之後當前鏡像層的大小為0;RUN wget http://abc.com/def.tar
命令會將壓縮包下載至當前文件夾下,因此當前這一層鏡像的大小為:對文件系統內容的增量改動部分,即def.tar文件的大小。
聯合文件系統
Dockerfile中命令與鏡像層一一相應。那麽是否意味著docker build完成之後。鏡像的總大小=每一層鏡像的大小總和呢?答案是肯定的。
依舊以上圖為例:假設ubuntu:14.04鏡像的大小為200MB,而run.sh的大小為5MB,那麽以上三層鏡像從上到下,每層大小依次為0、0以及5MB,那麽終於構建出的鏡像大小的確為0+0+5+200=205MB。
盡管終於鏡像的大小是每層鏡像的累加,可是須要額外註意的是:Docker鏡像的大小並不等於
容器中文件系統內容的大小(不包含掛載文件,/proc、/sys等虛擬文件)。個中緣由,就和聯合文件系統有非常大的關系了。
首先來看一下這個簡單的Dockerfile樣例(假如在Dockerfile當前文件夾下有一個100MB的壓縮文件compressed.tar):
FROM ubuntu:14.04
ADD compressed.tar /
RUN rm /compressed.tar
ADD compressed.tar /
1.FROM ubuntu:鏡像ubuntu:14.04的大小為200MB;
2.ADD compressed.tar /: compressed.tar文件為100MB,因此當前鏡像層的大小為100MB,鏡像總大小為300MB。
3.RUN rm /compressed.tar:刪除文件compressed.tar,此時的刪除並不會刪除下一層的compressed.tar文件。僅僅會在當前層產生一個compressed.tar的刪除標記,確保通過該層將看不到compressed.tar,因此當前鏡像層的大小也為0。鏡像總大小為300MB。
4.ADD compressed.tar /:compressed.tar文件為100MB,因此當前鏡像層的大小為300MB+100MB。鏡像總大小為400MB。
分析完成之後,我們發現鏡像的總大小為400MB。可是假設執行該鏡像的話,我們非常快能夠發如今容器根文件夾下執行du -sh
之後。顯示的數值並不是400MB,而是300MB左右。基本的原因還是:聯合文件系統的性質保證了兩個擁有compressed.tar
文件的鏡像層,僅僅會容器看到一個。同一時候這也說明了一個現狀,當用戶基於一個非常大,甚至好幾個GB的鏡像執行容器時。在容器內部查看根文件夾大小,發現居然僅僅有500MB不到,設置更小。
分析至此,有一點大家須要非常註意:鏡像大小和容器大小有著本質的差別。
鏡像共享關系
Docker鏡像說大不大,說小不小。可是一旦鏡像的總數上來之後,豈不是對本地磁盤造成非常大的存儲壓力?平均每一個鏡像500MB,豈不是100個鏡像就須要準備50GB的存儲空間?
結果往往不是我們想象的那樣,Docker在鏡像復用方面設計得非常出色,大大節省鏡像占用的磁盤空間。
Docker鏡像的復用主要體如今:多個不同的Docker鏡像能夠共享同樣的鏡像層。
假設本地鏡像存儲中僅僅有一個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的同一時候,往往有三部分內容是分不開的,那就是Dockerfile。Docker鏡像與Docker容器,分析Docker鏡像大小也是如此。Docker鏡像的大小,貌似平淡無奇,卻是優化鏡像,容器磁盤限額必須要涉及的內容。
本系列將通過下面多篇文章來分析Docker鏡像:
1.深刻理解 Docker 鏡像大小
2.事實上 docker commit 非常easy
3.不得不說的 docker save 與 docker export 差別
4.為什麽有些容器文件動不得
5.打破 MNT Namespace 的容器 VOLUME
歡迎關註[Docker源代碼分析]微信公眾號。很多其它精彩即將呈現。
深刻理解Docker鏡像大小