1. 容器
1.1 定義
一種沙盒技術(shù),
- 可以將應(yīng)用運(yùn)行在其中,與外界隔離
- 這個(gè)沙盒可以被方便地“轉(zhuǎn)移”。
本質(zhì)上,他就是一種特殊的進(jìn)程。通過在創(chuàng)建容器進(jìn)程的時(shí)候,指定了這個(gè)進(jìn)程所需要啟用的一組Namespace參數(shù),進(jìn)而讓該容器進(jìn)程只能看到當(dāng)前Namespace所限定的資源、文件、設(shè)備、狀態(tài)或者配置。--進(jìn)程劃分獨(dú)立空間思想
一個(gè)正在運(yùn)行的Linux容器,可以被一分為二地看待:
- 一組聯(lián)合掛載在/var/lib/docker/aufs/mnt上的rootfs,這部分被稱為“容器鏡像”(Container Image),是容器的靜態(tài)視圖
- 一個(gè)由Namespace + Cgroups構(gòu)成的隔離環(huán)境,這部分被稱為“容器運(yùn)行時(shí)”,是容器的動(dòng)態(tài)視圖
1.2 核心原理小結(jié)
在為待創(chuàng)建的用戶進(jìn)程
- 啟動(dòng)Linux Namespace配置
- 設(shè)置指定的Cgroups參數(shù)
- 切換進(jìn)程的根目錄(change root, 配合namespace mount)
切換進(jìn)程的根目錄的時(shí),Docker項(xiàng)目會(huì)在最后一步優(yōu)先使用pivot_root的系統(tǒng)調(diào)用,如果系統(tǒng)不支持,才會(huì)使用chroot。
2. 虛擬機(jī)
虛擬機(jī)在物理宿主機(jī)上存在一層Hypervisor的硬件虛擬化層,通過Hypervisor虛擬化出了CPU, 內(nèi)存,I/O設(shè)備等,然后在這些虛擬設(shè)備上裝了一個(gè)新的操作系統(tǒng),即GuestOS,通過不同的操作系統(tǒng),來(lái)實(shí)現(xiàn)同一個(gè)物理宿主機(jī)上的環(huán)境隔離。
容器和虛擬機(jī)的區(qū)別
| # | 容器 | 虛擬機(jī) |
|---|---|---|
| 關(guān)鍵技術(shù) | Docker Engine | Hypervisor,GuestOS |
| 實(shí)現(xiàn)原理 | Docker Engine通過在原有物理宿主機(jī)上的原操作系統(tǒng),在起Docker進(jìn)程的時(shí)候,通過指定各種namespace參數(shù)實(shí)現(xiàn)了不同Docker容器(進(jìn)程)之間的進(jìn)程隔離 | Hypervisor通過虛擬化出硬件設(shè)備,在硬件設(shè)備上安裝若干個(gè)GuestOS,通過操作系統(tǒng),實(shí)現(xiàn)不同操作系統(tǒng)內(nèi)的進(jìn)程互相不感知 |
| 本質(zhì) | 不同的Docker容器本質(zhì)上是物理宿主機(jī)上的操作系統(tǒng)中運(yùn)行的不同進(jìn)程 | 不同的虛擬機(jī)本質(zhì)上是Hypervisor虛擬化出來(lái)硬件上安裝的不同的操作系統(tǒng) |
| 優(yōu)劣 | GuestOS會(huì)帶來(lái)很大的性能消耗:1. 不做優(yōu)化的場(chǎng)景,根據(jù)實(shí)驗(yàn)一個(gè)運(yùn)行CentOS的KVM VM啟動(dòng)之后VM自身占用100~200M內(nèi)存 2. VM內(nèi)部用戶對(duì)宿主機(jī)操作系統(tǒng)的調(diào)用必須經(jīng)過Hypervisor這一層的攔截和處理,會(huì)對(duì)計(jì)算資源、網(wǎng)絡(luò)和磁盤I/O的造成很大損耗 | 不需要虛擬化層導(dǎo)致“敏捷”“高性能”是容器相比與虛擬機(jī)的最大優(yōu)勢(shì)。但是正因?yàn)槿绱耍珼ocker容器對(duì)應(yīng)用進(jìn)程隔離的不如通過Hypervisor的虛擬化工具進(jìn)行的隔離要徹底。如1. 同一個(gè)宿主機(jī)上的不同Docker容器還是共享的同一個(gè)操作系統(tǒng)內(nèi)核 2. Linux內(nèi)核中,很多資源和對(duì)象是不能被NameSpace化的,比如時(shí)間,如果調(diào)用系統(tǒng)方法修改系統(tǒng)時(shí)間,那么所有Docker容器都可以感知到---定制化安全加固方案:通過Seccomp技術(shù),對(duì)容器內(nèi)部發(fā)起的系統(tǒng)調(diào)用進(jìn)行過濾來(lái)進(jìn)行安全加固,但是犧牲了一定的性能,且這種定制不具備普適性 |
因此,不能Docker engine放在和Hypervisor同樣的地位,Docker Engine或任何容器管理工具都不對(duì)容器中的應(yīng)用進(jìn)程的隔離環(huán)境負(fù)責(zé),而是由物理宿主機(jī)的操作系統(tǒng)本身直接負(fù)責(zé)的。Hypervisor是會(huì)虛擬出硬件設(shè)備,在這上面通過不同的GuestOS實(shí)現(xiàn)隔離的。而Docker Engine在這里扮演的角色,更多是的旁路式的輔助和管理工作。
那么如何在容器的隔離和性能之間做出平衡呢?
基于虛擬化和獨(dú)立內(nèi)核技術(shù)的容器

