Docker終極指南:為什么Docker能做這么多事
本教程有三個(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能處理的事情包括:
- 隔離應(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ì)說明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的鏡像公共倉庫),通過安裝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ù)庫變化都丟失了……對(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á)成?
#p#
Docker如何完成它需要完成的東西?
兩個(gè)詞:cgroups和union文件系統(tǒng)。Docker使用cgroup來提供容器隔離,而union文件系統(tǒng)用于保存鏡像并使容器變得短暫。
Cgroups
這是Linux內(nèi)核功能,它讓兩件事情變成可能:
- 限制Linux進(jìn)程組的資源占用(內(nèi)存、CPU)
 - 為進(jìn)程組制作 PID、UTS、IPC、網(wǎng)絡(luò)、用戶及裝載命名空間
 
這里的關(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è)鏡像:
- [{
 - "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ī)科技樹(不像其他樹有一大堆的家族)。如果它看起來有點(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的容器:
- [{
 - "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
 - }
 - }
 - ]
 
基本上與鏡像相同,不過現(xiàn)在還指定了一些暴露給宿主的端口,也聲明了卷位于宿主的位置,容器狀態(tài)是從現(xiàn)在直到結(jié)束,等等。與前面一樣,如果它看起來讓人生畏,不要擔(dān)心,你不會(huì)直接處理這些json。
#p#
超級(jí)、無比簡(jiǎn)單的步驟說明
***步,安裝Docker。
Docker命令工具需要root權(quán)限才能工作。你可以將你的用戶放入docker組來避免每次都要使用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開發(fā)人員在這個(gè)公共registry里維護(hù)著數(shù)個(gè)鏡像,不過你能從上面拉取大量來自用戶發(fā)布的自建鏡像。
也許你需要或想要一個(gè)私有的registry(用于開發(fā)應(yīng)用之類的容器),你可以先看看這個(gè)?,F(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)要說明:
--rm:告訴Docker一旦運(yùn)行的進(jìn)程退出就刪除容器。這在進(jìn)行測(cè)試時(shí)非常有用,可免除雜亂
-ti:告訴Docker分配一個(gè)偽終端并進(jìn)入交互模式。這將進(jìn)入到容器內(nèi),對(duì)于快速原型開發(fā)或嘗試很有用,但不要在生產(chǎn)容器中打開這些標(biāo)志
ubuntu:這是容器立足的鏡像
/bin/bash:要運(yùn)行的命令,因?yàn)槲覀円越换ツJ絾?dòng),它將顯示一個(gè)容器的提示符
在運(yùn)行run命令時(shí),你可指定鏈接、卷、端口、窗口名稱(如果你沒提供,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ī)的,你的將有所不同。我們來檢查一下容器是否起來了:
- $> 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 baseimage
 - Shipyard
 - DockerUI
 - CoreOS
 - Decking
 - Docker-py
 - Docker-map
 - Docker-fabric
 





















 
 
 











 
 
 
 