深刻分析 Docker 鏡像原理

 

摘要:近日, DaoCloud 軟件工程師孫宏亮在 CSDN Container 微信羣爲你們帶來了 Docker 鏡像原理的深度分享,本次分享的重點是 Docker 鏡像,分享的內容主要包含兩個部分:1)Docker 鏡像的基本知識;2)Dockerfile,Docker 鏡像與 Docker 容器的關係。html

嘉賓介紹:孫宏亮,碩士,浙江大學畢業,現爲 DaoCloud 軟件工程師,出版有《Docker 源碼分析》,目前主要負責企業級容器雲平臺的研發工做。數年來一直從事雲計算、PaaS 領域的研究與實踐,是國內較早一批接觸 Docker 的先行者,同時也是 Docker 技術的推廣者。mysql

第一部分:Docker 鏡像的基本知識

1.1 什麼是 Docker 鏡像linux

從總體的角度來說,一個完整的 Docker 鏡像能夠支撐一個 Docker 容器的運行,在 Docker 容器運行過程當中主要提供文件系統視角。例如一個 ubuntu:14.04 的鏡像,提供了一個基本的 ubuntu:14.04 的發行版,固然此鏡像是不包含操做系統 Linux 內核的。golang

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

理解以上內容以後,就能夠理解,爲何在一個 Linux 內核版本爲 3.8 的 ubuntu:14.04 基礎上,能夠把 Linux 內核版本升級到 3.18,而 ubuntu 的版本依然是 14.04。最主要的就是:Linux 內核版本與 ubuntu 操做系統發行版之間的區別。docker

Linux 內核+ubuntu 操做系統發行版,組成一臺工做的機器讓用戶體驗。那麼靈活替換 ubuntu 操做系統發行版,那是否是也能夠實現呢。那麼 Docker 很方便的利用了這一點,技術手段就是 Docker 鏡像。json

Docker 的架構中,Docker 鏡像就是相似於 「ubuntu 操做系統發行版」,能夠在任何知足要求的 Linux 內核之上運行。簡單一點有 「Debian 操做系統發行版」 Docker 鏡像、「Ubuntu 操做系統發行版」 Docker鏡像;若是在 Debian 鏡像中安裝 MySQL 5.6,那咱們能夠將其命名爲 Mysql:5.6 鏡像;若是在 Debian 鏡像中安裝有 Golang 1.3,那咱們能夠將其命名爲 golang:1.3 鏡像;以此類推,你們能夠根據本身安裝的軟件,獲得任何本身想要的鏡像。ubuntu

那麼鏡像最後的做用是什麼呢?很好理解,回到 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,結果以下:

allen1

1.3 Docker 鏡像存儲位置

既然是說鏡像存儲的位置,那麼應該包含:鏡像層文件和鏡像 json 文件。如一個 ubuntu:14.04 鏡像,包含 4 個鏡像層,在 aufs 存儲驅動的狀況下,在磁盤上的狀況能夠如如下圖所示:

1.3.1 查看鏡像層組成:

咱們能夠經過命令 docker history ubuntu:14.04 查看 ubuntu:14.04,結果以下:

allen1

1.3.2 鏡像層文件內容存儲

Docker 鏡像層的內容通常在 Docker 根目錄的 aufs 路徑下,爲 /var/lib/docker/aufs/diff/,具體狀況以下:

allen2

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

1.3.3 鏡像 json 文件存儲

對於每個鏡像層,Docker 都會保存一份相應的 json 文件,json 文件的存儲路徑爲 /var/lib/docker/graph,ubuntu:14.04 全部鏡像層的 json 文件存儲路徑展現以下:

allen3

除了 json 文件,你們還看到每個鏡像層還包含一個 layersize 文件,該文件主要記錄鏡像層內部文件內容的總大小。既然談到了鏡像 json 文件,爲了給下文鋪墊,如下貼出 ubuntu:14.04 中空鏡像層 d2a0ecffe6fa 的 json 文件:

allen4

Docker 鏡像存儲,就和你們一塊兒先看到這。同時介紹 Docker 鏡像的基本知識也告一段落。如下咱們進入這次分享的第二部分。

第二部分 Dockerfile、Docker 鏡像和 Docker 容器的關係

Dockerfile 是軟件的原材料,Docker 鏡像是軟件的交付品,而 Docker 容器則能夠認爲是軟件的運行態。從應用軟件的角度來看,Dockerfile、Docker 鏡像與 Docker 容器分別表明軟件的三個不一樣階段,Dockerfile 面向開發,Docker 鏡像成爲交付標準,Docker 容器則涉及部署與運維,三者缺一不可,協力充當 Docker 體系的基石。

簡單來說,Dockerfile構建出Docker鏡像,經過Docker鏡像運行Docker容器。

咱們能夠從Docker容器的角度,來反推三者的關係。首先能夠來看下圖:

allen5

咱們假設這個容器的鏡像經過如下 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 容器提供一個文件系統的視角,供容器內部的進程訪問文件資源。

再次回到上圖,咱們再來看看容器和鏡像之間的一些特殊關係。

allen5

首先,以前已經說起 Docker 鏡像是分層管理的,管理 Docker 容器的時候,Docker 鏡像仍然是分層管理的。因爲此時動態的容器中已經存在進程,進程就會對文件系統視角內的文件進行讀寫操做,所以,就會涉及一個問題:容器是否會篡改 Docker 鏡像的內容?

答案天然是不會的。統一來說,正如上圖,全部的Docker鏡像層對於容器來講,都是隻讀的,容器對於文件的寫操做絕對不會做用在鏡像中。

既然如此,實現的原理就很重要,究其根本:Docker 守護進程會在 Docker 鏡像的最上層之上,再添加一個可讀寫層,容器全部的寫操做都會做用到這一層中。而若是 Docker 容器須要寫底層 Docker 鏡像中的文件,那麼此時就會涉及一個叫 Copy-on-Write 的機制,即 aufs 等聯合文件系統保證:首先將此文件從 Docker 鏡像層中拷貝至最上層的可讀寫層,而後容器進程再對讀寫層中的副本進行寫操縱。對於容器進程來說,它只能看到最上層的文件。

那最後咱們再來講說:Docker 容器的文件系統視角中,究竟是不是存在一些內容,不是存儲於 Docker 鏡像中的?

此次的答案依舊是確定的。

再次重申一點,Docker 鏡像中存儲的都是一些靜態文件。這些文件原則上應該和容器具體信息以及主機信息徹底解藕。那麼 Docker 容器中不存在 Docker 鏡像中的內容主要有如下幾點:

  • /proc 以及 /sys 等虛擬文件系統的內容
  • 容器的 hosts 文件,hostname 文件以及 resolv.conf 文件,這些事具體環境的信息,原則上的確不該該被打入鏡像。
  • 容器的 Volume 路徑,這部分的視角來源於從宿主機上掛載到容器內部的路徑
  • 部分的設備文件

 

更多閱讀:

Docker鏡像結構原理:https://blog.51cto.com/liuleis/2070461

Docker 建立鏡像、修改、上傳鏡像:http://www.javashuo.com/article/p-dmmsomqt-ca.html

相關文章
相關標籤/搜索