1. 程式人生 > >深刻理解Docker映象大小

深刻理解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原始碼分析]微信公眾號,更多精彩即將呈現。