3. Docker Engine是做什么的?
#TODO
4. 容器編排
5. 集群管理
#TODO
6. 進(jìn)程
6.1 定義
- 狹義定義:進(jìn)程是正在運(yùn)行的程序的實(shí)例(an instance of a computer program that is being executed)。
- 廣義定義:進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合的一次運(yùn)行活動(dòng)。它是操作系統(tǒng)動(dòng)態(tài)執(zhí)行的基本單元,在傳統(tǒng)的操作系統(tǒng)中,進(jìn)程既是基本的分配單元,也是基本的執(zhí)行單元。
對(duì)進(jìn)程來(lái)說,靜態(tài)表現(xiàn)就是程序,未運(yùn)行時(shí)存儲(chǔ)在磁盤上;運(yùn)行起來(lái)后,變成了計(jì)算機(jī)中數(shù)據(jù)和狀態(tài)的總和,也是其動(dòng)態(tài)表現(xiàn)。
容器技術(shù)的核心功能,是通過約束和修改進(jìn)程的動(dòng)態(tài)表現(xiàn),從而為其制造出一個(gè)容器的邊界。
6.2 進(jìn)程和線程的區(qū)別
#TODO
7. Linux Cgroups(Linux Control Group)
7.1 解決問題
雖然Docker容器可以通過Namespace改變進(jìn)程視圖可見性,但是本質(zhì)上容器這個(gè)進(jìn)程及其內(nèi)部的應(yīng)用進(jìn)程,都是統(tǒng)一歸宿主機(jī)的操作系統(tǒng)管理,這就意味著,這個(gè)容器內(nèi)部的進(jìn)程和容器外部的宿主機(jī)的進(jìn)程時(shí)處于平等競(jìng)爭(zhēng)關(guān)系的。那么問題來(lái)了,這些資源占用怎么去隔離?---Cgroups
7.2 主要作用
限制一個(gè)進(jìn)程族能夠使用的資源上限,包括CPU,內(nèi)存,磁盤,網(wǎng)絡(luò)帶寬等。此外還能夠?qū)M(jìn)程進(jìn)行優(yōu)先級(jí)設(shè)置、審計(jì)、以及將進(jìn)程掛起和恢復(fù)等操作。
詳見《Linux Cgroups》
7.3 存在問題
容器中執(zhí)行top指令,顯示的信息是宿主機(jī)的CPU和內(nèi)存數(shù)據(jù),不是當(dāng)前容器的數(shù)據(jù)。因?yàn)?proc下的存儲(chǔ)的信息是作為top指令的主要數(shù)據(jù)來(lái)源,而/proc文件系統(tǒng)是感知不Cgroups限制的存在
8. Namespace技術(shù)
8.1 作用
修改進(jìn)程視圖的主要方法。說白了,容器正是通過在起容器進(jìn)程的時(shí)候,增加namespace的配置參數(shù),使得在該容器內(nèi)部可見的進(jìn)程與容器外部(物理機(jī)OS)上看到的進(jìn)程“隔離”開來(lái)。但是,在物理宿主機(jī)看來(lái)這些被“隔離”的進(jìn)程與其他進(jìn)程并沒有太大區(qū)別。
8.2 存在問題
namespace problem.PNG
這部分如何理解?
看容器本質(zhì)就好了,容器項(xiàng)目通過起用戶進(jìn)程時(shí)候限定cgroups和namespace并切換root來(lái)起了一個(gè)“沙盒”里的應(yīng)用進(jìn)程,所以通過一個(gè)容器項(xiàng)目起來(lái)的一個(gè)應(yīng)用進(jìn)程就是一個(gè)容器。
9. RPC(Remote Procedure Call)

