本教程有三個(gè)目標(biāo):說(shuō)明Docker解決的問(wèn)題、說(shuō)明它如何解決這個(gè)問(wèn)題、以及說(shuō)明它使用了哪些技術(shù)來(lái)解決這個(gè)問(wèn)題,這不是一篇教你怎么運(yùn)行安裝Docker的教程。
Docker是一個(gè)相對(duì)較新且發(fā)展非常快速的項(xiàng)目,可用來(lái)創(chuàng)建非常輕量的“虛擬機(jī)”。注意這里的引號(hào)非常重要,Docker創(chuàng)建的并非真正的虛擬機(jī),而更像是打了激素的chroot,嗯,是大量的激素。
在我們繼續(xù)之前,我先說(shuō)下,截至目前(2015年1月4日)為止,Docker只能在Linux上工作,暫不支持Windows或OSX(譯者注:不直接支持)。我稍后會(huì)講到Docker的架構(gòu),你會(huì)明白其中的原因。所以,如果想在非Linux平臺(tái)上使用Docker,你需要在虛擬機(jī)里運(yùn)行Linux。
本教程有三個(gè)目標(biāo):說(shuō)明Docker解決的問(wèn)題、說(shuō)明它如何解決這個(gè)問(wèn)題、以及說(shuō)明它使用了哪些技術(shù)來(lái)解決這個(gè)問(wèn)題。這不是一篇教你怎么運(yùn)行安裝Docker的教程,Docker此類教程已經(jīng)有很多,包括Docker作者的在線互動(dòng)教程(譯者注:作者很喜歡在一個(gè)句子里引用多個(gè)鏈接,下同)。本文最后有一個(gè)步驟說(shuō)明,目的是用一個(gè)明確的現(xiàn)實(shí)世界的例子來(lái)串聯(lián)文章中所有的理論,但不會(huì)太過(guò)詳細(xì)。
Docker能做什么?
Docker可以解決虛擬機(jī)能夠解決的問(wèn)題,同時(shí)也能夠解決虛擬機(jī)由于資源要求過(guò)高而無(wú)法解決的問(wèn)題。Docker能處理的事情包括:
隔離應(yīng)用依賴創(chuàng)建應(yīng)用鏡像并進(jìn)行復(fù)制創(chuàng)建容易分發(fā)的即啟即用的應(yīng)用允許實(shí)例簡(jiǎn)單、快速地?cái)U(kuò)展測(cè)試應(yīng)用并隨后銷毀它們Docker背后的想法是創(chuàng)建軟件程序可移植的輕量容器,讓其可以在任何安裝了Docker的機(jī)器上運(yùn)行,而不用關(guān)心底層操作系統(tǒng),類似船舶使用的集裝箱,野心勃勃的他們成功了。
Docker究竟做了什么?
這一節(jié)我不會(huì)說(shuō)明Docker使用了哪些技術(shù)來(lái)完成它的工作,或有什么具體的命令可用,這些放在了最后一節(jié),這里我將說(shuō)明的是Docker提供的資源和抽象。
Docker兩個(gè)最重要的概念是鏡像和容器。除此之外,鏈接和數(shù)據(jù)卷也很重要。我們先從鏡像入手。
鏡像
Docker的鏡像類似虛擬機(jī)的快照,但更輕量,非常非常輕量(下節(jié)細(xì)說(shuō))。
創(chuàng)建Docker鏡像有幾種方式,多數(shù)是在一個(gè)現(xiàn)有鏡像基礎(chǔ)上創(chuàng)建新鏡像,因?yàn)閹缀跄阈枰娜魏螙|西都有了公共鏡像,包括所有主流Linux發(fā)行版,你應(yīng)該不會(huì)找不到你需要的鏡像。不過(guò),就算你想從頭構(gòu)建一個(gè)鏡像,也有好幾種方法。
要?jiǎng)?chuàng)建一個(gè)鏡像,你可以拿一個(gè)鏡像,對(duì)它進(jìn)行修改來(lái)創(chuàng)建它的子鏡像。實(shí)現(xiàn)的方式有兩種:在一個(gè)文件中指定一個(gè)基礎(chǔ)鏡像及需要完成的修改;或通過(guò)“運(yùn)行”一個(gè)鏡像,對(duì)其進(jìn)行修改并提交。不同方式各有優(yōu)點(diǎn),不過(guò)一般會(huì)使用文件來(lái)指定所做的變化。
鏡像擁有唯一ID,以及一個(gè)供人閱讀的名字和標(biāo)簽對(duì)。鏡像可以命名為類似ubuntu:latest、ubuntu:precise、django:1.6、django:1.7等等。
容器
現(xiàn)在說(shuō)容器了。你可以從鏡像中創(chuàng)建容器,這等同于從快照中創(chuàng)建虛擬機(jī),不過(guò)更輕量。應(yīng)用是由容器運(yùn)行的。
舉個(gè)例子,你可以下載一個(gè)Ubuntu的鏡像(有個(gè)叫docker registry的鏡像公共倉(cāng)庫(kù)),通過(guò)安裝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)程,無(wú)法很好地模擬一個(gè)完整的環(huán)境(如果那是你需要的,請(qǐng)看看LXC)。你可能會(huì)嘗試運(yùn)行runit或supervisord實(shí)例來(lái)啟動(dòng)多個(gè)進(jìn)程,但(以我的愚見(jiàn))這真的沒(méi)有必要。
單進(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的東西來(lái)訪問(wèn)運(yùn)行中的容器,不過(guò)docker exec命令解決了這個(gè)問(wèn)題。
容器和虛擬機(jī)的第二個(gè)巨大差異是:當(dāng)你停止一個(gè)虛擬機(jī)時(shí),可能除了一些臨時(shí)文件,沒(méi)有文件會(huì)被刪除;當(dāng)你停止一個(gè)Docker容器,對(duì)初始狀態(tài)(創(chuàng)建容器所用的鏡像的狀態(tài))做的所有變化都會(huì)丟失。這是使用Docker時(shí)必須做出的最大思維變化之一:容器是短暫和一次性的。
數(shù)據(jù)卷
如果你的電子商務(wù)網(wǎng)站剛收到客戶支付的3萬(wàn)元,內(nèi)核崩潰了,所有數(shù)據(jù)庫(kù)變化都丟失了……對(duì)你或Docker來(lái)說(shuō)都不是一件好事,不過(guò)不要擔(dān)心。Docker允許你定義數(shù)據(jù)卷——用于保存持久數(shù)據(jù)的空間。Docker強(qiáng)制你定義應(yīng)用部分和數(shù)據(jù)部分,并要求你將它們分開(kāi)。
卷是針對(duì)容器的,你可以使用同一個(gè)鏡像創(chuàng)建多個(gè)容器并定義不同的卷。卷保存在運(yùn)行Docker的宿主文件系統(tǒng)上,你可以指定卷存放的目錄,或讓Docker保存在默認(rèn)位置。保存在其他類型文件系統(tǒng)上的都不是一個(gè)卷,稍后再具體說(shuō)。
鏈接
鏈接是Docker的另一個(gè)重要部分。
容器啟動(dòng)時(shí),將被分配一個(gè)隨機(jī)的私有IP,其它容器可以使用這個(gè)IP地址與其進(jìn)行通訊。這點(diǎn)非常重要,原因有二:一是它提供了容器間相互通信的渠道,二是容器將共享一個(gè)本地網(wǎng)絡(luò)。我曾經(jīng)碰到一個(gè)問(wèn)題,在同一臺(tái)機(jī)器上為兩個(gè)客戶啟動(dòng)兩個(gè)elasticsearch容器,但保留集群名稱為默認(rèn)設(shè)置,結(jié)果這兩臺(tái)elasticsearch服務(wù)器立馬變成了一個(gè)自主集群。
要開(kāi)啟容器間通訊,Docker允許你在創(chuàng)建一個(gè)新容器時(shí)引用其它現(xiàn)存容器,在你剛創(chuàng)建的容器里被引用的容器將獲得一個(gè)(你指定的)別名。我們就說(shuō),這兩個(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í)要開(kāi)放哪些端口給其他容器,否則將沒(méi)有端口可用。
Docker鏡像的可移植性
在創(chuàng)建鏡像時(shí)有一點(diǎn)要注意。Docker允許你在一個(gè)鏡像中指定卷和端口。從這個(gè)鏡像創(chuàng)建的容器繼承了這些設(shè)置。但是,Docker不允許你在鏡像上指定任何不可移植的內(nèi)容。
例如,你可以在鏡像里定義卷,只要它們被保存在Docker使用的默認(rèn)位置。這是因?yàn)槿绻阍谒拗魑募到y(tǒng)里指定了一個(gè)特定目錄來(lái)保存卷,其他使用這個(gè)鏡像的宿主無(wú)法保證這個(gè)目錄是存在的。
你可以定義要暴露的端口,但僅限那些在創(chuàng)建鏈接時(shí)暴露給其他容器的端口,你不能指定暴露給宿主的端口,因?yàn)槟銦o(wú)從知曉使用那個(gè)鏡像的宿主有哪些端口可用。
你也不能在鏡像上定義鏈接。使用鏈接要求通過(guò)名字引用其他容器,但你無(wú)法預(yù)知每個(gè)使用那個(gè)鏡像的宿主如何命名容器。
鏡像必須完全可移植,Docker不允許例外。
以上就是主要的部分,創(chuàng)建鏡像、用它們創(chuàng)建容器,在需要時(shí)暴露端口和創(chuàng)造卷、通過(guò)鏈接將幾個(gè)容器連接在一起。不過(guò),這一切如何能在不引起額外開(kāi)銷條件下達(dá)成?
Docker如何完成它需要完成的東西?
兩個(gè)詞:cgroups和union文件系統(tǒng)。Docker使用cgroup來(lái)提供容器隔離,而union文件系統(tǒng)用于保存鏡像并使容器變得短暫。
Cgroups
這是Linux內(nèi)核功能,它讓兩件事情變成可能:
限制Linux進(jìn)程組的資源占用(內(nèi)存、CPU)為進(jìn)程組制作 PID、UTS、IPC、網(wǎng)絡(luò)、用戶及裝載命名空間這里的關(guān)鍵詞是命名空間。比如說(shuō),一個(gè)PID命名空間允許它里面的進(jìn)程使用隔離的PID,并與主PID命名空間獨(dú)立開(kāi)來(lái),因此你可以在一個(gè)PID命名空間里擁有自己的PID為1的初始化進(jìn)程。其他命名空間與此類似。然后你可以使用cgroup創(chuàng)建一個(gè)環(huán)境,進(jìn)程可以在其中運(yùn)行,并與操作系統(tǒng)的其他進(jìn)程隔離開(kāi),但這里的關(guān)鍵點(diǎn)是這個(gè)環(huán)境上的進(jìn)程使用的是已經(jīng)加載和運(yùn)行的內(nèi)核,因此額外開(kāi)銷與運(yùn)行其他進(jìn)程幾乎是一樣的。Chroot之于cgroup就好像我之于綠巨人(The Hulk)、貝恩(Bane)和毒液(Venom)的組合(譯者注:本文作者非常瘦弱,后三者都非常強(qiáng)壯)。
Union文件系統(tǒng)
Union文件系統(tǒng)允許通過(guò)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)。
鏡像
我們來(lái)看一下postgresql的一個(gè)鏡像:
[{ "AppArmorProfile": "", "Args": [ "postgres" ], "Config": { "AttachStderr": true, "AttachStdin": false, "AttachStdout": true, "Cmd": [ "postgres" ], "CpuShares": 0, "Cpuset": "", "Domainname": "", "Entrypoint": [ "/docker-entrypoint.sh" ], "Env": [ "PATH=/usr/lib/postgresql/9.3/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "LANG=en_US.utf8", "PG_MAJOR=9.3", "PG_VERSION=9.3.5-1.pgdg70 1", "PGDATA=/var/lib/postgresql/data" ], "ExposedPorts": { "5432/tcp": {} }, "Hostname": "6334a2022f21", "Image": "postgres", "MacAddress": "", "Memory": 0, "MemorySwap": 0, "NetworkDisabled": false, "OnBuild": null, "OpenStdin": false, "PortSpecs": null, "StdinOnce": false, "Tty": false, "User": "", "Volumes": { "/var/lib/postgresql/data": {} }, "WorkingDir": "" }, "Created": "2015-01-03T23:56:12.354896658Z", "Driver": "devicemapper", "ExecDriver": "native-0.2", "HostConfig": { "Binds": null, "CapAdd": null, "CapDrop": null, "ContainerIDFile": "", "Devices": null, "Dns": null, "DnsSearch": null, "ExtraHosts": null, "IpcMode": "", "Links": null, "LxcConf": null, "NetworkMode": "", "PortBindings": null, "Privileged": false, "PublishAllPorts": false, "RestartPolicy": { "MaximumRetryCount": 0, "Name": "" }, "SecurityOpt": null, "VolumesFrom": [ "bestwebappever.dev.db-data" ] }, "HostnamePath": "/mnt/docker/containers/6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da/hostname", "HostsPath": "/mnt/docker/containers/6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da/hosts", "Id": "6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da", "Image": "aaab661c1e3e8da2d9fc6872986cbd7b9ec835dcd3886d37722f1133baa3d2db", "MountLabel": "", "Name": "/bestwebappever.dev.db", "NetworkSettings": { "Bridge": "docker0", "Gateway": "172.17.42.1", "IPAddress": "172.17.0.176", "IPPrefixLen": 16, "MacAddress": "02:42:ac:11:00:b0", "PortMapping": null, "Ports": { "5432/tcp": null } }, "Path": "/docker-entrypoint.sh", "ProcessLabel": "", "ResolvConfPath": "/mnt/docker/containers/6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da/resolv.conf", "State": { "Error": "", "ExitCode": 0, "FinishedAt": "0001-01-01T00:00:00Z", "OOMKilled": false, "Paused": false, "Pid": 21654, "Restarting": false, "Running": true, "StartedAt": "2015-01-03T23:56:42.003405983Z" }, "Volumes": { "/var/lib/postgresql/data": "/mnt/docker/vfs/dir/5ac73c52ca86600a82e61279346dac0cb3e173b067ba9b219ea044023ca67561", "postgresql_data": "/mnt/docker/vfs/dir/abace588b890e9f4adb604f633c280b9b5bed7d20285aac9cc81a84a2f556034" }, "VolumesRW": { "/var/lib/postgresql/data": true, "postgresql_data": true } } ]就是這樣,鏡像只是一個(gè)json,它指定了從該鏡像運(yùn)行的容器的特性,union裝載點(diǎn)保存在哪里,要暴露什么端口等等。每個(gè)鏡像與一個(gè)union文件系統(tǒng)相關(guān)聯(lián),每個(gè)Docker上的union文件系統(tǒng)都有一個(gè)上層,就像是計(jì)算機(jī)科技樹(shù)(不像其他樹(shù)有一大堆的家族)。如果它看起來(lái)有點(diǎn)嚇人或有些東西串不起來(lái),不要擔(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)是空白的,這意味著沒(méi)有變化會(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的容器:
[{ "AppArmorProfile": "", "Args": , "Config": { "AttachStderr": true, "AttachStdin": false, "AttachStdout": true, "Cmd": [ "/sbin/my_init" ], "CpuShares": 0, "Cpuset": "", "Domainname": "", "Entrypoint": null, "Env": [ "DJANGO_CONFIGURATION=Local", "HOME=/root", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "TALPOR_ENVIRONMENT=local", "TALPOR_DIR=/opt/bestwebappever" ], "ExposedPorts": { "80/tcp": {} }, "Hostname": "44a87fdaf870", "Image": "talpor/bestwebappever:dev", "MacAddress": "", "Memory": 0, "MemorySwap": 0, "NetworkDisabled": false, "OnBuild": null, "OpenStdin": false, "PortSpecs": null, "StdinOnce": false, "Tty": false, "User": "", "Volumes": { "/opt/bestwebappever": {} }, "WorkingDir": "/opt/bestwebappever" }, "Created": "2015-01-03T23:56:15.378511619Z", "Driver": "devicemapper", "ExecDriver": "native-0.2", "HostConfig": { "Binds": [ "/home/german/bestwebappever/:/opt/bestwebappever:rw" ], "CapAdd": null, "CapDrop": null, "ContainerIDFile": "", "Devices": null, "Dns": null, "DnsSearch": null, "ExtraHosts": null, "IpcMode": "", "Links": [ "/bestwebappever.dev.db:/bestwebappever.dev.app/db", "/bestwebappever.dev.redis:/bestwebappever.dev.app/redis" ], "LxcConf": null, "NetworkMode": "", "PortBindings": { "80/tcp": [ { "HostIp": "", "HostPort": "8887" } ] }, "Privileged": false, "PublishAllPorts": false, "RestartPolicy": { "MaximumRetryCount": 0, "Name": "" }, "SecurityOpt": null, "VolumesFrom": [ "bestwebappever.dev.requirements-data" ] }, "HostnamePath": "/mnt/docker/containers/44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4/hostname", "HostsPath": "/mnt/docker/containers/44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4/hosts", "Id": "44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4", "Image": "b84804fac17b61fe8f344359285186f1a63cd8c0017930897a078cd09d61bb60", "MountLabel": "", "Name": "/bestwebappever.dev.app", "NetworkSettings": { "Bridge": "docker0", "Gateway": "172.17.42.1", "IPAddress": "172.17.0.179", "IPPrefixLen": 16, "MacAddress": "02:42:ac:11:00:b3", "PortMapping": null, "Ports": { "80/tcp": [ { "HostIp": "0.0.0.0", "HostPort": "8887" } ] } }, "Path": "/sbin/my_init", "ProcessLabel": "", "ResolvConfPath": "/mnt/docker/containers/44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4/resolv.conf", "State": { "Error": "", "ExitCode": 0, "FinishedAt": "0001-01-01T00:00:00Z", "OOMKilled": false, "Paused": false, "Pid": 21796, "Restarting": false, "Running": true, "StartedAt": "2015-01-03T23:56:47.537259546Z" }, "Volumes": { "/opt/bestwebappever": "/home/german/bestwebappever", "requirements_data": "/mnt/docker/vfs/dir/bc14bec26ca311d5ed9f2a83eebef872a879c9e2f1d932470e0fd853fe8be336" }, "VolumesRW": { "/opt/bestwebappever": true, "requirements_data": true } } [page]超級(jí)、無(wú)比簡(jiǎn)單的步驟說(shuō)明
第一步,安裝Docker。
Docker命令工具需要root權(quán)限才能工作。你可以將你的用戶放入docker組來(lái)避免每次都要使用sudo。
第二步,使用以下命令從公共registry下載一個(gè)鏡像:
$> docker pull ubuntu:latest ubuntu:latest: The image you are pulling has been verified 3b363fd9d7da: Pull complete .....<bunch of downloading-stuff output>..... 8eaa4ff06b53: Pull complete Status: Downloaded newer image for ubuntu:latest $>這個(gè)公共registry上有你需要的幾乎所有東西的鏡像:Ubuntu、Fedora、Postgresql、MySQL、Jenkins、Elasticsearch、Redis等等。Docker開(kāi)發(fā)人員在這個(gè)公共registry里維護(hù)著數(shù)個(gè)鏡像,不過(guò)你能從上面拉取大量來(lái)自用戶發(fā)布的自建鏡像。
也許你需要或想要一個(gè)私有的registry(用于開(kāi)發(fā)應(yīng)用之類的容器),你可以先看看這個(gè)。現(xiàn)在,有好幾個(gè)方式可以設(shè)置你自己的私有registry。你也可以買一個(gè)。
第三步,列出你的鏡像:
$> docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE ubuntu latest 8eaa4ff06b53 4 days ago 192.7 MB第四步,從該鏡像上創(chuàng)建一個(gè)容器。
$> docker run --rm -ti ubuntu /bin/bash root@4638a40c2fbb:/# ls bin boot dev etc home lib lib64 media mnt opt proc root...... root@4638a40c2fbb:/# exit上一條命令的簡(jiǎn)要說(shuō)明:
--rm:告訴Docker一旦運(yùn)行的進(jìn)程退出就刪除容器。這在進(jìn)行測(cè)試時(shí)非常有用,可免除雜亂
-ti:告訴Docker分配一個(gè)偽終端并進(jìn)入交互模式。這將進(jìn)入到容器內(nèi),對(duì)于快速原型開(kāi)發(fā)或嘗試很有用,但不要在生產(chǎn)容器中打開(kāi)這些標(biāo)志
ubuntu:這是容器立足的鏡像
/bin/bash:要運(yùn)行的命令,因?yàn)槲覀円越换ツJ絾?dòng),它將顯示一個(gè)容器的提示符
在運(yùn)行run命令時(shí),你可指定鏈接、卷、端口、窗口名稱(如果你沒(méi)提供,Docker將分配一個(gè)默認(rèn)名稱)等等。
現(xiàn)在,我們?cè)诤笈_(tái)運(yùn)行一個(gè)容器:
$> docker run -d ubuntu ping 8.8.8.8 31c68e9c09a0d632caae40debe13da3d6e612364198e2ef21f842762df4f987f $>輸出的是分配的ID,因?yàn)槭请S機(jī)的,你的將有所不同。我們來(lái)檢查一下容器是否起來(lái)了:
$> docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 31c68e9c09a0 ubuntu:latest "ping 8.8.8.8" 2 minutes ago Up 2 minutes loving_mcclintock就在那,它被自動(dòng)分配了一個(gè)叫l(wèi)oving_mcclintock的名稱。我們看看容器里正在發(fā)生什么:
$> docker exec -ti loving_mcclintock /bin/bash root@31c68e9c09a0:/# ps -aux|grep ping root 1 0.0 0.0 6504 636 ? Ss 20:46 0:00 ping 8.8.8.8 root@31c68e9c09a0:/# exit我們所做的是在容器里運(yùn)行程序,這里的程序是/bin/bash。-ti標(biāo)志與docker run的作用相同,將我們放置到容器的控制臺(tái)里。
結(jié)尾
差不多就是這樣了。有太多的東西可以講,但那超出了本文的范圍。
Docker的基本結(jié)構(gòu):
https://docs.docker.com/introd ... cker/ http://blog.docker.com/2014/03 ... iner/與Docker相關(guān)的項(xiàng)目:
Phusion Docker baseimageShipyardDockerUICoreOSDeckingDocker-pyDocker-mapDocker-fabric原文鏈接:http://dockerone.com/article/133