從總體的角度來說,一個完整的Docker鏡像能夠支撐一個Docker容器的運行,在 Docker容器運行過程當中主要提供文件系統視角。例如一個ubuntu:14.04的鏡像,提供了一個基本的ubuntu:14.04的發行版,固然此 鏡像是不包含操做系統Linux內核的。 mysql
說到此,可能就須要注意一下,linux內核和ubuntu:14.04Docker鏡像的區別了。傳統虛擬機安裝ubuntu:14.04會包含兩部分,第一,某一個Linux內核的發行版本,好比Linux 3.8版本的內核;第二,第一個特定的Ubuntu發行版,這部份內容不包含Linux內核,可是包含Linux以外的軟件管理方式,軟件驅動,如 apt-get軟件管理包等。 linux
理解以上內容以後,就能夠理解,爲何在一個Linux內核版本爲3.8的ubuntu:14.04基礎上,能夠把Linux內核版本升級到3.18,而ubuntu的版本依然是14.04。最主要的就是:Linux內核版本與ubuntu操做系統發行版之間的區別。 git
Linux內核+ubuntu操做系統發行版,組成一臺工做的機器讓用戶體驗。那麼靈活替換ubuntu操做系統發行版,那是否是也能夠實現呢。那麼Docker很方便的利用了這一點,技術手段就是Docker鏡像。 golang
Docker的架構中,Docker鏡像就是相似於「ubuntu操做系統發行版」,可 以在任何知足要求的Linux內核之上運行。簡單一點有「Debian操做系統發行版」Docker鏡像、「Ubuntu操做系統發行版」Docker鏡 像;若是在Debian鏡像中安裝MySQL 5.6,那咱們能夠將其命名爲Mysql:5.6鏡像;若是在Debian鏡像中安裝有Golang 1.3,那咱們能夠將其命名爲golang:1.3鏡像;以此類推,你們能夠根據本身安裝的軟件,獲得任何本身想要的鏡像。 sql
那麼鏡像最後的做用是什麼呢?很好理解,回到Linux內核上來運行,經過鏡像來運行時咱們經常將提供的環境稱爲容器。 docker
以上內容是從宏觀的角度看看Docker鏡像是什麼,咱們再從微觀的角度進一步深刻 Docker鏡像。剛纔提到了「Debian鏡像中安裝MySQL 5.6,就成了mysql:5.6鏡像」,其實在此時Docker鏡像的層級概念就體現出來了。底層一個Debian操做系統鏡像,上面疊加一個 mysql層,就完成了一個mysql鏡像的構建。層級概念就不難理解,此時咱們通常debian操做系統鏡像稱爲mysql鏡像層的父鏡像。 json
層級管理的方式大大便捷了Docker鏡像的分發與存儲。說到分發,你們天然會聯想到 Docker鏡像的靈活性,傳輸的便捷性,以及高超的移植性。Docker Hub,做爲全球的鏡像倉庫,做爲Docker生態中的數據倉庫,將全世界的Docker數據匯聚在一塊兒,是Docker生態的命脈。 ubuntu
Docker有兩方面的技術很是重要,第一是Linux 容器方面的技術,第二是Docker鏡像的技術。從技術自己來說,二者的可複製性很強,不存在絕對的技術難點,然而Docker Hub因爲存在大量的數據的緣由,致使Docker Hub的可複製性幾乎不存在,這須要一個生態的營造。 安全
大體介紹了Docker鏡像是什麼,咱們來看看Docker鏡像中有哪些內容? 網絡
介紹以前,我先分享一下,我我的在接觸Docker的兩年時間中,對Docker鏡像內容認識的變化。
第一階段:初步接觸Docker。相信不少愛好者都會和我同樣,有這樣一個認識:Docker 鏡像表明一個容器的文件系統內容;
第二階段:初步接觸聯合文件系統。聯合文件系統的概念,讓我意識到鏡像層級管理的技術,每一層鏡像都是容器文件系統內容的一部分。
第三階段:研究鏡像與容器的關係:容器是一個動態的環境,每一層鏡像中的文件屬於靜態內 容,然而 Dockerfile 中的 ENV、VOLUME、CMD 等內容最終都須要落實到容器的運行環境中,而這些內容均不可能直接坐落到每一層鏡像所包含的文件系統內容中,那此時每個Docker鏡像還會包含 json文件記錄與容器之間的關係。
所以,Docker鏡像的內容主要包含兩個部分:第一,鏡像層文件內容;第二,鏡像json文件。
既然是說鏡像存儲的位置,那麼應該包含:鏡像層文件和鏡像json文件。如一個ubuntu:14.04鏡像,包含4個鏡像層,在aufs存儲驅動的狀況下,在磁盤上的狀況能夠如如下圖所示:
咱們能夠經過命令 docker history ubuntu:14.04 查看 ubuntu:14.04,結果以下:
Docker 鏡像層的內容通常在 Docker 根目錄的 aufs 路徑下,爲 /var/lib/docker/aufs/diff/,具體狀況以下:
圖中顯示了鏡像 ubuntu:14.04 的 4 個鏡像層內容,以及每一個鏡像層內的一級目錄狀況。須要額外注意的是:鏡像層 d2a0ecffe6fa 中沒有任何內容,也就是所謂的空鏡像。
對於每個鏡像層,Docker 都會保存一份相應的 json 文件,json 文件的存儲路徑爲 /var/lib/docker/graph,ubuntu:14.04 全部鏡像層的 json 文件存儲路徑展現以下:
除了 json 文件,你們還看到每個鏡像層還包含一個 layersize 文件,該文件主要記錄鏡像層內部文件內容的總大小。既然談到了鏡像 json 文件,爲了給下文鋪墊,如下貼出 ubuntu:14.04 中空鏡像層 d2a0ecffe6fa 的 json 文件:
Docker鏡像存儲,就和你們一塊兒先看到這。同時介紹Docker鏡像的基本知識也告一段落。如下咱們進入這次分享的第二部分。
Dockerfile 是軟件的原材料,Docker 鏡像是軟件的交付品,而 Docker 容器則能夠認爲是軟件的運行態。從應用軟件的角度來看,Dockerfile、Docker 鏡像與 Docker 容器分別表明軟件的三個不一樣階段,Dockerfile 面向開發,Docker 鏡像成爲交付標準,Docker 容器則涉及部署與運維,三者缺一不可,協力充當 Docker 體系的基石。
簡單來說,Dockerfile構建出Docker鏡像,經過Docker鏡像運行Docker容器。
咱們能夠從Docker容器的角度,來反推三者的關係。首先能夠來看下圖:
咱們假設這個容器的鏡像經過如下Dockerfile構建而得:
FROM ubuntu:14.04 ADD run.sh / VOLUME /data CMD ["./run.sh"]
首先,咱們結合上圖來看看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個鏡像層,每一條命令會和一個鏡像層對應,鏡像之間會存在父子關係。圖中很清楚的代表了這些關係。
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.部分的設備文件
問:爲何一個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會永遠留在下層,可是沒有影響。(責編/魏偉)