簡(jiǎn)單理解:rpc調(diào)用能夠通過類似本地調(diào)用的方式實(shí)現(xiàn)在分布式架構(gòu)中Service A調(diào)用起Service B的方法。
在上圖中,以左邊的Client端為例,Application就是rpc的調(diào)用方,Client Stub就是代理對(duì)象,也就是那個(gè)看起來(lái)像是Calculator(這個(gè)類是實(shí)際實(shí)現(xiàn)在右側(cè)Application中的)的實(shí)現(xiàn)類,其實(shí)內(nèi)部是通過rpc方式來(lái)進(jìn)行遠(yuǎn)程調(diào)用的代理對(duì)象,至于Client Run-time Library,則是實(shí)現(xiàn)遠(yuǎn)程調(diào)用的工具包,比如jdk的Socket,最后通過底層網(wǎng)絡(luò)實(shí)現(xiàn)實(shí)現(xiàn)數(shù)據(jù)的傳輸。
這個(gè)過程中最重要的就是序列化和反序列化了,因?yàn)閿?shù)據(jù)傳輸?shù)臄?shù)據(jù)包必須是二進(jìn)制的,你直接丟一個(gè)Java對(duì)象過去,人家可不認(rèn)識(shí),你必須把Java對(duì)象序列化為二進(jìn)制格式,傳給Server端,Server端接收到之后,再反序列化為Java對(duì)象。
10. 容器鏡像(rootfs)
10.1 既然Docker容器是在物理宿主機(jī)上的進(jìn)程,那么Docker鏡像和物理宿主機(jī)的操作系統(tǒng)之間是什么關(guān)系?
可以簡(jiǎn)單這么理解
- 如果容器鏡像os支持某硬件的驅(qū)動(dòng),但是宿主機(jī)os如果不支持該硬件驅(qū)動(dòng)的話,也白搭
- 可以理解為鏡像只是提供了一套鏡像文件系統(tǒng)中的各種文件,而各種內(nèi)核相關(guān)的模塊或者特性支持,完全依賴于宿主機(jī)
也正是通過容器鏡像,打包了整個(gè)操作系統(tǒng)的文件和目錄(雖然沒打包內(nèi)核),也就意味著,應(yīng)用以及他所運(yùn)行所需要的全部依賴都被封裝在了一起,而對(duì)于一個(gè)應(yīng)用來(lái)說,操作系統(tǒng)本身才是他運(yùn)行所需要的完整的依賴庫(kù)。也正是因?yàn)檫@樣,才有了容器的一個(gè)重要特性:一致性。
一致性
優(yōu)雅地解決云端和本地服務(wù)環(huán)境不同造成在應(yīng)用打包過程中使用PaaS最“痛苦”的步驟。
11. 操作系統(tǒng)
11.1 鏡像和操作系統(tǒng)關(guān)系
#TODO
11.2 操作系統(tǒng)文件和操作系統(tǒng)內(nèi)核的區(qū)別
為什么說容器通過mount namespace掛載不同版本的操作系統(tǒng)文件,但是不同的Docker還是使用的操作系統(tǒng)內(nèi)核。(通俗理解就是不能通過容器技術(shù)在Windows宿主機(jī)上運(yùn)行Linux容器,或者在低版本的Linux宿主機(jī)上運(yùn)行高版本的Linux容器)
12. Copy-on-Write
#TODO
13. Volume機(jī)制
13.1 作用
是為了解決在通過rootfs機(jī)制(打包了操作系統(tǒng)所包含的文件、配置和目錄)和mount 那么死pace建立起了一個(gè)同宿主機(jī)完全隔離的文件環(huán)境系統(tǒng)同時(shí)引入的兩個(gè)問題:
- 容器里面的新建的文件,怎么讓宿主機(jī)獲取到
- 宿主機(jī)上的文件系統(tǒng),怎么讓容器的進(jìn)程訪問到
總而言之,是在隔離開宿主機(jī)和容器的文件系統(tǒng)之后,怎么進(jìn)行文件共享操作。
13.2 是什么
允許用戶將宿主機(jī)上指定的目錄或者文件,掛載到容器里面進(jìn)行讀取和修改操作。
注意mount namespace在根目錄下掛載rootfs和Volume機(jī)制掛載的區(qū)別
- rootfs掛載到根目錄是docker倉(cāng)庫(kù)中打包的操作系統(tǒng)的文件系統(tǒng)
- Volume機(jī)制是把物理機(jī)上的卷掛載到通過rootfs和mount namespace隔離開的容器卷上以達(dá)到容器和宿主機(jī)的文件共享目的
13.3 用法
// mount /home on host to /test in container filesystem
$ docker run -v /home:/test
思考
- 你是否知道最新的 Docker 項(xiàng)目默認(rèn)會(huì)為容器啟用哪些Namespace?
- 如何修復(fù)容器中的top指令以及/proc文件系統(tǒng)中的信息?(考慮lxcfs)
參考答案:
- top 是從 /prof/stats 目錄下獲取數(shù)據(jù),所以道理上來(lái)講,容器不掛載宿主機(jī)的該目錄就可以了。lxcfs就是來(lái)實(shí)現(xiàn)這個(gè)功能的,做法是把宿主機(jī)的 /var/lib/lxcfs/proc/memoinfo 文件掛載到Docker容器的/proc/meminfo位置后。容器中進(jìn)程讀取相應(yīng)文件內(nèi)容時(shí),LXCFS的FUSE實(shí)現(xiàn)會(huì)從容器對(duì)應(yīng)的Cgroup中讀取正確的內(nèi)存限制。從而使得應(yīng)用獲得正確的資源約束設(shè)定。kubernetes環(huán)境下,也能用,以ds 方式運(yùn)行 lxcfs ,自動(dòng)給容器注入爭(zhēng)取的 proc 信息。
- 用的是vanilla kubernetes,遇到的主要挑戰(zhàn)就是性能損失和多租戶隔離問題,性能損失目前沒想到好辦法,可能的方案是用ipvs 替換iptables ,以及用 RPC 替換 rest。多租戶隔離也沒有很好的方法,現(xiàn)在是讓不同的namespace調(diào)度到不同的物理機(jī)上。也許 rancher和openshift已經(jīng)多租戶隔離。
- 在從虛擬機(jī)向容器環(huán)境遷移應(yīng)用的過程中,可能會(huì)遇到哪些容器和虛擬機(jī)不一致的問題?
- 容器通過起進(jìn)程時(shí)候指明mount namespace命名空間啟動(dòng),但是如果不再做其他操作,那么會(huì)導(dǎo)致容器內(nèi)部看到的目錄繼承了宿主機(jī)目錄,所以需要重新掛載需要掛載的目錄,但是,那些沒有重新掛載的是否會(huì)被自動(dòng)umount?
在用戶起Docker容器時(shí),會(huì)通過mount namespace參數(shù)重新限定應(yīng)用進(jìn)程mount點(diǎn)可見視圖,同時(shí)在容器的根目錄下重新moun一個(gè)新的文件系統(tǒng),比如Ubuntu 16.04的ISO,用來(lái)為容器進(jìn)程提供隔離后執(zhí)行環(huán)境的文件系統(tǒng)(這個(gè)文件系統(tǒng)也被成為容器鏡像,但也有一個(gè)更專業(yè)的名字,rootfs(根文件系統(tǒng)),他包含一個(gè)操作系統(tǒng)所包含的文件、配置和目錄,并不包括操作系統(tǒng)的內(nèi)核。在Linux操作系統(tǒng)中,這兩部分是分開存在的,操作系統(tǒng)只有在開機(jī)啟動(dòng)時(shí)才會(huì)加載指定版本的內(nèi)核鏡像。因此,需要注意,同一臺(tái)機(jī)器上的所有容器,都共享宿主機(jī)操作系統(tǒng)的內(nèi)核)。這樣就不存在問題中的只掛載到比如/opt/ 目錄,而其他掛載點(diǎn)需不需要再去卸載的問題了。
- 既然容器的 rootfs(比如,Ubuntu 鏡像),是以只讀方式掛載的,那么又如何在容器里修改 Ubuntu 鏡像的內(nèi)容呢?(提示:Copy-on-Write)
-
除了 AuFS,你知道 Docker 項(xiàng)目還支持哪些 UnionFS 實(shí)現(xiàn)嗎?你能說出不同宿主機(jī)環(huán)境下推薦使用哪種實(shí)現(xiàn)嗎?
5-6參考答案:
qac6.PNG - 你在查看 Docker 容器的 Namespace 時(shí),是否注意到有一個(gè)叫 cgroup 的 Namespace?它是 Linux 4.6 之后新增加的一個(gè) Namespace,你知道它的作用嗎?
- 如果你執(zhí)行 docker run -v /home:/test 的時(shí)候,容器鏡像里的 /test 目錄下本來(lái)就有內(nèi)容的話,你會(huì)發(fā)現(xiàn),在宿主機(jī)的 /home 目錄下,也會(huì)出現(xiàn)這些內(nèi)容。這是怎么回事?為什么它們沒有被綁定掛載隱藏起來(lái)呢?(提示:Docker 的“copyData”功能)
- 請(qǐng)嘗試給這個(gè) Python 應(yīng)用加上 CPU 和 Memory 限制,然后啟動(dòng)它。根據(jù)我們前面介紹的 Cgroups 的知識(shí),請(qǐng)你查看一下這個(gè)容器的 Cgroups 文件系統(tǒng)的設(shè)置,是不是跟我前面的講解一致。
- 你在查看 Docker 容器的 Namespace 時(shí),是否注意到有一個(gè)叫 cgroup 的 Namespace?它是 Linux 4.6 之后新增加的一個(gè) Namespace,你知道它的作用嗎?
- 如果你執(zhí)行 docker run -v /home:/test 的時(shí)候,容器鏡像里的 /test 目錄下本來(lái)就有內(nèi)容的話,你會(huì)發(fā)現(xiàn),在宿主機(jī)的 /home 目錄下,也會(huì)出現(xiàn)這些內(nèi)容。這是怎么回事?為什么它們沒有被綁定掛載隱藏起來(lái)呢?(提示:Docker 的“copyData”功能)
- 請(qǐng)嘗試給這個(gè) Python 應(yīng)用加上 CPU 和 Memory 限制,然后啟動(dòng)它。根據(jù)我們前面介紹的 Cgroups 的知識(shí),請(qǐng)你查看一下這個(gè)容器的 Cgroups 文件系統(tǒng)的設(shè)置,是不是跟我前面的講解一致。(第八節(jié) 深入理解Docker)