深入分析 Docker 映象原理

CSDN發表於2015-08-23

第一部分:Docker映象的基本知識

1.1 什麼是Docker映象

深入分析 Docker 映象原理

從整體的角度來講,一個完整的Docker映象可以支撐一個Docker容器的執行,在 Docker容器執行過程中主要提供檔案系統視角。例如一個ubuntu:14.04的映象,提供了一個基本的ubuntu:14.04的發行版,當然此 映象是不包含作業系統Linux核心的。

說到此,可能就需要注意一下,linux核心和ubuntu:14.04Docker映象的區別了。傳統虛擬機器安裝ubuntu:14.04會包含兩部分,第一,某一個Linux核心的發行版本,比如Linux 3.8版本的核心;第二,第一個特定的Ubuntu發行版,這部分內容不包含Linux核心,但是包含Linux之外的軟體管理方式,軟體驅動,如 apt-get軟體管理包等。

理解以上內容之後,就可以理解,為什麼在一個Linux核心版本為3.8的ubuntu:14.04基礎上,可以把Linux核心版本升級到3.18,而ubuntu的版本依然是14.04。最主要的就是:Linux核心版本與ubuntu作業系統發行版之間的區別。

Linux核心+ubuntu作業系統發行版,組成一臺工作的機器讓使用者體驗。那麼靈活替換ubuntu作業系統發行版,那是不是也可以實現呢。那麼Docker很方便的利用了這一點,技術手段就是Docker映象。

Docker的架構中,Docker映象就是類似於“ubuntu作業系統發行版”,可 以在任何滿足要求的Linux核心之上執行。簡單一點有“Debian作業系統發行版”Docker映象、“Ubuntu作業系統發行版”Docker鏡 像;如果在Debian映象中安裝MySQL 5.6,那我們可以將其命名為Mysql:5.6映象;如果在Debian映象中安裝有Golang 1.3,那我們可以將其命名為golang:1.3映象;以此類推,大家可以根據自己安裝的軟體,得到任何自己想要的映象。

那麼映象最後的作用是什麼呢?很好理解,回到Linux核心上來執行,通過映象來執行時我們常常將提供的環境稱為容器。

以上內容是從巨集觀的角度看看Docker映象是什麼,我們再從微觀的角度進一步深入 Docker映象。剛才提到了“Debian映象中安裝MySQL 5.6,就成了mysql:5.6映象”,其實在此時Docker映象的層級概念就體現出來了。底層一個Debian作業系統映象,上面疊加一個 mysql層,就完成了一個mysql映象的構建。層級概念就不難理解,此時我們一般debian作業系統映象稱為mysql映象層的父映象。

層級管理的方式大大便捷了Docker映象的分發與儲存。說到分發,大家自然會聯想到 Docker映象的靈活性,傳輸的便捷性,以及高超的移植性。Docker Hub,作為全球的映象倉庫,作為Docker生態中的資料倉儲,將全世界的Docker資料匯聚在一起,是Docker生態的命脈。

Docker有兩方面的技術非常重要,第一是Linux 容器方面的技術,第二是Docker映象的技術。從技術本身來講,兩者的可複製性很強,不存在絕對的技術難點,然而Docker Hub由於存在大量的資料的原因,導致Docker Hub的可複製性幾乎不存在,這需要一個生態的營造。

1.2 Docker映象的內容

大致介紹了Docker映象是什麼,我們來看看Docker映象中有哪些內容?

介紹之前,我先分享一下,我個人在接觸Docker的兩年時間中,對Docker映象內容認識的變化。

第一階段:初步接觸Docker。相信很多愛好者都會和我一樣,有這樣一個認識:Docker 映象代表一個容器的檔案系統內容;

第二階段:初步接觸聯合檔案系統。聯合檔案系統的概念,讓我意識到映象層級管理的技術,每一層映象都是容器檔案系統內容的一部分。

第三階段:研究映象與容器的關係:容器是一個動態的環境,每一層映象中的檔案屬於靜態內 容,然而 Dockerfile 中的 ENV、VOLUME、CMD 等內容最終都需要落實到容器的執行環境中,而這些內容均不可能直接坐落到每一層映象所包含的檔案系統內容中,那此時每一個Docker映象還會包含 json檔案記錄與容器之間的關係。

因此,Docker映象的內容主要包含兩個部分:第一,映象層檔案內容;第二,映象json檔案。

1.3 Docker映象儲存位置

既然是說映象儲存的位置,那麼應該包含:映象層檔案和映象json檔案。如一個ubuntu:14.04映象,包含4個映象層,在aufs儲存驅動的情況下,在磁碟上的情況可以如以下圖所示:

1.3.1 檢視映象層組成:

我們可以通過命令 docker history ubuntu:14.04 檢視 ubuntu:14.04,結果如下:

深入分析 Docker 映象原理

1.3.2 映象層檔案內容儲存

Docker 映象層的內容一般在 Docker 根目錄的 aufs 路徑下,為 /var/lib/docker/aufs/diff/,具體情況如下:

深入分析 Docker 映象原理

圖中顯示了映象 ubuntu:14.04 的 4 個映象層內容,以及每個映象層內的一級目錄情況。需要額外注意的是:映象層 d2a0ecffe6fa 中沒有任何內容,也就是所謂的空映象。

1.3.3 映象 json 檔案儲存

對於每一個映象層,Docker 都會儲存一份相應的 json 檔案,json 檔案的儲存路徑為 /var/lib/docker/graph,ubuntu:14.04 所有映象層的 json 檔案儲存路徑展示如下:

深入分析 Docker 映象原理

除了 json 檔案,大家還看到每一個映象層還包含一個 layersize 檔案,該檔案主要記錄映象層內部檔案內容的總大小。既然談到了映象 json 檔案,為了給下文鋪墊,以下貼出 ubuntu:14.04 中空映象層 d2a0ecffe6fa 的 json 檔案:

深入分析 Docker 映象原理

Docker映象儲存,就和大家一起先看到這。同時介紹Docker映象的基本知識也告一段落。以下我們進入此次分享的第二部分。

第二部分 Dockerfile、Docker映象和Docker容器的關係

Dockerfile 是軟體的原材料,Docker 映象是軟體的交付品,而 Docker 容器則可以認為是軟體的執行態。從應用軟體的角度來看,Dockerfile、Docker 映象與 Docker 容器分別代表軟體的三個不同階段,Dockerfile 面向開發,Docker 映象成為交付標準,Docker 容器則涉及部署與運維,三者缺一不可,合力充當 Docker 體系的基石。

簡單來講,Dockerfile構建出Docker映象,通過Docker映象執行Docker容器。

我們可以從Docker容器的角度,來反推三者的關係。首先可以來看下圖:

深入分析 Docker 映象原理

我們假設這個容器的映象通過以下Dockerfile構建而得:

FROM ubuntu:14.04  
ADD run.sh /  
VOLUME /data  
CMD ["./run.sh"]

2.1 Dockerfile與Docker映象

首先,我們結合上圖來看看Dockerfile與Docker映象之間的關係。

FROM ubuntu:14.04:設定基礎映象,此時會使用基礎映象 ubuntu:14.04 的所有映象層,為簡單起見,圖中將其作為一個整體展示。

ADD run.sh /:將 Dockerfile 所在目錄的檔案 run.sh 加至映象的根目錄,此時新一層的映象只有一項內容,即根目錄下的 run.sh。

VOLUME /data:設定映象的 VOLUME,此 VOLUME 在容器內部的路徑為 /data。需要注意的是,此時並未在新一層的映象中新增任何檔案,即構建出的磁層映象中檔案為空,但更新了映象的 json 檔案,以便通過此映象啟動容器時獲取這方面的資訊。

CMD ["./run.sh"]:設定映象的預設執行入口,此命令同樣不會在新建映象中新增任何檔案,僅僅在上一層映象 json 檔案的基礎上更新新建映象的 json 檔案。

因此,通過以上分析,以上的Dockerfile可以構建出一個新的映象,包含4個映象層,每一條命令會和一個映象層對應,映象之間會存在父子關係。圖中很清楚的表明了這些關係。

2.2 Docker映象與Docker容器的關係

Docker映象是Docker容器執行的基礎,沒有Docker映象,就不可能有Docker容器,這也是Docker的設計原則之一。

可以理解的是:Docker映象畢竟是映象,屬於靜態的內容;而Docker容器就不一樣了,容器屬於動態的內容。動態的內容,大家很容易聯想到程式,記憶體,CPU等之類的東西。的確,Docker容器作為動態的內容,都會包含這些。

為了便於理解,大家可以把Docker容器,理解為一個或多個執行程式,而這些執行程式將佔有相應的記憶體,相應的CPU計算資源,相應的虛擬網路裝置以及相應的檔案系統資源。而Docker容器所佔用的檔案系統資源,則通過Docker映象的映象層檔案來提供。

那麼作為靜態的映象,如何才有能力轉化為一個動態的Docker容器呢?此時,我們可以想象:第一,轉化的依據是什麼;第二,由誰來執行這個轉化操作。

