深刻理解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 很簡單
3.不得不說的 docker save 與 docker export 區別
4.為什麼有些容器檔案動不得
5.打破 MNT Namespace 的容器 VOLUME
歡迎關注[Docker原始碼分析]微信公眾號,更多精彩即將呈現。