本教程有三個(gè)目標(biāo):說明Docker解決的問題、說明它如何解決這個(gè)問題、以及說明它使用了哪些技術(shù)來解決這個(gè)問題,這不是一篇教你怎么運(yùn)行安裝Docker的教程。 Docker是一個(gè)相對(duì)較新且發(fā)展非??焖俚捻?xiàng)目,可用來創(chuàng)建非常輕量的“虛擬機(jī)”。注意這里的引號(hào)非常重要,Docker創(chuàng)建的并非真正的虛擬機(jī),而更像是打了激素的chroot,嗯,是大量的激素。 在我們繼續(xù)之前,我先說下,截至目前(2015年1月4日)為止,Docker只能在Linux上工作,暫不支持Windows或OSX(譯者注:不直接支持)。我稍后會(huì)講到Docker的架構(gòu),你會(huì)明白其中的原因。所以,如果想在非Linux平臺(tái)上使用Docker,你需要在虛擬機(jī)里運(yùn)行Linux。 本教程有三個(gè)目標(biāo):說明Docker解決的問題、說明它如何解決這個(gè)問題、以及說明它使用了哪些技術(shù)來解決這個(gè)問題。這不是一篇教你怎么運(yùn)行安裝Docker的教程,Docker此類教程已經(jīng)有很多,包括Docker作者的在線互動(dòng)教程(譯者注:作者很喜歡在一個(gè)句子里引用多個(gè)鏈接,下同)。本文最后有一個(gè)步驟說明,目的是用一個(gè)明確的現(xiàn)實(shí)世界的例子來串聯(lián)文章中所有的理論,但不會(huì)太過詳細(xì)。 Docker能做什么? Docker可以解決虛擬機(jī)能夠解決的問題,同時(shí)也能夠解決虛擬機(jī)由于資源要求過高而無法解決的問題。Docker能處理的事情包括:
Docker背后的想法是創(chuàng)建軟件程序可移植的輕量容器,讓其可以在任何安裝了Docker的機(jī)器上運(yùn)行,而不用關(guān)心底層操作系統(tǒng),類似船舶使用的集裝箱,野心勃勃的他們成功了。 Docker究竟做了什么? 這一節(jié)我不會(huì)說明Docker使用了哪些技術(shù)來完成它的工作,或有什么具體的命令可用,這些放在了最后一節(jié),這里我將說明的是Docker提供的資源和抽象。 Docker兩個(gè)最重要的概念是鏡像和容器。除此之外,鏈接和數(shù)據(jù)卷也很重要。我們先從鏡像入手。 鏡像 Docker的鏡像類似虛擬機(jī)的快照,但更輕量,非常非常輕量(下節(jié)細(xì)說)。 創(chuàng)建Docker鏡像有幾種方式,多數(shù)是在一個(gè)現(xiàn)有鏡像基礎(chǔ)上創(chuàng)建新鏡像,因?yàn)閹缀跄阈枰娜魏螙|西都有了公共鏡像,包括所有主流Linux發(fā)行版,你應(yīng)該不會(huì)找不到你需要的鏡像。不過,就算你想從頭構(gòu)建一個(gè)鏡像,也有好幾種方法。 要?jiǎng)?chuàng)建一個(gè)鏡像,你可以拿一個(gè)鏡像,對(duì)它進(jìn)行修改來創(chuàng)建它的子鏡像。實(shí)現(xiàn)的方式有兩種:在一個(gè)文件中指定一個(gè)基礎(chǔ)鏡像及需要完成的修改;或通過“運(yùn)行”一個(gè)鏡像,對(duì)其進(jìn)行修改并提交。不同方式各有優(yōu)點(diǎn),不過一般會(huì)使用文件來指定所做的變化。 鏡像擁有唯一ID,以及一個(gè)供人閱讀的名字和標(biāo)簽對(duì)。鏡像可以命名為類似ubuntu:latest、ubuntu:precise、django:1.6、django:1.7等等。 容器 現(xiàn)在說容器了。你可以從鏡像中創(chuàng)建容器,這等同于從快照中創(chuàng)建虛擬機(jī),不過更輕量。應(yīng)用是由容器運(yùn)行的。 舉個(gè)例子,你可以下載一個(gè)Ubuntu的鏡像(有個(gè)叫docker registry的鏡像公共倉(cāng)庫(kù)),通過安裝Gunicorn和你的Django應(yīng)用及其依賴完成對(duì)它的修改,然后從該鏡像中創(chuàng)建一個(gè)容器,在它啟動(dòng)后運(yùn)行你的應(yīng)用。 容器與虛擬機(jī)一樣,是隔離的(有一點(diǎn)要注意,我稍后會(huì)討論到)。它們也擁有一個(gè)唯一ID和唯一的供人閱讀的名字。容器有必要對(duì)外暴露服務(wù),因此Docker允許暴露容器的特定端口。 容器與虛擬機(jī)相比有兩個(gè)主要差異。第一個(gè)是:它們被設(shè)計(jì)成運(yùn)行單進(jìn)程,無法很好地模擬一個(gè)完整的環(huán)境(如果那是你需要的,請(qǐng)看看LXC)。你可能會(huì)嘗試運(yùn)行runit或supervisord實(shí)例來啟動(dòng)多個(gè)進(jìn)程,但(以我的愚見)這真的沒有必要。 單進(jìn)程與多進(jìn)程之爭(zhēng)非常精彩。你應(yīng)該知道的是,Docker設(shè)計(jì)者極力推崇“一個(gè)容器一個(gè)進(jìn)程的方式”,如果你要選擇在一個(gè)容器中運(yùn)行多個(gè)進(jìn)程,那唯一情況是:出于調(diào)試目的,運(yùn)行類似ssh的東西來訪問運(yùn)行中的容器,不過docker exec命令解決了這個(gè)問題。 容器和虛擬機(jī)的第二個(gè)巨大差異是:當(dāng)你停止一個(gè)虛擬機(jī)時(shí),可能除了一些臨時(shí)文件,沒有文件會(huì)被刪除;當(dāng)你停止一個(gè)Docker容器,對(duì)初始狀態(tài)(創(chuàng)建容器所用的鏡像的狀態(tài))做的所有變化都會(huì)丟失。這是使用Docker時(shí)必須做出的最大思維變化之一:容器是短暫和一次性的。 數(shù)據(jù)卷 如果你的電子商務(wù)網(wǎng)站剛收到客戶支付的3萬元,內(nèi)核崩潰了,所有數(shù)據(jù)庫(kù)變化都丟失了……對(duì)你或Docker來說都不是一件好事,不過不要擔(dān)心。Docker允許你定義數(shù)據(jù)卷——用于保存持久數(shù)據(jù)的空間。Docker強(qiáng)制你定義應(yīng)用部分和數(shù)據(jù)部分,并要求你將它們分開。 卷是針對(duì)容器的,你可以使用同一個(gè)鏡像創(chuàng)建多個(gè)容器并定義不同的卷。卷保存在運(yùn)行Docker的宿主文件系統(tǒng)上,你可以指定卷存放的目錄,或讓Docker保存在默認(rèn)位置。保存在其他類型文件系統(tǒng)上的都不是一個(gè)卷,稍后再具體說。 鏈接 鏈接是Docker的另一個(gè)重要部分。 容器啟動(dòng)時(shí),將被分配一個(gè)隨機(jī)的私有IP,其它容器可以使用這個(gè)IP地址與其進(jìn)行通訊。這點(diǎn)非常重要,原因有二:一是它提供了容器間相互通信的渠道,二是容器將共享一個(gè)本地網(wǎng)絡(luò)。我曾經(jīng)碰到一個(gè)問題,在同一臺(tái)機(jī)器上為兩個(gè)客戶啟動(dòng)兩個(gè)elasticsearch容器,但保留集群名稱為默認(rèn)設(shè)置,結(jié)果這兩臺(tái)elasticsearch服務(wù)器立馬變成了一個(gè)自主集群。 要開啟容器間通訊,Docker允許你在創(chuàng)建一個(gè)新容器時(shí)引用其它現(xiàn)存容器,在你剛創(chuàng)建的容器里被引用的容器將獲得一個(gè)(你指定的)別名。我們就說,這兩個(gè)容器鏈接在了一起。 因此,如果DB容器已經(jīng)在運(yùn)行,我可以創(chuàng)建web服務(wù)器容器,并在創(chuàng)建時(shí)引用這個(gè)DB容器,給它一個(gè)別名,比如dbapp。在這個(gè)新建的web服務(wù)器容器里,我可以在任何時(shí)候使用主機(jī)名dbapp與DB容器進(jìn)行通訊。 Docker更進(jìn)一步,要求你聲明容器在被鏈接時(shí)要開放哪些端口給其他容器,否則將沒有端口可用。 Docker鏡像的可移植性 在創(chuàng)建鏡像時(shí)有一點(diǎn)要注意。Docker允許你在一個(gè)鏡像中指定卷和端口。從這個(gè)鏡像創(chuàng)建的容器繼承了這些設(shè)置。但是,Docker不允許你在鏡像上指定任何不可移植的內(nèi)容。 例如,你可以在鏡像里定義卷,只要它們被保存在Docker使用的默認(rèn)位置。這是因?yàn)槿绻阍谒拗魑募到y(tǒng)里指定了一個(gè)特定目錄來保存卷,其他使用這個(gè)鏡像的宿主無法保證這個(gè)目錄是存在的。 你可以定義要暴露的端口,但僅限那些在創(chuàng)建鏈接時(shí)暴露給其他容器的端口,你不能指定暴露給宿主的端口,因?yàn)槟銦o從知曉使用那個(gè)鏡像的宿主有哪些端口可用。 你也不能在鏡像上定義鏈接。使用鏈接要求通過名字引用其他容器,但你無法預(yù)知每個(gè)使用那個(gè)鏡像的宿主如何命名容器。 鏡像必須完全可移植,Docker不允許例外。 以上就是主要的部分,創(chuàng)建鏡像、用它們創(chuàng)建容器,在需要時(shí)暴露端口和創(chuàng)造卷、通過鏈接將幾個(gè)容器連接在一起。不過,這一切如何能在不引起額外開銷條件下達(dá)成? Docker如何完成它需要完成的東西? 兩個(gè)詞:cgroups和union文件系統(tǒng)。Docker使用cgroup來提供容器隔離,而union文件系統(tǒng)用于保存鏡像并使容器變得短暫。 Cgroups 這是Linux內(nèi)核功能,它讓兩件事情變成可能:
這里的關(guān)鍵詞是命名空間。比如說,一個(gè)PID命名空間允許它里面的進(jìn)程使用隔離的PID,并與主PID命名空間獨(dú)立開來,因此你可以在一個(gè)PID命名空間里擁有自己的PID為1的初始化進(jìn)程。其他命名空間與此類似。然后你可以使用cgroup創(chuàng)建一個(gè)環(huán)境,進(jìn)程可以在其中運(yùn)行,并與操作系統(tǒng)的其他進(jìn)程隔離開,但這里的關(guān)鍵點(diǎn)是這個(gè)環(huán)境上的進(jìn)程使用的是已經(jīng)加載和運(yùn)行的內(nèi)核,因此額外開銷與運(yùn)行其他進(jìn)程幾乎是一樣的。Chroot之于cgroup就好像我之于綠巨人(The Hulk)、貝恩(Bane)和毒液(Venom)的組合(譯者注:本文作者非常瘦弱,后三者都非常強(qiáng)壯)。 Union文件系統(tǒng) Union文件系統(tǒng)允許通過union裝載變化的分層疊加。在union文件系統(tǒng)里,文件系統(tǒng)可以被裝載在其他文件系統(tǒng)之上,其結(jié)果就是一個(gè)變化的分層集合。每個(gè)裝載的文件系統(tǒng)表示前一個(gè)文件系統(tǒng)之后的變化集合,就像是一個(gè)diff。 當(dāng)你下載一個(gè)鏡像,修改它,然后保存成新版本,你只是創(chuàng)建了加載在包裹基礎(chǔ)鏡像的初始層上的一個(gè)新的union文件系統(tǒng)。這使得Docker鏡像非常輕,比如:你的DB、Nginx和Syslog鏡像都可以共享同一個(gè)Ubuntu基礎(chǔ),每一個(gè)鏡像保存的只是在基礎(chǔ)之上工作需要的變化。 截至2015年1月4日,Docker允許在union文件系統(tǒng)中使用aufs、btrfs或設(shè)備映射(device mapper)。 鏡像 我們來看一下postgresql的一個(gè)鏡像:
就是這樣,鏡像只是一個(gè)json,它指定了從該鏡像運(yùn)行的容器的特性,union裝載點(diǎn)保存在哪里,要暴露什么端口等等。每個(gè)鏡像與一個(gè)union文件系統(tǒng)相關(guān)聯(lián),每個(gè)Docker上的union文件系統(tǒng)都有一個(gè)上層,就像是計(jì)算機(jī)科技樹(不像其他樹有一大堆的家族)。如果它看起來有點(diǎn)嚇人或有些東西串不起來,不要擔(dān)心,這只是出于教學(xué)目的,你并不會(huì)直接處理這些文件。 容器 容器之所以是短暫的,是因?yàn)楫?dāng)你從鏡像上創(chuàng)建一個(gè)容器,Docker會(huì)創(chuàng)建一個(gè)空白的union文件系統(tǒng)加載在與該鏡像關(guān)聯(lián)的union文件系統(tǒng)之上。 由于union文件系統(tǒng)是空白的,這意味著沒有變化會(huì)被應(yīng)用到鏡像的文件系統(tǒng)上,你創(chuàng)建的變化會(huì)得到體現(xiàn),但是當(dāng)容器停止,該容器的union文件系統(tǒng)會(huì)被丟棄,留下的是你啟動(dòng)時(shí)的原始鏡像文件系統(tǒng)。除非你創(chuàng)建一個(gè)新的鏡像,或制作一個(gè)卷,你所做的變化在容器停止時(shí)都會(huì)消失。 卷所做的是在容器內(nèi)指定一個(gè)目錄,以便在union文件系統(tǒng)之外保存它。 這是一個(gè)bestwebappever的容器:
基本上與鏡像相同,不過現(xiàn)在還指定了一些暴露給宿主的端口,也聲明了卷位于宿主的位置,容器狀態(tài)是從現(xiàn)在直到結(jié)束,等等。與前面一樣,如果它看起來讓人生畏,不要擔(dān)心,你不會(huì)直接處理這些json。 超級(jí)、無比簡(jiǎn)單的步驟說明 第一步,安裝Docker。 Docker命令工具需要root權(quán)限才能工作。你可以將你的用戶放入docker組來避免每次都要使用sudo。 第二步,使用以下命令從公共registry下載一個(gè)鏡像:
這個(gè)公共registry上有你需要的幾乎所有東西的鏡像:Ubuntu、Fedora、Postgresql、MySQL、Jenkins、Elasticsearch、Redis等等。Docker開發(fā)人員在這個(gè)公共registry里維護(hù)著數(shù)個(gè)鏡像,不過你能從上面拉取大量來自用戶發(fā)布的自建鏡像。 也許你需要或想要一個(gè)私有的registry(用于開發(fā)應(yīng)用之類的容器),你可以先看看這個(gè)。現(xiàn)在,有好幾個(gè)方式可以設(shè)置你自己的私有registry。你也可以買一個(gè)。 第三步,列出你的鏡像:
第四步,從該鏡像上創(chuàng)建一個(gè)容器。
上一條命令的簡(jiǎn)要說明: --rm:告訴Docker一旦運(yùn)行的進(jìn)程退出就刪除容器。這在進(jìn)行測(cè)試時(shí)非常有用,可免除雜亂 在運(yùn)行run命令時(shí),你可指定鏈接、卷、端口、窗口名稱(如果你沒提供,Docker將分配一個(gè)默認(rèn)名稱)等等。 現(xiàn)在,我們?cè)诤笈_(tái)運(yùn)行一個(gè)容器:
輸出的是分配的ID,因?yàn)槭请S機(jī)的,你的將有所不同。我們來檢查一下容器是否起來了:
就在那,它被自動(dòng)分配了一個(gè)叫l(wèi)oving_mcclintock的名稱。我們看看容器里正在發(fā)生什么:
我們所做的是在容器里運(yùn)行程序,這里的程序是/bin/bash。-ti標(biāo)志與docker run的作用相同,將我們放置到容器的控制臺(tái)里。 結(jié)尾 差不多就是這樣了。有太多的東西可以講,但那超出了本文的范圍。 Docker的基本結(jié)構(gòu):
與Docker相關(guān)的項(xiàng)目:
原文鏈接:http:///article/133 【編輯推薦】 【責(zé)任編輯:Ophira TEL:(010)68476606】 |
|