1.前言回首過(guò)去的2014年,大家可以看到Docker在全球刮起了一陣又一陣的“容器風(fēng)”,工業(yè)界對(duì)Docker的探索與實(shí)踐更是一波高過(guò)一波。在如今的2015年以及未來(lái),Docker似乎并不會(huì)像其他曇花一現(xiàn)的技術(shù)一樣,在歷史的舞臺(tái)上熱潮褪去,反而在工業(yè)界實(shí)踐與評(píng)估之后,顯現(xiàn)了前所未有的發(fā)展?jié)摿Α? 究其本質(zhì),“Docker提供容器服務(wù)”這句話,相信很少有人會(huì)有異議。那么,既然Docker提供的服務(wù)屬于“容器”技術(shù),那么反觀“容器”技術(shù)的本質(zhì)與歷史,我們又可以發(fā)現(xiàn)什么呢?正如前文所提到的,Docker使用的“容器”技術(shù),主要是以Linux的cgroup、namespace等內(nèi)核特性為基礎(chǔ),保障進(jìn)程或者進(jìn)程組處于一個(gè)隔離、安全的環(huán)境。Docker發(fā)行第一個(gè)版本是在2013年的3月,而cgroup的正式亮相可以追溯到2007年下半年,當(dāng)時(shí)cgroup被合并至Linux內(nèi)核2.6.24版本。期間6年時(shí)間,并不是“容器”技術(shù)發(fā)展的真空期,2008年LXC(Linux Container)誕生,其簡(jiǎn)化了容器的創(chuàng)建與管理;之后業(yè)界一些PaaS平臺(tái)也初步嘗試采用容器技術(shù)作為其云應(yīng)用的運(yùn)行環(huán)境;而與Docker發(fā)布同年,Google也發(fā)布了開源容器管理工具lmctfy。除此之外,若拋開Linux操作系統(tǒng),其他操作系統(tǒng)如FreeBSD、Solaris等,同樣誕生了作用相類似的“容器”技術(shù),其發(fā)展歷史更是需要追溯至千禧年初期。 可見,“容器”技術(shù)的發(fā)展不可謂短暫,然而論同時(shí)代的影響力,卻鮮有Docker的媲美者。不論是云計(jì)算大潮催生了Docker技術(shù),抑或是Docker技術(shù)趕上了云計(jì)算的大時(shí)代,毋庸置疑的是,Docker作為領(lǐng)域內(nèi)的新寵兒,必然會(huì)繼續(xù)受到業(yè)界的廣泛青睞。云計(jì)算時(shí)代,分布式應(yīng)用逐漸流行,并對(duì)其自身的構(gòu)建、交付與運(yùn)行有著與傳統(tǒng)不一樣的要求。借助Linux內(nèi)核的cgroup與namespace特性,自然可以做到應(yīng)用運(yùn)行環(huán)境的資源隔離與應(yīng)用部署的快速等;然而,cgroup和namespace等內(nèi)核特性卻無(wú)法為容器的運(yùn)行環(huán)境做全盤打包。而Docker的設(shè)計(jì)則很好得考慮到了這一點(diǎn),除cgroup和namespace之外,另外采用了神奇的“鏡像”技術(shù)作為Docker管理文件系統(tǒng)以及運(yùn)行環(huán)境的強(qiáng)有力補(bǔ)充。Docker靈活的“鏡像”技術(shù),在筆者看來(lái),也是其大紅大紫最重要的因素之一。 2.Docker鏡像介紹大家看到這,第一個(gè)問(wèn)題肯定是“什么是Docker鏡像”? 據(jù)Docker官網(wǎng)的技術(shù)文檔描述,Image(鏡像)是Docker術(shù)語(yǔ)的一種,代表一個(gè)只讀的layer。而layer則具體代表Docker Container文件系統(tǒng)中可疊加的一部分。 筆者如此介紹Docker鏡像,相信眾多Docker愛好者理解起來(lái)依舊是云里霧里。那么理解之前,先讓我們來(lái)認(rèn)識(shí)一下與Docker鏡像相關(guān)的4個(gè)概念:rootfs、Union mount、image以及l(fā)ayer。 2.1 rootfsRootfs:代表一個(gè)Docker Container在啟動(dòng)時(shí)(而非運(yùn)行后)其內(nèi)部進(jìn)程可見的文件系統(tǒng)視角,或者是Docker Container的根目錄。當(dāng)然,該目錄下含有Docker Container所需要的系統(tǒng)文件、工具、容器文件等。 傳統(tǒng)來(lái)說(shuō),Linux操作系統(tǒng)內(nèi)核啟動(dòng)時(shí),內(nèi)核首先會(huì)掛載一個(gè)只讀(read-only)的rootfs,當(dāng)系統(tǒng)檢測(cè)其完整性之后,決定是否將其切換為讀寫(read-write)模式,或者最后在rootfs之上另行掛載一種文件系統(tǒng)并忽略rootfs。Docker架構(gòu)下,依然沿用Linux中rootfs的思想。當(dāng)Docker Daemon為Docker Container掛載rootfs的時(shí)候,與傳統(tǒng)Linux內(nèi)核類似,將其設(shè)定為只讀(read-only)模式。在rootfs掛載完畢之后,和Linux內(nèi)核不一樣的是,Docker Daemon沒(méi)有將Docker Container的文件系統(tǒng)設(shè)為讀寫(read-write)模式,而是利用Union mount的技術(shù),在這個(gè)只讀的rootfs之上再掛載一個(gè)讀寫(read-write)的文件系統(tǒng),掛載時(shí)該讀寫(read-write)文件系統(tǒng)內(nèi)空無(wú)一物。 舉一個(gè)Ubuntu容器啟動(dòng)的例子。假設(shè)用戶已經(jīng)通過(guò)Docker Registry下拉了Ubuntu:14.04的鏡像,并通過(guò)命令docker run –it ubuntu:14.04 /bin/bash將其啟動(dòng)運(yùn)行。則Docker Daemon為其創(chuàng)建的rootfs以及容器可讀寫的文件系統(tǒng)可參見圖2.1: 圖2.1 Ubuntu 14.04容器rootfs示意圖 正如read-only和read-write的含義那樣,該容器中的進(jìn)程對(duì)rootfs中的內(nèi)容只擁有讀權(quán)限,對(duì)于read-write讀寫文件系統(tǒng)中的內(nèi)容既擁有讀權(quán)限也擁有寫權(quán)限。通過(guò)觀察圖2.1可以發(fā)現(xiàn):容器雖然只有一個(gè)文件系統(tǒng),但該文件系統(tǒng)由“兩層”組成,分別為讀寫文件系統(tǒng)和只讀文件系統(tǒng)。這樣的理解已然有些層級(jí)(layer)的意味。 簡(jiǎn)單來(lái)講,可以將Docker Container的文件系統(tǒng)分為兩部分,而上文提到是Docker Daemon利用Union Mount的技術(shù),將兩者掛載。那么Union mount又是一種怎樣的技術(shù)? 2.2 Union mountUnion mount:代表一種文件系統(tǒng)掛載的方式,允許同一時(shí)刻多種文件系統(tǒng)掛載在一起,并以一種文件系統(tǒng)的形式,呈現(xiàn)多種文件系統(tǒng)內(nèi)容合并后的目錄。 一般情況下,通過(guò)某種文件系統(tǒng)掛載內(nèi)容至掛載點(diǎn)的話,掛載點(diǎn)目錄中原先的內(nèi)容將會(huì)被隱藏。而Union mount則不會(huì)將掛載點(diǎn)目錄中的內(nèi)容隱藏,反而是將掛載點(diǎn)目錄中的內(nèi)容和被掛載的內(nèi)容合并,并為合并后的內(nèi)容提供一個(gè)統(tǒng)一獨(dú)立的文件系統(tǒng)視角。通常來(lái)講,被合并的文件系統(tǒng)中只有一個(gè)會(huì)以讀寫(read-write)模式掛載,而其他的文件系統(tǒng)的掛載模式均為只讀(read-only)。實(shí)現(xiàn)這種Union mount技術(shù)的文件系統(tǒng)一般被稱為Union Filesystem,較為常見的有UnionFS、AUFS、OverlayFS等。 Docker實(shí)現(xiàn)容器文件系統(tǒng)Union mount時(shí),提供多種具體的文件系統(tǒng)解決方案,如Docker早版本沿用至今的的AUFS,還有在docker 1.4.0版本中開始支持的OverlayFS等。 更深入的了解Union mount,可以使用AUFS文件系統(tǒng)來(lái)進(jìn)一步闡述上文中ubuntu:14.04容器文件系統(tǒng)的例子。如圖2.2: 圖2.2 AUFS掛載Ubuntu 14.04文件系統(tǒng)示意圖 使用鏡像ubuntu:14.04創(chuàng)建的容器中,可以暫且將該容器整個(gè)rootfs當(dāng)成是一個(gè)文件系統(tǒng)。上文也提到,掛載時(shí)讀寫(read-write)文件系統(tǒng)中空無(wú)一物。既然如此,從用戶視角來(lái)看,容器內(nèi)文件系統(tǒng)和rootfs完全一樣,用戶完全可以按照往常習(xí)慣,無(wú)差別的使用自身視角下文件系統(tǒng)中的所有內(nèi)容;然而,從內(nèi)核的角度來(lái)看,兩者在有著非常大的區(qū)別。追溯區(qū)別存在的根本原因,那就不得不提及AUFS等文件系統(tǒng)的COW(copy-on-write)特性。 COW文件系統(tǒng)和其他文件系統(tǒng)最大的區(qū)別就是:從不覆寫已有文件系統(tǒng)中已有的內(nèi)容。由于通過(guò)COW文件系統(tǒng)將兩個(gè)文件系統(tǒng)(rootfs和read-write filesystem)合并,最終用戶視角為合并后的含有所有內(nèi)容的文件系統(tǒng),然而在Linux內(nèi)核邏輯上依然可以區(qū)別兩者,那就是用戶對(duì)原先rootfs中的內(nèi)容擁有只讀權(quán)限,而對(duì)read-write filesystem中的內(nèi)容擁有讀寫權(quán)限。 既然對(duì)用戶而言,全然不知哪些內(nèi)容只讀,哪些內(nèi)容可讀寫,這些信息只有內(nèi)核在接管,那么假設(shè)用戶需要更新其視角下的文件/etc/hosts,而該文件又恰巧是rootfs只讀文件系統(tǒng)中的內(nèi)容,內(nèi)核是否會(huì)拋出異常或者駁回用戶請(qǐng)求呢?答案是否定的。當(dāng)此情形發(fā)生時(shí),COW文件系統(tǒng)首先不會(huì)覆寫read-only文件系統(tǒng)中的文件,即不會(huì)覆寫rootfs中/etc/hosts,其次反而會(huì)將該文件拷貝至讀寫文件系統(tǒng)中,即拷貝至讀寫文件系統(tǒng)中的/etc/hosts,最后再對(duì)后者進(jìn)行更新操作。如此一來(lái),縱使rootfs與read-write filesystem中均由/etc/ hosts,諸如AUFS類型的COW文件系統(tǒng)也能保證用戶視角中只能看到read-write filesystem中的/etc/hosts,即更新后的內(nèi)容。 當(dāng)然,這樣的特性同樣支持rootfs中文件的刪除等其他操作。例如:用戶通過(guò)apt-get軟件包管理工具安裝Golang,所有與Golang相關(guān)的內(nèi)容都會(huì)被安裝在讀寫文件系統(tǒng)中,而不會(huì)安裝在rootfs。此時(shí)用戶又希望通過(guò)apt-get軟件包管理工具刪除所有關(guān)于MySQL的內(nèi)容,恰巧這部分內(nèi)容又都存在于rootfs中時(shí),刪除操作執(zhí)行時(shí)同樣不會(huì)刪除rootfs實(shí)際存在的MySQL,而是在read-write filesystem中刪除該部分內(nèi)容,導(dǎo)致最終rootfs中的MySQL對(duì)容器用戶不可見,也不可訪。 掌握Docker中rootfs以及Union mount的概念之后,再來(lái)理解Docker鏡像,就會(huì)變得水到渠成。 2.3 imageDocker中rootfs的概念,起到容器文件系統(tǒng)中基石的作用。對(duì)于容器而言,其只讀的特性,也是不難理解。神奇的是,實(shí)際情況下Docker的rootfs設(shè)計(jì)與實(shí)現(xiàn)比上文的描述還要精妙不少。 繼續(xù)以u(píng)buntu 14.04為例,雖然通過(guò)AUFS可以實(shí)現(xiàn)rootfs與read-write filesystem的合并,但是考慮到rootfs自身接近200MB的磁盤大小,如果以這個(gè)rootfs的粒度來(lái)實(shí)現(xiàn)容器的創(chuàng)建與遷移等,是否會(huì)稍顯笨重,同時(shí)也會(huì)大大降低鏡像的靈活性。而且,若用戶希望擁有一個(gè)ubuntu 14.10的rootfs,那么是否有必要?jiǎng)?chuàng)建一個(gè)全新的rootfs,畢竟ubuntu 14.10和ubuntu 14.04的rootfs中有很多一致的內(nèi)容。 Docker中image的概念,非常巧妙的解決了以上的問(wèn)題。最為簡(jiǎn)單的解釋image,就是 Docker容器中只讀文件系統(tǒng)rootfs的一部分。換言之,實(shí)際上Docker容器的rootfs可以由多個(gè)image來(lái)構(gòu)成。多個(gè)image構(gòu)成rootfs的方式依然沿用Union mount技術(shù)。 多個(gè)Image構(gòu)成rootfs的示意圖如圖2.3(圖中,rootfs中每一層image中的內(nèi)容劃分只為了闡述清楚rootfs由多個(gè)image構(gòu)成,并不代表實(shí)際情況中rootfs中的內(nèi)容劃分): 圖2.3容器rootfs多image構(gòu)成圖 從上圖可以看出,舉例的容器rootfs包含4個(gè)image,其中每個(gè)image中都有一些用戶視角文件系統(tǒng)中的一部分內(nèi)容。4個(gè)image處于層疊的關(guān)系,除了最底層的image,每一層的image都疊加在另一個(gè)image之上。另外,每一個(gè)image均含有一個(gè)image ID,用以唯一的標(biāo)記該image。 基于以上的概念,Docker Image中又抽象出兩種概念:Parent Image以及Base Image。除了容器rootfs最底層的image,其余image都依賴于其底下的一個(gè)或多個(gè)image,而Docker中將下一層的image稱為上一層image的Parent Image。以圖2.3為例,imageID_0是imageID_1的Parent Image,imageID_2是imageID_3的Parent Image,而imageID_0沒(méi)有Parent Image。對(duì)于最下層的image,即沒(méi)有Parent Image的鏡像,在Docker中習(xí)慣稱之為Base Image。 通過(guò)image的形式,原先較為臃腫的rootfs被逐漸打散成輕便的多層。Image除了輕便的特性,同時(shí)還有上文提到的只讀特性,如此一來(lái),在不同的容器、不同的rootfs中image完全可以用來(lái)復(fù)用。 多image組織關(guān)系與復(fù)用關(guān)系如圖2.4(圖中鏡像名稱的舉例只為將image之間的關(guān)系闡述清楚,并不代表實(shí)際情況中相應(yīng)名稱image之間的關(guān)系): 圖2.4 多image組織關(guān)系示意圖 圖2.4中,共羅列了11個(gè)image,這11個(gè)image之間的關(guān)系呈現(xiàn)一副森林圖。森林中含有兩棵樹,左邊樹中包含5個(gè)節(jié)點(diǎn),即含有5個(gè)image;右邊樹中包含6個(gè)節(jié)點(diǎn),即含有6個(gè)image。圖中,有些image標(biāo)記了紅色字段,意味該image代表某一種容器鏡像rootfs的最上層image。如圖中的ubuntu:14.04,代表imageID_3為該類型容器rootfs的最上層,沿著該節(jié)點(diǎn)找到樹的根節(jié)點(diǎn),可以發(fā)現(xiàn)路徑上還有imageID_2,imageID_1和imageID_0。特殊的是,imageID_2作為imageID_3的Parent Image,同時(shí)又是容器鏡像ubuntu:12.04的rootfs中的最上層,可見鏡像ubuntu:14.04只是在鏡像ubuntu:12.04之上,再另行疊加了一層。因此,在下載鏡像ubuntu:12.04以及ubuntu:14.04時(shí),只會(huì)下載一份imageID_2、imageID_1和imageID_0,實(shí)現(xiàn)image的復(fù)用。同時(shí),右邊樹中mysql:5.6、mongo:2.2、debian:wheezy和debian:jessie也呈現(xiàn)同樣的關(guān)系。 2.4 layerDocker術(shù)語(yǔ)中,layer是一個(gè)與image含義較為相近的詞。容器鏡像的rootfs是容器只讀的文件系統(tǒng),rootfs又是由多個(gè)只讀的image構(gòu)成。于是,rootfs中每個(gè)只讀的image都可以稱為一層layer。 除了只讀的image之外,Docker Daemon在創(chuàng)建容器時(shí)會(huì)在容器的rootfs之上,再mount一層read-write filesystem,而這一層文件系統(tǒng),也稱為容器的一層layer,常被稱為top layer。 因此,總結(jié)而言,Docker容器中的每一層只讀的image,以及最上層可讀寫的文件系統(tǒng),均被稱為layer。如此一來(lái),layer的范疇比image多了一層,即多包含了最上層的read-write filesystem。 有了layer的概念,大家可以思考這樣一個(gè)問(wèn)題:容器文件系統(tǒng)分為只讀的rootfs,以及可讀寫的top layer,那么容器運(yùn)行時(shí)若在top layer中寫入了內(nèi)容,那這些內(nèi)容是否可以持久化,并且也被其它容器復(fù)用? 上文對(duì)于image的分析中,提到了image有復(fù)用的特性,既然如此,再提一個(gè)更為大膽的假設(shè):容器的top layer是否可以轉(zhuǎn)變?yōu)閕mage? 答案是肯定的。Docker的設(shè)計(jì)理念中,top layer轉(zhuǎn)變?yōu)閕mage的行為(Docker中稱為commit操作),大大釋放了容器rootfs的靈活性。Docker的開發(fā)者完全可以基于某個(gè)鏡像創(chuàng)建容器做開發(fā)工作,并且無(wú)論在開發(fā)周期的哪個(gè)時(shí)間點(diǎn),都可以對(duì)容器進(jìn)行commit,將所有top layer中的內(nèi)容打包為一個(gè)image,構(gòu)成一個(gè)新的鏡像。Commit完畢之后,用戶完全可以基于新的鏡像,進(jìn)行開發(fā)、分發(fā)、測(cè)試、部署等。不僅docker commit的原理如此,基于Dockerfile的docker build,其追核心的思想,也是不斷將容器的top layer轉(zhuǎn)化為image。 3.總結(jié)Docker風(fēng)暴席卷全球,并非偶然。如今的云計(jì)算時(shí)代下,輕量級(jí)容器技術(shù)與靈活的鏡像技術(shù)相結(jié)合,似乎顛覆了以往的軟件交付模式,為持續(xù)集成(Continuous Integration, CI)與持續(xù)交付(Continuous Delivery, CD)的發(fā)展帶來(lái)了全新的契機(jī)。 理解Docker的“鏡像”技術(shù),有助于Docker愛好者更好的使用、創(chuàng)建以及交付Docker鏡像?;诖?,本文從Docker鏡像的4個(gè)重要概念入手,介紹了Docker鏡像中包含的內(nèi)容,涉及到的技術(shù),還有重要的特性。Docker引入優(yōu)秀的“鏡像”技術(shù)時(shí),著實(shí)使容器的使用變得更為便利,也拓寬了Docker的使用范疇。然而,于此同時(shí),我們也應(yīng)該理性地看待鏡像技術(shù)引入時(shí),是否會(huì)帶來(lái)其它的副作用。關(guān)于鏡像技術(shù)的其它思考,《Docker源碼分析系列》將在后續(xù)另文分析。 4.作者介紹孫宏亮,DaoCloud初創(chuàng)團(tuán)隊(duì)成員,軟件工程師,浙江大學(xué)VLIS實(shí)驗(yàn)室應(yīng)屆研究生。讀研期間活躍在PaaS和Docker開源社區(qū),對(duì)Cloud Foundry有深入研究和豐富實(shí)踐,擅長(zhǎng)底層平臺(tái)代碼分析,對(duì)分布式平臺(tái)的架構(gòu)有一定經(jīng)驗(yàn),撰寫了大量有深度的技術(shù)博客。2014年末以合伙人身份加入DaoCloud團(tuán)隊(duì),致力于傳播以Docker為主的容器的技術(shù),推動(dòng)互聯(lián)網(wǎng)應(yīng)用的容器化步伐。郵箱:allen.sun@ 5.參考文獻(xiàn)http://www.csdn.net/article/2014-09-24/2821832 http://www./cn/articles/docker-future https://docs./terms/layer/#layer https://www./legacy/publications/library/proceedings/neworl/full_papers/mckusick.a |
|