其實,轉化的依據是每個映象的json檔案,Docker可以通過解析Docker映象的json的檔案,獲知應該在這個映象之上執行什麼樣的程式,應該為程式配置怎麼樣的環境變數,此時也就實現了靜態向動態的轉變。

誰來執行這個轉化工作?答案是Docker守護程式。也許大家早就理解這樣一句 話:Docker容器實質上就是一個或者多個程式,而容器的父程式就是Docker守護程式。這樣的,轉化工作的執行就不難理解了:Docker守護程式 手握Docker映象的json檔案,為容器配置相應的環境,並真正執行Docker映象所指定的程式,完成Docker容器的真正建立。

Docker容器執行起來之後,Docker映象json檔案就失去作用了。此時Docker映象的絕大部分作用就是:為Docker容器提供一個檔案系統的視角,供容器內部的程式訪問檔案資源。

再次回到上圖,我們再來看看容器和映象之間的一些特殊關係。首先,之前已經提及Docker映象是分層管理的,管理Docker容器的時候,Docker映象仍然是分層管理的。由於此時動態的容器中已經存在程式,程式就會對檔案系統視角內的檔案進行讀寫操作,因此,就會涉及一個問題:容器是否會篡改Docker映象的內容?

答案自然是不會的。統一來講,正如上圖,所有的Docker映象層對於容器來說,都是隻讀的,容器對於檔案的寫操作絕對不會作用在映象中。

既然如此,實現的原理就很重要,究其根本:Docker守護程式會在Docker映象的 最上層之上,再新增一個可讀寫層,容器所有的寫操作都會作用到這一層中。而如果Docker容器需要寫底層Docker映象中的檔案,那麼此時就會涉及一 個叫Copy-on-Write的機制,即aufs等聯合檔案系統保證:首先將此檔案從Docker映象層中拷貝至最上層的可讀寫層,然後容器程式再對讀 寫層中的副本進行寫操縱。對於容器程式來講,它只能看到最上層的檔案。

那最後我們再來說說:Docker容器的檔案系統視角中,到底是不是存在一些內容,不是儲存於Docker映象中的?

這次的答案依舊是肯定的。

再次重申一點,Docker映象中儲存的都是一些靜態檔案。這些檔案原則上應該和容器具體資訊以及主機資訊完全解藕。那麼Docker容器中不存在Docker映象中的內容主要有以下幾點:

1. /proc以及/sys等虛擬檔案系統的內容

2. 容器的hosts檔案,hostname檔案以及resolv.conf檔案,這些事具體環境的資訊,原則上的確不應該被打入映象。

3. 容器的Volume路徑,這部分的視角來源於從宿主機上掛載到容器內部的路徑

4. 部分的裝置檔案

QA選集:

問:為什麼一個ubuntu:14.04映象的映象層的數量是4個,前三層的內容似乎有相同的,如etc?

孫巨集亮:這一點,細心的大家肯定發現了。首先,雖然三層 都有,但是會存在兩種情況,etc的子目錄下有相同路徑的檔案,那麼上層的會覆蓋下層的檔案;如果內部的檔案路徑不相同,那麼都會存在,都會呈現給最上 層。[可別較真,說目錄也是檔案哈,意會]

問:關於docker安全性問題,對於安全是怎樣處理的,如果我從hub下載映象,能判別是否安全麼2.層級之間的依賴會導致一個崩了整個docker 都崩了麼?

孫巨集亮:從流程上來講,如果一切可控的話,我認為是安全的。但是依然會存在一些隱患,比如Dockerfile中基於的base images是否完全受信;映象的傳輸過程是否受信;自己的private docker resgitry的安全級別達到什麼樣的層次,這些都有影響。

問:如何保證僅有的一個deamon的穩定性健壯性?

孫巨集亮:這個問題首先需要知道docker daemon的穩定性在哪些方面,那種場景下比較差?的確,docker daemon存在弊病。比如,daemon和容器的耦合等,目前general來講,docker daemon保證絕對的穩定應該還做不到。

問:生產環境中怎麼用docker備份mysql資料?

孫巨集亮:資料儲存上docker,我目前的建議是:三思。舉個簡單的例子,官方的mysql映象執行出來的 容器,密碼是明文的,明文的密碼存在於:docker inspect container_name, container.json檔案中,容器的環境變數中,甚至在日誌檔案中都會存在,just think about it。當然也有辦法解決,緩解這種情況。

問:如果是多層構建,中間的一個層做了升級或者bugfix,會潛在影響上層吧?

孫巨集亮:這個bugfix會在上層有體現,但是使用效果是不會有影響的,還有之前的bug會永遠留在下層,但是沒有影響

相關文章