作為一種容器虛擬化技術(shù)护桦,Docker深度應(yīng)用了操作系統(tǒng)的多項(xiàng)底層支持技術(shù)煤裙。
早期版本的Docker是基于已經(jīng)成熟的Linux Container(LXC)技術(shù)實(shí)現(xiàn)的。自Docker 0.9版本起系草,Docker逐漸從LXC轉(zhuǎn)移到新的libcontainer(https://github.com/docker/libcontainer)上撕贞,并且積極推動(dòng)開(kāi)放容器規(guī)范runc,試圖打造更通用的底層容器虛擬化庫(kù)。
從操作系統(tǒng)功能上看罚渐,目前Docker底層依賴(lài)的核心技術(shù)主要包括Linux操作系統(tǒng)的命名空間(Namespace)却汉、控制組(Control Group)、聯(lián)合文件系統(tǒng)(Union File System)和Linux網(wǎng)絡(luò)虛擬化支持荷并。
基本架構(gòu)
Docker目前采用了標(biāo)準(zhǔn)的C/S架構(gòu)合砂。客戶(hù)端和服務(wù)端既可以運(yùn)行在一個(gè)機(jī)器上璧坟,也可運(yùn)行在不同機(jī)器上通過(guò)socket或者RESTful API來(lái)進(jìn)行通信既穆。
1.服務(wù)端
Docker Daemon一般在宿主主機(jī)后臺(tái)運(yùn)行,作為服務(wù)端接受來(lái)自客戶(hù)的請(qǐng)求雀鹃,并處理這些請(qǐng)求(創(chuàng)建幻工、運(yùn)行、分發(fā)容器)黎茎。
在設(shè)計(jì)上囊颅,Docker Daemon是一個(gè)模塊化的架構(gòu),通過(guò)專(zhuān)門(mén)的Engine模塊來(lái)分發(fā)管理各個(gè)來(lái)自客戶(hù)端的任務(wù)傅瞻。
Docker服務(wù)端默認(rèn)監(jiān)聽(tīng)本地的unix:///var/run/docker.sock套接字踢代,只允許本地的root用戶(hù)或docker用戶(hù)組成員訪問(wèn)⌒峤荆可以通過(guò)-H選項(xiàng)來(lái)修改監(jiān)聽(tīng)的方式胳挎。
例如,讓服務(wù)端監(jiān)聽(tīng)本地的TCP連接1234端口溺森,如下所示:
$ docker daemon -H 0.0.0.0:1234
此外慕爬,Docker還支持通過(guò)HTTPS認(rèn)證方式來(lái)驗(yàn)證訪問(wèn)。
Debian/Ubuntu 14.04等使用upstart管理啟動(dòng)服務(wù)的系統(tǒng)中屏积,Docker服務(wù)端的默認(rèn)啟動(dòng)配置文件在/etc/default/docker医窿。對(duì)于使用systemd管理啟動(dòng)服務(wù)的系統(tǒng),配置文件在/etc/systemd/system/docker.service.d/docker.conf炊林。
2.客戶(hù)端
Docker客戶(hù)端為用戶(hù)提供一系列可執(zhí)行命令姥卢,用戶(hù)用這些命令與Docker Daemon交互。
用戶(hù)使用的Docker可執(zhí)行命令即為客戶(hù)端程序渣聚。與Docker Daemon不同的是独榴,客戶(hù)端發(fā)送命令后,等待服務(wù)端返回饵逐,一旦收到返回后括眠,客戶(hù)端立刻執(zhí)行結(jié)束并退出。用戶(hù)執(zhí)行新的命令倍权,需要再次調(diào)用客戶(hù)端命令掷豺。同樣捞烟,客戶(hù)端默認(rèn)通過(guò)本地的unix:///var/run/docker.sock套接字向服務(wù)端發(fā)送命令。如果服務(wù)端沒(méi)有監(jiān)聽(tīng)在默認(rèn)的地址当船,則需要客戶(hù)端在執(zhí)行命令的時(shí)候顯式指定服務(wù)端地址题画。
例如,假定服務(wù)端監(jiān)聽(tīng)在本地的TCP連接1234端口tcp://127.0.0.1:1234德频,只有通過(guò)-H參數(shù)指定了正確的地址信息才能連接到服務(wù)端苍息,如下所示:
$ docker version
Client:
Version: 1.12.0
API version: 1.24
Go version: go1.6.3
Git commit: 8eab29e
Built: Thu Sep 28 22:00:36 2016
OS/Arch: linux/amd64
Cannot connect to the Docker daemon. Is the docker daemon running on this host?
$ docker -H tcp://127.0.0.1:1234 version
Client:
Version: 1.12.0
API version: 1.24
Go version: go1.6.3
Git commit: 8eab29e
Built: Thu Sep 28 22:00:36 2016
OS/Arch: linux/amd64
Server:
Version: 1.12.0
API version: 1.24
Go version: go1.6.3
Git commit: 8eab29e
Built: Thu Sep 28 22:00:36 2016
OS/Arch: linux/amd64
3.新的架構(gòu)設(shè)計(jì)
C/S架構(gòu)給Docker基本功能的實(shí)現(xiàn)帶來(lái)了許多便利,但同時(shí)也引入了一些限制壹置。
使用Docker時(shí)竞思,必須要啟動(dòng)并保持Docker Daemon的正常運(yùn)行,它既要管理容器的運(yùn)行時(shí)钞护,又要負(fù)責(zé)提供對(duì)外部API的響應(yīng)盖喷。而一旦Docker Daemon服務(wù)不正常,則已經(jīng)運(yùn)行在Docker主機(jī)上的容器也往往無(wú)法繼續(xù)使用难咕。
Docker團(tuán)隊(duì)已經(jīng)意識(shí)到了這個(gè)問(wèn)題课梳,在較新的版本(1.11.0+)中,開(kāi)始將維護(hù)容器運(yùn)行的任務(wù)放到一個(gè)單獨(dú)的組件containerd中來(lái)管理余佃,并且支持OCI的runc規(guī)范暮刃。原先的對(duì)客戶(hù)端API的支持則仍然放在Docker Daemon,通過(guò)解耦爆土,大大減少了對(duì)Docker Daemon的依賴(lài)椭懊。同時(shí),新的架構(gòu)提高了啟動(dòng)容器的速度步势,一項(xiàng)測(cè)試表明灾搏,可以達(dá)到每秒啟動(dòng)超過(guò)100個(gè)容器。
命名空間
命名空間(namespace)是Linux內(nèi)核的一個(gè)強(qiáng)大特性立润,為容器虛擬化的實(shí)現(xiàn)帶來(lái)極大便利。
利用這一特性媳板,每個(gè)容器都可以擁有自己?jiǎn)为?dú)的命名空間桑腮,運(yùn)行在其中的應(yīng)用都像是在獨(dú)立的操作系統(tǒng)環(huán)境中一樣。命名空間機(jī)制保證了容器之間彼此互不影響蛉幸。
在操作系統(tǒng)中破讨,包括內(nèi)核、文件系統(tǒng)奕纫、網(wǎng)絡(luò)提陶、PID、UID匹层、IPC隙笆、內(nèi)存锌蓄、硬盤(pán)、CPU等資源撑柔,所有的資源都是應(yīng)用進(jìn)程直接共享的瘸爽。要想實(shí)現(xiàn)虛擬化,除了要實(shí)現(xiàn)對(duì)內(nèi)存铅忿、CPU、網(wǎng)絡(luò)IO、硬盤(pán)IO垒酬、存儲(chǔ)空間等的限制外埂软,還要實(shí)現(xiàn)文件系統(tǒng)、網(wǎng)絡(luò)峻凫、PID渗鬼、UID、IPC等的相互隔離蔚晨。前者相對(duì)容易實(shí)現(xiàn)一些乍钻,后者則需要宿主主機(jī)系統(tǒng)的深入支持。
隨著Linux系統(tǒng)對(duì)于命名空間功能的逐步完善铭腕,現(xiàn)在已經(jīng)可以實(shí)現(xiàn)這些需求银择,讓進(jìn)程在彼此隔離的命名空間中運(yùn)行。雖然這些進(jìn)程仍在共用同一個(gè)內(nèi)核和某些運(yùn)行時(shí)環(huán)境(runtime累舷,例如一些系統(tǒng)命令和系統(tǒng)庫(kù))浩考,但是彼此是不可見(jiàn)的,并且認(rèn)為自己是獨(dú)占系統(tǒng)的被盈。
1.進(jìn)程命名空間
Linux通過(guò)命名空間管理進(jìn)程號(hào)析孽,對(duì)于同一進(jìn)程(即同一個(gè)task_struct),在不同的命名空間中只怎,看到的進(jìn)程號(hào)不相同袜瞬,每個(gè)進(jìn)程命名空間有一套自己的進(jìn)程號(hào)管理方法。進(jìn)程命名空間是一個(gè)父子關(guān)系的結(jié)構(gòu)身堡,子空間中的進(jìn)程對(duì)于父空間是可見(jiàn)的邓尤。新fork出的進(jìn)程在父命名空間和子命名空間將分別有一個(gè)進(jìn)程號(hào)來(lái)對(duì)應(yīng)。
例如贴谎,查看Docker主進(jìn)程的pid進(jìn)程號(hào)是5989汞扎,如下所示:
$ ps -ef |grep docker
root 5989 5988 0 14:38 pts/6 00:00:00 docker -d
新建一個(gè)Ubuntu的“hello world”容器:
$ docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world;sleep 1; done"
ec559327572b5bf99d0f80b98ed3a3b62023844c7fdbea3f8caed4ffa5c62e86
查看新建容器進(jìn)程的父進(jìn)程,正是Docker主進(jìn)程5989:
$ ps -ef |grep while
root 6126 5989 0 14:41 ? 00:00:00 /bin/sh -c while true; do echo hello world; sleep 1; done
2.網(wǎng)絡(luò)命名空間
如果有了pid命名空間擅这,那么每個(gè)命名空間中的進(jìn)程就可以相互隔離澈魄,但是網(wǎng)絡(luò)端口還是共享本地系統(tǒng)的端口。
通過(guò)網(wǎng)絡(luò)命名空間仲翎,可以實(shí)現(xiàn)網(wǎng)絡(luò)隔離痹扇。網(wǎng)絡(luò)命名空間為進(jìn)程提供了一個(gè)完全獨(dú)立的網(wǎng)絡(luò)協(xié)議棧的視圖铛漓,包括網(wǎng)絡(luò)設(shè)備接口、IPv4和IPv6協(xié)議棧帘营、IP路由表票渠、防火墻規(guī)則、sockets等芬迄,這樣每個(gè)容器的網(wǎng)絡(luò)就能隔離開(kāi)來(lái)问顷。Docker采用虛擬網(wǎng)絡(luò)設(shè)備(Virtual Network Device)的方式,將不同命名空間的網(wǎng)絡(luò)設(shè)備連接到一起禀梳。默認(rèn)情況下杜窄,容器中的虛擬網(wǎng)卡將同本地主機(jī)上的docker0網(wǎng)橋連接在一起。
使用brctl工具可以看到橋接到宿主主機(jī)docker0網(wǎng)橋上的虛擬網(wǎng)口:
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.56847afe9799 no veth4148 vethd166 vethd533
3.IPC命名空間
容器中進(jìn)程交互還是采用了Linux常見(jiàn)的進(jìn)程間交互方法(Interprocess Communication算途,IPC)塞耕,包括信號(hào)量、消息隊(duì)列和共享內(nèi)存等嘴瓤。PID Namespace和IPC Namespace可以組合起來(lái)一起使用扫外,同一個(gè)IPC命名空間內(nèi)的進(jìn)程可以彼此可見(jiàn),允許進(jìn)行交互廓脆;不同空間的進(jìn)程則無(wú)法交互筛谚。
4.掛載命名空間
類(lèi)似于chroot,將一個(gè)進(jìn)程放到一個(gè)特定的目錄執(zhí)行停忿。掛載命名空間允許不同命名空間的進(jìn)程看到的文件結(jié)構(gòu)不同驾讲,這樣每個(gè)命名空間中的進(jìn)程所看到的文件目錄彼此被隔離。
5.UTS命名空間
UTS(UNIX Time-sharing System)命名空間允許每個(gè)容器擁有獨(dú)立的主機(jī)名和域名席赂,從而可以虛擬出一個(gè)有獨(dú)立主機(jī)名和網(wǎng)絡(luò)空間的環(huán)境吮铭,就跟網(wǎng)絡(luò)上一臺(tái)獨(dú)立的主機(jī)一樣。默認(rèn)情況下颅停,Docker容器的主機(jī)名就是返回的容器ID:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ec559327572b ubuntu:14.04 /bin/sh -c 'while tr 18 minutes ago Up 18 minutes furious_goodall
$ docker inspect -f {{".Config.Hostname"}} ec5
ec559327572b
6.用戶(hù)命名空間
每個(gè)容器可以有不同的用戶(hù)和組id谓晌,也就是說(shuō)可以在容器內(nèi)使用特定的內(nèi)部用戶(hù)執(zhí)行程序,而非本地系統(tǒng)上存在的用戶(hù)癞揉。
每個(gè)容器內(nèi)部都可以有root帳號(hào)扎谎,但跟宿主主機(jī)不在一個(gè)命名空間。
通過(guò)使用隔離的用戶(hù)命名空間可以提高安全性烧董,避免容器內(nèi)進(jìn)程獲取到額外的權(quán)限。
控制組
控制組(CGroups)是Linux內(nèi)核的一個(gè)特性胧奔,主要用來(lái)對(duì)共享資源進(jìn)行隔離逊移、限制、審計(jì)等龙填。只有能控制分配到容器的資源胳泉,才能避免多個(gè)容器同時(shí)運(yùn)行時(shí)對(duì)宿主機(jī)系統(tǒng)的資源競(jìng)爭(zhēng)拐叉。控制組可以提供對(duì)容器的內(nèi)存扇商、CPU凤瘦、磁盤(pán)IO等資源進(jìn)行限制和計(jì)費(fèi)管理“钙蹋控制組的設(shè)計(jì)目標(biāo)是為不同的應(yīng)用情況提供統(tǒng)一的接口蔬芥,從控制單一進(jìn)程(比如nice工具)到系統(tǒng)級(jí)虛擬化(包括OpenVZ、Linux-VServer控汉、LXC等)笔诵。
具體來(lái)看,控制組提供:
- 資源限制(Resource limiting):可以將組設(shè)置為不超過(guò)設(shè)定的內(nèi)存限制姑子。比如:內(nèi)存子系統(tǒng)可以為進(jìn)程組設(shè)定一個(gè)內(nèi)存使用上限乎婿,一旦進(jìn)程組使用的內(nèi)存達(dá)到限額再申請(qǐng)內(nèi)存,就會(huì)出發(fā)Out of Memory警告街佑。
- 優(yōu)先級(jí)(Prioritization):通過(guò)優(yōu)先級(jí)讓一些組優(yōu)先得到更多的CPU等資源谢翎。
- 資源審計(jì)(Accounting):用來(lái)統(tǒng)計(jì)系統(tǒng)實(shí)際上把多少資源用到適合的目的上,可以使用cpuacct子系統(tǒng)記錄某個(gè)進(jìn)程組使用的CPU時(shí)間沐旨。
- 隔離(isolation):為組隔離命名空間森逮,這樣一個(gè)組不會(huì)看到另一個(gè)組的進(jìn)程、網(wǎng)絡(luò)連接和文件系統(tǒng)希俩。
- 控制(Control):掛起吊宋、恢復(fù)和重啟動(dòng)等操作。
安裝Docker后颜武,用戶(hù)可以在/sys/fs/cgroup/memory/docker/目錄下看到對(duì)Docker組應(yīng)用的各種限制項(xiàng)璃搜,包括:
$ cd /sys/fs/cgroup/memory/docker
$ ls
用戶(hù)可以通過(guò)修改這些文件值來(lái)控制組限制Docker應(yīng)用資源。
例如鳞上,通過(guò)下面的命令可限制Docker組中所有進(jìn)程使用的物理內(nèi)存總量不超過(guò)100MB:
$ sudo echo 104857600 >/sys/fs/cgroup/memory/docker/memory.limit_in_bytes
進(jìn)入對(duì)應(yīng)的容器文件夾这吻,可以看到對(duì)應(yīng)容器的一些狀態(tài):
$ cd 42352bb6c1d1c5c411be8fa04e97842da87d14623495189c4d865dfc444d12ae/
$ ls
$ cat memory.stat
在開(kāi)發(fā)容器工具時(shí),往往需要一些容器運(yùn)行狀態(tài)數(shù)據(jù)篙议,這時(shí)就可以從這里得到更多的信息唾糯。
可以在創(chuàng)建或啟動(dòng)容器時(shí)為每個(gè)容器指定資源的限制,例如使用-c|--cpu-shares[=0]參數(shù)來(lái)調(diào)整容器使用CPU的權(quán)重鬼贱;使用-m|--memory[=MEMORY]參數(shù)來(lái)調(diào)整容器使用內(nèi)存的大小移怯。
聯(lián)合文件系統(tǒng)
聯(lián)合文件系統(tǒng)(UnionFS)是一種輕量級(jí)的高性能分層文件系統(tǒng),它支持將文件系統(tǒng)中的修改信息作為一次提交这难,并層層疊加舟误,同時(shí)可以將不同目錄掛載到同一個(gè)虛擬文件系統(tǒng)下,應(yīng)用看到的是掛載的最終結(jié)果姻乓。
聯(lián)合文件系統(tǒng)是實(shí)現(xiàn)Docker鏡像的技術(shù)基礎(chǔ)嵌溢。Docker鏡像可以通過(guò)分層來(lái)進(jìn)行繼承眯牧。例如,用戶(hù)基于基礎(chǔ)鏡像(用來(lái)生成其他鏡像的基礎(chǔ)赖草,往往沒(méi)有父鏡像)來(lái)制作各種不同的應(yīng)用鏡像学少。這些鏡像共享同一個(gè)基礎(chǔ)鏡像層,提高了存儲(chǔ)效率秧骑。此外版确,當(dāng)用戶(hù)改變了一個(gè)Docker鏡像(比如升級(jí)程序到新的版本),則會(huì)創(chuàng)建一個(gè)新的層(layer)腿堤。因此阀坏,用戶(hù)不用替換整個(gè)原鏡像或者重新建立,只需要添加新層即可笆檀。用戶(hù)分發(fā)鏡像的時(shí)候忌堂,也只需要分發(fā)被改動(dòng)的新層內(nèi)容(增量部分)。這讓Docker的鏡像管理變得十分輕量級(jí)和快速酗洒。
1.Docker存儲(chǔ)
Docker目前通過(guò)插件化方式支持多種文件系統(tǒng)后端士修。Debian/Ubuntu上成熟的AUFS(Another Union File System,或v2版本往后的Advanced Multilayered Unification File System)樱衷,就是一種聯(lián)合文件系統(tǒng)實(shí)現(xiàn)棋嘲。AUFS支持為每一個(gè)成員目錄(類(lèi)似Git的分支)設(shè)定只讀(readonly)、讀寫(xiě)(readwrite)或?qū)懗觯╳hiteout-able)權(quán)限矩桂,同時(shí)AUFS里有一個(gè)類(lèi)似分層的概念沸移,對(duì)只讀權(quán)限的分支可以在邏輯上進(jìn)行增量地修改(不影響只讀部分的)。
Docker鏡像自身就是由多個(gè)文件層組成侄榴,每一層有唯一的編號(hào)(層ID)雹锣。
可以通過(guò)docker history查看一個(gè)鏡像由哪些層組成。例如查看ubuntu:14.04鏡像由4層組成癞蚕,每層執(zhí)行了不同的命令:
$ docker history ubuntu:14.04
IMAGE CREATED CREATED BY SIZE COMMENT
2a274e3405ec 13 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B
df697c8b1bf4 13 months ago /bin/sh -c sed -i 's/^#\s(deb.universe)$/ 1.895 kB
371166fb96e0 13 months ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic 194.5 kB
69191ca023af 13 months ago /bin/sh -c #(nop) ADD file:c8f078961a543cdefa 188.1 MB
對(duì)于Docker鏡像來(lái)說(shuō)蕊爵,這些層的內(nèi)容都是不可修改的、只讀的桦山。而當(dāng)Docker利用鏡像啟動(dòng)一個(gè)容器時(shí)攒射,將在鏡像文件系統(tǒng)的最頂端再掛載一個(gè)新的可讀寫(xiě)的層給容器。容器中的內(nèi)容更新將會(huì)發(fā)生在可讀寫(xiě)層恒水。當(dāng)所操作對(duì)象位于較深的某層時(shí)会放,需要先復(fù)制到最上層的可讀寫(xiě)層。當(dāng)數(shù)據(jù)對(duì)象較大時(shí)钉凌,往往意味著IO性能較差咧最。因此,一般推薦將容器修改的數(shù)據(jù)通過(guò)volume方式掛載,而不是直接修改鏡像內(nèi)數(shù)據(jù)窗市。此外,對(duì)于頻繁啟停Docker容器的場(chǎng)景下饮笛,文件系統(tǒng)的IO性能也將十分關(guān)鍵咨察。具體看,Docker所有的存儲(chǔ)都在Docker目錄下福青,
以Ubuntu系統(tǒng)為例摄狱,使用AUFS,默認(rèn)路徑是/var/lib/docker
在這個(gè)目錄下面无午,存儲(chǔ)由Docker鏡像和容器運(yùn)行相關(guān)的文件和目錄媒役,可能包括aufs、containers宪迟、graph酣衷、image、init次泽、linkgraph.db穿仪、network、repositories-aufs意荤、swarm啊片、tmp、trust玖像、volumes等紫谷。
最關(guān)鍵的就是aufs目錄,這是aufs文件系統(tǒng)所在捐寥,保存Docker鏡像相關(guān)數(shù)據(jù)和信息笤昨。該目錄包括layers、diff和mnt三個(gè)子目錄上真。1.9版本和之前的版本中咬腋,命名跟鏡像層的ID是匹配的,而自1.10開(kāi)始睡互,層數(shù)據(jù)相關(guān)的文件和目錄名與層ID不再匹配根竿。
layers子目錄包含層屬性文件,用來(lái)保存各個(gè)鏡像層的元數(shù)據(jù):某鏡像的某層下面包括哪些層就珠。
例如:某鏡像由5層組成寇壳,則文件內(nèi)容應(yīng)該如下:
# cat aufs/layers/78f4601eee00b1f770b1aecf5b6433635b99caa5c11b8858dd6c8cec03b4584f-init
d2a0ecffe6fa4ef3de9646a75cc629bbd9da7eead7f767cb810f9808d6b3ecb6
29460ac934423a55802fcad24856827050697b4a9f33550bd93c82762fb6db8f
b670fb0c7ecd3d2c401fbfd1fa4d7a872fbada0a4b8c2516d0be18911c6b25d6
83e4dde6b9cfddf46b75a07ec8d65ad87a748b98cf27de7d5b3298c1f3455ae4
diff子目錄包含層內(nèi)容子目錄,用來(lái)保存所有鏡像層的內(nèi)容數(shù)據(jù)妻怎。
例如:# ls aufs/diff/78f4601eee00b1f770b1aecf5b6433635b99caa5c11b8858dd6c8cec03b4584f-init/
dev etc
mnt子目錄下面的子目錄是各個(gè)容器最終的掛載點(diǎn)壳炎,所有相關(guān)的AUFS層在這里掛載到一起,形成最終效果。一個(gè)運(yùn)行中容器的根文件系統(tǒng)就掛載在這下面的子目錄上匿辩。同樣腰耙,1.10版本之前的Docker中,子目錄名和容器ID是一致的铲球。其中挺庞,還包括容器的元數(shù)據(jù)、配置文件和運(yùn)行日志等稼病。
以centos7為例选侨,使用overlay2
1.1 overlay原理
OverlayFS將單個(gè)Linux主機(jī)上的兩個(gè)目錄合并成一個(gè)目錄。這些目錄被稱(chēng)為層然走,統(tǒng)一過(guò)程被稱(chēng)為聯(lián)合掛載援制。OverlayFS底層目錄稱(chēng)為lowerdir, 高層目錄稱(chēng)為upperdir芍瑞。合并統(tǒng)一視圖稱(chēng)為merged晨仑。
下圖分層圖,鏡像層是lowdir啄巧,容器層是upperdir寻歧,統(tǒng)一的視圖層是merged層
當(dāng)鏡像層和容器層都有相同的文件,使用容器層的文件秩仆,overlay驅(qū)動(dòng)使用兩層码泛,這就意味著,如果是多層的鏡像就無(wú)法使用了澄耍,替代的方案是:
鏡像層都在/var/lib/docker/overlay目錄下噪珊,通過(guò)硬鏈接的方式把下部的層關(guān)聯(lián)起來(lái)
Docker1.10之后,鏡像層ID和/var/lib/docker中的目錄名不再一一對(duì)應(yīng)齐莲。
- lower-id是容器鏡像頂層的ID痢站,Ove??rlayFS lowerdir
- upper包含與OverlayFS相對(duì)應(yīng)的容器的讀寫(xiě)層的內(nèi)容upperdir。
- merged目錄是lowerdir聯(lián)合裝載upperdir选酗,包含正在運(yùn)行的容器內(nèi)的文件系統(tǒng)的視圖阵难。
- work目錄是OverlayFS內(nèi)部的。
2.多種文件系統(tǒng)比較
Docker目前支持的聯(lián)合文件系統(tǒng)種類(lèi)包括AUFS芒填、OverlayFS呜叫、btrfs、vfs殿衰、zfs和Device Mapper等朱庆。
各種文件系統(tǒng)目前的支持情況如下:
- AUFS:最早支持的文件系統(tǒng),對(duì)Debian/Ubuntu支持好闷祥,雖然沒(méi)有合并到Linux內(nèi)核中娱颊,但成熟度很高;
- OverlayFS:類(lèi)似于AUFS,性能更好一些箱硕,已經(jīng)合并到內(nèi)核拴竹,未來(lái)會(huì)取代AUFS,但成熟度有待提高剧罩;
- Device Mapper:Redhat公司和Docker團(tuán)隊(duì)一起開(kāi)發(fā)用于支持RHEL的文件系統(tǒng)殖熟,內(nèi)核支持,性能略慢斑响,成熟度高;
- btrfs:參考zfs等特性設(shè)計(jì)的文件系統(tǒng)钳榨,由Linux社區(qū)開(kāi)發(fā)舰罚,試圖未來(lái)取代Device Mapper,成熟度有待提高薛耻;
- vfs:基于普通文件系統(tǒng)(ext营罢、nfs等)的中間層抽象,性能差饼齿,比較占用空間饲漾,成熟度也一般。
- zfs:最初設(shè)計(jì)為Solaris 10上的寫(xiě)時(shí)文件系統(tǒng)缕溉,擁有不少好的特性考传,但對(duì)Linux支持還不夠成熟。
總結(jié)一下证鸥,AUFS和Device Mapper的應(yīng)用最為廣泛僚楞,支持也相對(duì)成熟,推薦生產(chǎn)環(huán)境考慮枉层。長(zhǎng)期來(lái)看泉褐,OverlayFS將可能具有更好的特性。
Linux網(wǎng)絡(luò)虛擬化
Docker的本地網(wǎng)絡(luò)實(shí)現(xiàn)其實(shí)就是利用了Linux上的網(wǎng)絡(luò)命名空間和虛擬網(wǎng)絡(luò)設(shè)備(特別是veth pair)鸟蜡。
1.基本原理
直觀上看膜赃,要實(shí)現(xiàn)網(wǎng)絡(luò)通信,機(jī)器需要至少一個(gè)網(wǎng)絡(luò)接口(物理接口或虛擬接口)與外界相通揉忘,并可以收發(fā)數(shù)據(jù)包跳座;此外,如果不同子網(wǎng)之間要進(jìn)行通信癌淮,需要額外的路由機(jī)制躺坟。
Docker中的網(wǎng)絡(luò)接口默認(rèn)都是虛擬的接口。虛擬接口的最大優(yōu)勢(shì)就是轉(zhuǎn)發(fā)效率極高乳蓄。這是因?yàn)長(zhǎng)inux通過(guò)在內(nèi)核中進(jìn)行數(shù)據(jù)復(fù)制來(lái)實(shí)現(xiàn)虛擬接口之間的數(shù)據(jù)轉(zhuǎn)發(fā)咪橙,即發(fā)送接口的發(fā)送緩存中的數(shù)據(jù)包將被直接復(fù)制到接收接口的接收緩存中,而無(wú)需通過(guò)外部物理網(wǎng)絡(luò)設(shè)備進(jìn)行交換。對(duì)于本地系統(tǒng)和容器內(nèi)系統(tǒng)來(lái)看美侦,虛擬接口跟一個(gè)正常的以太網(wǎng)卡相比并無(wú)區(qū)別产舞,只是它速度要快得多。
Docker容器網(wǎng)絡(luò)就很好地利用了Linux虛擬網(wǎng)絡(luò)技術(shù)菠剩,在本地主機(jī)和容器內(nèi)分別創(chuàng)建一個(gè)虛擬接口易猫,并讓它們彼此連通(這樣的一對(duì)接口叫做veth pair)。
一般情況下具壮,Docker創(chuàng)建一個(gè)容器的時(shí)候准颓,會(huì)具體執(zhí)行如下操作:
- 創(chuàng)建一對(duì)虛擬接口,分別放到本地主機(jī)和新容器的命名空間中棺妓;
- 本地主機(jī)一端的虛擬接口連接到默認(rèn)的docker0網(wǎng)橋或指定網(wǎng)橋上攘已,并具有一個(gè)以veth開(kāi)頭的唯一名字,如veth1234怜跑;
- 容器一端的虛擬接口將放到新創(chuàng)建的容器中样勃,并修改名字作為eth0。這個(gè)接口只在容器的命名空間可見(jiàn)性芬;
- 從網(wǎng)橋可用地址段中獲取一個(gè)空閑地址分配給容器的eth0(例如172.17.0.2/16)峡眶,并配置默認(rèn)路由網(wǎng)關(guān)為docker0網(wǎng)卡的內(nèi)部接口docker0的IP地址(例如172.17.42.1/16)。
完成這些之后植锉,容器就可以使用它所能看到的eth0虛擬網(wǎng)卡來(lái)連接其他容器和訪問(wèn)外部網(wǎng)絡(luò)辫樱。用戶(hù)也可以通過(guò)docker network命令來(lái)手動(dòng)管理網(wǎng)絡(luò)。
在使用docker run命令啟動(dòng)容器的時(shí)候俊庇,可以通過(guò)--net參數(shù)來(lái)指定容器的網(wǎng)絡(luò)配置搏熄。
有5個(gè)可選值bridge、none暇赤、container心例、host和用戶(hù)定義的網(wǎng)絡(luò):
- --net=bridge:默認(rèn)值,在Docker網(wǎng)橋docker0上為容器創(chuàng)建新的網(wǎng)絡(luò)棧鞋囊。
- --net=none:讓Docker將新容器放到隔離的網(wǎng)絡(luò)棧中止后,但是不進(jìn)行網(wǎng)絡(luò)配置。之后溜腐,用戶(hù)可以自行進(jìn)行配置译株。
- --net=container:NAME_or_ID:讓Docker將新建容器的進(jìn)程放到一個(gè)已存在容器的網(wǎng)絡(luò)棧中,新容器進(jìn)程有自己的文件系統(tǒng)挺益、進(jìn)程列表和資源限制歉糜,但會(huì)和已存在的容器共享IP地址和端口等網(wǎng)絡(luò)資源,兩者進(jìn)程可以直接通過(guò)lo環(huán)回接口通信望众。
- --net=host:告訴Docker不要將容器網(wǎng)絡(luò)放到隔離的命名空間中匪补,即不要容器化容器內(nèi)的網(wǎng)絡(luò)伞辛。此時(shí)容器使用本地主機(jī)的網(wǎng)絡(luò),它擁有完全的本地主機(jī)接口訪問(wèn)權(quán)限夯缺。容器進(jìn)程可以跟主機(jī)其他root進(jìn)程一樣打開(kāi)低范圍的端口蚤氏,可以訪問(wèn)本地網(wǎng)絡(luò)服務(wù),比如D-bus踊兜,還可以讓容器做一些影響整個(gè)主機(jī)系統(tǒng)的事情竿滨,比如重啟主機(jī)。因此使用這個(gè)選項(xiàng)的時(shí)候要非常小心捏境。如果進(jìn)一步的使用--privileged=true參數(shù)于游,容器甚至?xí)辉试S直接配置主機(jī)的網(wǎng)絡(luò)棧。
- --net=user_defined_network:用戶(hù)自行用network相關(guān)命令創(chuàng)建一個(gè)網(wǎng)絡(luò)垫言,通過(guò)這種方式將容器連接到指定的已創(chuàng)建網(wǎng)絡(luò)上去曙砂。
3.手動(dòng)配置網(wǎng)絡(luò)
用戶(hù)使用--net=none后,Docker將不對(duì)容器網(wǎng)絡(luò)進(jìn)行配置骏掀。
下面,將手動(dòng)完成配置網(wǎng)絡(luò)的整個(gè)過(guò)程柱告。
首先截驮,啟動(dòng)一個(gè)/bin/bash容器,指定--net=none參數(shù):
$ docker run -i -t --rm --net=none base /bin/bash
root@63f36fc01b5f:/#
在本地主機(jī)查找容器的進(jìn)程id际度,并為它創(chuàng)建網(wǎng)絡(luò)命名空間:
$ docker inspect -f '{{.State.Pid}}' 63f36fc01b5f
2778
$ pid=2778
$ sudo mkdir -p /var/run/netns
pid/ns/net /var/run/netns/$pid
檢查橋接網(wǎng)卡的IP和子網(wǎng)掩碼信息:
$ ip addr show docker0
21: docker0: ...
inet 172.17.42.1/16 scope global docker0
...
創(chuàng)建一對(duì)“veth pair”接口A和B葵袭,綁定A接口到網(wǎng)橋docker0,并啟用它:
$ sudo ip link add A type veth peer name B
$ sudo brctl addif docker0 A
$ sudo ip link set A up
將B接口放到容器的網(wǎng)絡(luò)命名空間乖菱,命名為eth0坡锡,啟動(dòng)它并配置一個(gè)可用IP(橋接網(wǎng)段)和默認(rèn)網(wǎng)關(guān):
pid
pid ip link set dev B name eth0
pid ip link set eth0 up
pid ip addr add 172.17.42.99/16 dev eth0
pid ip route add default via 172.17.42.1
以上,就是Docker配置網(wǎng)絡(luò)的具體過(guò)程窒所。
當(dāng)容器終止后鹉勒,Docker會(huì)清空容器,容器內(nèi)的網(wǎng)絡(luò)接口會(huì)隨網(wǎng)絡(luò)命名空間一起被清除吵取,A接口也會(huì)自動(dòng)從docker0卸載并清除禽额。
此外,在刪除/var/run/netns/下的內(nèi)容之前皮官,用戶(hù)可以使用ip netns exec命令在指定網(wǎng)絡(luò)命名空間中進(jìn)行配置脯倒,從而更新容器內(nèi)的網(wǎng)絡(luò)配置。