關于容器和容器運行時的那些事
容器技術是一種虛擬化技術,也就是我們常常說的“軟件定義XXX”。在容器之前被廣泛應用的是虛擬機技術,也就是軟件定義的硬件。代表產(chǎn)品就是VMWare。我早年在EMC工作的時候,公司常常提起收購VMWare是多么多么英明的一項決策,那個時候虛擬機技術真的很火。例如軟件模擬硬件,用戶可以很方便的在自己的主機上運行不用的硬件和操作系統(tǒng),并且可以方便的把整個系統(tǒng)的快照作為文件遷移,真的非常方便。
但是虛擬機需要模擬整個的硬件,它的開銷是非常大的。內(nèi)存,CPU和磁盤空間都是獨占的。在系統(tǒng)架構的層面,虛擬機技術仍然非常有用,但是對日常的開發(fā)工作來說,虛擬機技術就太重了。
從軟件開發(fā)和部署的角度來看,我們希望有一個這樣的虛擬化技術:
- 能夠隔離CPU,內(nèi)存,磁盤,網(wǎng)絡等資源
- 能夠控制資源使用的量和優(yōu)先級
- 能夠運行獨立的操作系統(tǒng)
- 比較輕量級的系統(tǒng)開銷
- 比較方便的管理功能
在Unix/Linux世界,我們第一個想到的類似的東西是Chroot。
1.Chroot
Chroot是在Unix系統(tǒng)的一個操作,即 change root directory (更改 root 目錄)。在 Linux 系統(tǒng)中,系統(tǒng)默認的目錄結構都是以 `/`,即是以根 (root) 開始的。而在使用 chroot 之后,系統(tǒng)的目錄結構將以指定的位置作為 `/` 位置。由Chroot創(chuàng)造出的那個根目錄,叫做“chroot jail”(chroot jail,或chroot prison)。
只有root用戶可以執(zhí)行chroot。大多數(shù)Unix系統(tǒng)都沒有以完全文件系統(tǒng)為導向,以及可能通過網(wǎng)絡和過程控制,通過系統(tǒng)調(diào)用接口來提供一個破壞chroot的程序。
在經(jīng)過chroot 之后,系統(tǒng)讀取到的目錄和文件將不在是舊系統(tǒng)根下的而是新根下(即被指定的新的位置)的目錄結構和文件,因此它帶來的好處大致有以下3個:
- 增加了系統(tǒng)的安全性,限制了用戶的權力;在經(jīng)過 chroot 之后,在新根下將訪問不到舊系統(tǒng)的根目錄結構和文件,這樣就增強了系統(tǒng)的安全性。這個一般是在登錄 (login) 前使用 chroot,以此達到用戶不能訪問一些特定的文件。
- 建立一個與原系統(tǒng)隔離的系統(tǒng)目錄結構,方便用戶的開發(fā);使用 chroot 后,系統(tǒng)讀取的是新根下的目錄和文件,這是一個與原系統(tǒng)根下文件不相關的目錄結構。在這個新的環(huán)境中,可以用來測試軟件的靜態(tài)編譯以及一些與系統(tǒng)不相關的獨立開發(fā)。
- 切換系統(tǒng)的根目錄位置,引導 Linux 系統(tǒng)啟動以及急救系統(tǒng)等。chroot 的作用就是切換系統(tǒng)的根位置,而這個作用最為明顯的是在系統(tǒng)初始引導磁盤的處理過程中使用,從初始 RAM 磁盤 (initrd) 切換系統(tǒng)的根位置并執(zhí)行真正的 init。另外,當系統(tǒng)出現(xiàn)一些問題時,我們也可以使用 chroot 來切換到一個臨時的系統(tǒng)。
但是Chroot并不能滿足我們之前提到的哪些需求,chroot的隔離功能非常有限,chroot的機制本身不是為限制資源的使用而設計,如I/O,帶寬,磁盤空間或CPU時間。
為了實現(xiàn)我們之前提到的那些需求,我們需要一種更為強大的虛擬化技術,容器也就隨之發(fā)展起來。
2.容器VS.虛機
Linux的容器和虛擬機的差比我想大家都比較了解了,我這里再簡單敘述一下:
對于使用虛擬機的傳統(tǒng)虛擬化,每個虛擬機都有自己的完整操作系統(tǒng),因此在運行內(nèi)置于虛擬機的應用程序時,內(nèi)存使用量可能會高于必要值,虛擬機可能會開始耗盡主機所需的資源。容器共享操作系統(tǒng)環(huán)境(內(nèi)核),因此它們比完整虛擬機使用更少的資源,并減輕主機內(nèi)存的壓力。
傳統(tǒng)虛擬機可占用大量磁盤空間:除了虛擬機托管的任何應用程序外,它們還包含完整的操作系統(tǒng)和相關工具。
容器相對較輕:它們僅包含使容器化應用程序運行所需的庫和工具,因此它們比虛擬機更緊湊,并且啟動速度更快。
在更新或修補操作系統(tǒng)時,必須逐個更新傳統(tǒng)計算機:必須單獨修補每個客戶操作系統(tǒng)。對于容器,只需更新容器主機(托管容器的機器)的操作系統(tǒng)。這顯著簡化了維護。
利用這些優(yōu)勢容器在軟件開發(fā)領域里迅速發(fā)展,我已經(jīng)很習慣用容器去安裝各種軟件應用,因為它開銷很小,而且隔離性很好,我可以很方便的使用同一個軟件的多個版本而不用擔心沖突問題。
Linux的容器技術是如何做到這些的呢?我們來看看構建容器技術的兩個核心功能命名空間Namespace和控制組CGroup。
3.命名空間Namespace
命名空間是Linux內(nèi)核的一項功能,該功能對內(nèi)核資源進行分區(qū)。控制進程可以訪問的資源,以使一組進程看到一組資源,而另一組進程看到另一組資源。資源可能存在于多個空間中。Linux系統(tǒng)以每種類型的單個名稱空間開始,供所有進程使用。進程可以創(chuàng)建其他名稱空間,并加入不同的名稱空間。
Linux下常見的命名空間有:
- 掛載Mount mnt
- 進程Id pid
- 網(wǎng)絡 net
- 進程間通信 ipc
- 用戶Id user
- UTS
簡單的講,利用Namespace可以實現(xiàn)我們想要的資源隔離,控制哪些資源可以使用。
4.控制組CGroup
CGroup 是 Control Groups 的縮寫,是 Linux 內(nèi)核提供的一種可以限制、記錄、隔離進程組 (process groups) 所使用的物理資源 (如 cpu memory i/o 等等) 的機制。2007 年進入 Linux 2.6.24 內(nèi)核,CGroups 不是全新創(chuàng)造的,它將進程管理從 cpuset 中剝離出來,作者是 Google 的 Paul Menage。CGroups 也是 LXC 為實現(xiàn)虛擬化所使用的資源管理手段。
CGroup 是將任意進程進行分組化管理的 Linux 內(nèi)核功能。CGroup 本身是提供將進程進行分組化管理的功能和接口的基礎結構,I/O 或內(nèi)存的分配控制等具體的資源管理功能是通過這個功能來實現(xiàn)的。這些具體的資源管理功能稱為 CGroup 子系統(tǒng)或控制器。CGroup 子系統(tǒng)有控制內(nèi)存的 Memory 控制器、控制進程調(diào)度的 CPU 控制器等。運行中的內(nèi)核可以使用的 Cgroup 子系統(tǒng)由/proc/cgroup 來確認。
CGroup的主要功能:
- 資源限制,可以將組設置為不超過配置的內(nèi)存限制,其中還包括文件系統(tǒng)緩存[8] [9]
- 優(yōu)先次序,一些組可能會在CPU利用率或磁盤I / O吞吐量中獲得更大份額。
- 會計,衡量組的資源使用情況,例如可用于計費目的
- 控制,凍結進程組,記錄檢查點并重新啟動
CGroup常見的子系統(tǒng)包括:
- blkio 對塊設備(比如硬盤)的IO進行訪問限制
- cpu 設置進程的CPU調(diào)度的策略,比如CPU時間片的分配
- memory 用于控制cgroup中進程的占用以及生成內(nèi)存占用報告
- net_cls 使用等級識別符(classid)標記網(wǎng)絡數(shù)據(jù)包,這讓 Linux 流量控制器 tc (traffic controller) 可以識別來自特定 cgroup 的包并做限流或監(jiān)控
- net_prio 設置cgroup中進程產(chǎn)生的網(wǎng)絡流量的優(yōu)先級
- hugetlb 限制使用的內(nèi)存頁數(shù)量
- pids 限制任務的數(shù)量
- ns 可以使不同cgroups下面的進程使用不同的namespace.
每個subsystem會關聯(lián)到定義的cgroup上,并對這個cgroup中的進程做相應的限制和控制.
簡單的講,利用CGroup,可以控制能使用的資源的量。
有了Namespace和CGroup這兩個特性,容器做到了控制資源隔離和訪問的量。但是我們還是需要方便的管理功能和接口,Docker在容器的基本功能的基礎上提供了出色的管理功能和接口,成為了容器領域里的事實標準,我們一般說容器,默認的是用Docker的技術。
5.Docker
Docker 是一個開放源代碼軟件,是一個開放平臺,用于開發(fā)應用、交付應用、運行應用。Docker允許用戶將基礎設施中的應用單獨分割出來,形成更小的顆粒,從而提高交付軟件的速度。Docker容器與虛擬機類似,但原理上,容器是將操作系統(tǒng)層虛擬化,虛擬機則是虛擬化硬件,因此容器更具有便攜性、高效地利用服務器。
Docker的主要特性有:
- 分層容器
Docker使用AUFS / devicemapper / btrfs使用文件系統(tǒng)的只讀層來構建容器。容器由只讀層組成,這些只讀層在提交后將成為容器映像。鏡像是一個包含用于構建應用程序的圖層的容器。當docker容器運行時,只有頂層是可讀寫的,下面的所有層都是只讀的,頂層是臨時數(shù)據(jù),直到將其提交到新層為止。使用只讀文件系統(tǒng)的覆蓋層會帶來固有的復雜性和性能損失。
- 單一應用容器
Docker將容器限制為僅一個進程。默認的docker baseimage OS模板并非旨在支持多個應用程序,進程或服務,如init,cron,syslog,ssh等。您可以想象這會引入一定的復雜性,并且對日常使用場景具有巨大的影響。由于當前的體系結構,應用程序和服務旨在在正常的多進程OS環(huán)境中運行,因此您需要找到一種Docker方式來做事或使用支持Docker的工具。對于LAMP容器的應用程序,需要構建3個相互使用服務的容器,一個PHP容器,一個Apache容器和一個MySQL容器。能在一個容器中建造所有3個容器嗎?可以,但無法在同一容器中運行php-fpm,apache和mysqld,也無法安裝單獨的進程管理器(如runit或supervisor)。
- 狀態(tài)分離
Docker將容器存儲與應用程序分開,可以在數(shù)據(jù)卷容器中將持久性數(shù)據(jù)安裝在主機中的容器外部。除非用例只是具有非持久性數(shù)據(jù)的容器,否則有可能使Docker容器的可移植性降低。這也是容器編排更容易支持無狀態(tài)應用的根本原因。
- 鏡像注冊
Docker提供了一個公共和私有鏡像注冊,用戶可以在其中推送和提取鏡像。鏡像用于組成應用程序的只讀層。這使用戶可以輕松共享和分發(fā)應用程序。
上圖是Docker的架構圖,我們看到Docker是如何提供容器的管理功能的。
- Docker 守護進程負責容器聲明周期的管理
- Registry 提供容器鏡像倉庫的功能
- Docker 守護進程負責從鏡像倉庫推/拉取容器的鏡像
- 客戶端程序負責和守護進程通信,發(fā)送相關的容器管理的命令
在Docker 1.11版之前,Docker Engine守護進程下載容器映像,啟動容器進程,公開遠程API并充當日志收集守護進程,所有這些都以集中化進程的身份以root身份運行。盡管這樣的集中式體系結構便于部署,但是它沒有遵循Unix進程和特權分離的優(yōu)秀實踐;此外,這使得Docker難以與Linux初始系統(tǒng)(如upstart和systemd)正確集成。
如下圖所示,從1.11版開始,Docker守護程序不再處理容器本身的執(zhí)行。而是現(xiàn)在由containerd處理。更準確地說,Docker守護程序將映像準備為開放容器鏡像(OCI)捆綁包,并對容器進行API調(diào)用以啟動OCI捆綁包。然后使用runC啟動容器化容器。
那么ContainerD和RunC又分別是神馬東東呢?我們繼續(xù)探索。
6.ContainerD
Containerd是行業(yè)標準的容器運行時,重點是簡單性,健壯性和可移植性。containerd可用作Linux和Windows的守護程序。它管理著主機系統(tǒng)的容器的整個生命周期,從鏡像傳輸和存儲到容器執(zhí)行和監(jiān)督,再到低級存儲再到網(wǎng)絡附件等等。containerd旨在嵌入到更大的系統(tǒng)中,而不是由開發(fā)人員或最終用戶直接使用。
containerD是用Go語言構建的,有興趣的可以去看它的代碼:
https://github.com/containerd/containerd
7.RunC
RunC是一個輕量級的工具,它是用來運行容器的,只用來做這一件事,并且這一件事要做好。我們可以認為它就是個命令行小工具,可以不用通過 docker 引擎,直接運行容器。事實上,runC 是標準化的產(chǎn)物,它根據(jù) OCI 標準來創(chuàng)建和運行容器。而 OCI(Open Container Initiative)組織,旨在圍繞容器格式和運行時制定一個開放的工業(yè)化標準。
RunC支持一普通用戶的身份運行容器。
RunC支持容器的熱遷移操作,所謂熱遷移就是將一個容器進行 checkpoint 操作,并獲得一系列文件,使用這一系列文件可以在本機或者其他主機上進行容器的 restore 工作。這也是 checkpoint 和 restore 兩個命令存在的原因。熱遷移屬于比較復雜的操作,目前 runC 使用了 CRIU 作為熱遷移的工具。RunC 主要是調(diào)用 CRIU(Checkpoint and Restore in Userspace)來完成熱遷移操作。CIRU 負責凍結進程,并將作為一系列文件存儲在硬盤上。并負責使用這些文件還原這個被凍結的進程。
上圖顯示了不同的容器技術是如何使用RunC的,可以看到,Docker/Podman/CRI-O都使用了RunC。那么我們看看除了Docker,現(xiàn)在還有哪些容器的運行時呢?
8.CRI-O
CRI-O是Kubernetes的輕量級容器運行時,這就是CRI-O提供的。該名稱源于CRI(Container Runtime Interface)加開放容器倡議(OCI open container initiative ),因為CRI-O嚴格關注符合OCI的運行時和容器映像。CRI-O的范圍是與Kubernetes一起使用,以管理和運行OCI容器。盡管該項目確實具有一些用于故障排除的面向用戶的工具,但它并不是面向開發(fā)人員的工具。
上圖是CRI-O的架構。
簡而言之,CRI-O是用于Kubernetes內(nèi)部的容器運行時接口的標準。它的出現(xiàn)我的理解是K8s(google)為了擺脫docker的束縛,走向開放平臺的一步棋??梢钥吹剑珼ocker作為一個成功的技術產(chǎn)品,它在商業(yè)上卻似乎不是那么成功,k8s如果完成對docker的解耦,docker的前景似乎不太妙。
9.Podman
守護進程是人們對Docker架構的主要詬病,它帶來了很多管理和安全上的問題。
Podman是一個無守護進程的容器引擎,用于在Linux系統(tǒng)上開發(fā),管理和運行OCI容器。容器可以以root用戶或普通用戶的模式運行。
Podman管理容器使用傳統(tǒng)的fork / exec模型,因此容器進程是Podman進程的后代。Docker使用客戶端/服務器模型。執(zhí)行的docker命令是Docker客戶端工具,它通過客戶端/服務器操作與Docker守護進程通信。然后,Docker守護程序創(chuàng)建容器并處理stdin / stdout與Docker客戶端工具的通信。Podman可以運行于非root用戶模式下,而docker的守護進程必須用root用戶啟動。Podman的模型被認為是更為安全的模型。同時因為唯有守護進程,你的系統(tǒng)看上去也更為干凈。
當然Podman的問題是它還很新,管理工具和功能都很弱,你可能需要buildah來構建鏡像,社區(qū)和生態(tài)都還很小。如果你想用Podman取代Docker,請謹慎操作。
10.LXC/LXD
LXC是Linux內(nèi)核容器功能的用戶空間接口。通過功能強大的API和簡單的工具,它使Linux用戶可以輕松地創(chuàng)建和管理系統(tǒng)或應用程序容器。
LXC是一個系統(tǒng)容器運行時,旨在執(zhí)行“完整的系統(tǒng)容器”,通常由完整的操作系統(tǒng)映像組成。在最常見的用例中,LXC進程將引導完整的Linux發(fā)行版,如Debian,F(xiàn)edora,Arch等,并且用戶將與虛擬機映像進行交互。LXC也可以用于運行(但不下載)應用程序容器,但是這種用法需要對底層操作系統(tǒng)的詳細信息有更多的了解,并且這種做法不太常見。LXC可以從各種公共鏡像下載“完整系統(tǒng)容器”映像,并以密碼方式對其進行驗證。LXC沒有中央守護程序,可以與instart系統(tǒng)(例如upstart和systemd)集成。
LXD與LXC相似,但它是liblxc之上的REST API,它派生了一個監(jiān)視器和容器進程。這樣可以確保LXD守護程序不是故障的中心點,并且在LXD守護程序發(fā)生故障的情況下容器可以繼續(xù)運行。所有其他細節(jié)與LXC幾乎相同。簡單的說LXD = LXC + RestAPI
LXC是一種容器技術,可為您提供輕量級Linux容器,而Docker是基于容器的單個應用程序虛擬化引擎。它們聽起來可能相似,但完全不同。與LXC容器不同,Docker容器的行為不像輕量級VM,因此不能被視為輕量級VM。Docker容器在設計上僅限于單個應用程序。你可以登錄到LXC容器,將其像OS一樣對待,然后安裝您的應用程序和服務,它將按預期運行。您無法在Docker容器中做到這一點。Docker基礎OS模板被簡化為單個應用程序環(huán)境,并且沒有適當?shù)某跏蓟蛑С种T如服務,守護程序,syslog,cron或運行多個應用程序之類的東西。
11.rkt
rkt是為現(xiàn)代生產(chǎn)云原生環(huán)境開發(fā)的應用程序容器引擎。它具有pod-native方法,可插入執(zhí)行環(huán)境以及定義明確的表面積,使其非常適合與其他系統(tǒng)集成。
rkt的核心執(zhí)行單元是Pod,它是在共享上下文中執(zhí)行的一個或多個應用程序的集合(rkt的Pod與Kubernetes編排系統(tǒng)中的概念同義)。rkt允許用戶在Pod級別和更細粒度的每個應用程序級別應用不同的配置(例如隔離參數(shù))。rkt的體系結構意味著在一個獨立的,獨立的環(huán)境中,每個pod都可以直接在經(jīng)典的Unix流程模型(即沒有中央守護程序)中執(zhí)行。rkt實施了現(xiàn)代,開放,標準的容器格式,即App Container(appc)規(guī)范,但還可以執(zhí)行其他容器映像,例如使用Docker創(chuàng)建的那些。
自2014年12月由CoreOS引入以來,rkt項目已經(jīng)非常成熟并得到了廣泛使用。它可用于大多數(shù)主要的Linux發(fā)行版,并且每個rkt發(fā)行版都會構建供用戶安裝的獨立rpm / deb軟件包。這些軟件包還可以作為Kubernetes存儲庫的一部分使用,以支持rkt + Kubernetes集成的測試。rkt在Google Container Image和CoreOS Container Linux如何運行Kubernetes方面也起著核心作用。RKT以其快速,可組合和安全的提供功能而聞名。許多用戶已經(jīng)注意到docker的安全問題,因此CoreOS必須在2014年發(fā)布RKT作為docker的競爭對手,并且由于其功能(如安全性,可互操作性等)而變得流行。
類似Podman,rkt沒有集中的守護進程,而是直接從客戶端命令啟動容器,從而使其與系統(tǒng)初始化功能(例如systemd,upstart等)兼容。
12.Kata Container
Kata Containers 是由 OpenStack 基金會管理,但獨立于 OpenStack 項目之外的容器項目。它是一個可以使用容器鏡像以超輕量級虛機的形式創(chuàng)建容器的運行時工具,Kata Containers 創(chuàng)建的不同容器跑在一個個不同的虛擬機(kernel)上,比起傳統(tǒng)容器提供了更好的隔離性和安全性。同時繼承了容器快速啟動和快速部署等優(yōu)點。
我們之前提到了虛擬機技術因為其開銷的原因,受到了一定的使用限制。Kata Container可以說是虛擬機技術的逆襲,可以多快好省的建設容器社會。
如上圖的架構所示,Kata Containers 其實跟 RunC 類似,也是一個符合 OCI 運行時規(guī)范的一種實現(xiàn)。不同之處是它給每個 Docker 容器或每個 K8S Pod 增加了一個獨立的 Linux 內(nèi)核 (不共享宿主機的內(nèi)核),使容器具有更好的隔離性、安全性。
13.其他
除了我們之前提到的,還有其他一些容器技術,我們簡單的看看。
- systemd-nspawn是一個容器運行時,旨在在Linux容器內(nèi)部執(zhí)行進程。systemd-nspawn的名字來源從“從命名空間spawn”,這意味著它僅處理進程隔離,而不執(zhí)行內(nèi)存,CPU等資源隔離。systemd-nspawn可以運行應用程序容器或系統(tǒng)容器,但不能單獨運行,下載或驗證鏡像。systemd-nspawn沒有集中的守護程序,可以與系統(tǒng)啟動(例如upstart和systemd)集成。
- OpenVZ OpenVZ是基于Linux內(nèi)核的操作系統(tǒng)級虛擬化技術。OpenVZ允許物理服務器運行多個操作系統(tǒng),該技術常用于虛擬專用服務器(VPS,Virtual Private Server)。OpenVZ是一種系統(tǒng)容器運行時,旨在執(zhí)行通常是完整系統(tǒng)映像的“完整系統(tǒng)容器”。在最常見的用例中,OpenVZ進程將引導完整的Linux 發(fā)行版,例如Debian,F(xiàn)edora,Arch等,并且用戶將與虛擬機映像類似地與其交互。OpenVZ可以從各種公共鏡像下載“完整系統(tǒng)容器”鏡像,并以密碼方式對其進行驗證。OpenVZ沒有中央守護程序,可以與inup系統(tǒng)(例如upstart和systemd)集成。
- FreeBSD jail,一種操作系統(tǒng)層虛擬化技術,在FreeBSD操作系統(tǒng)中運作。利用這個技術,F(xiàn)reeBSD的系統(tǒng)管理者,可以創(chuàng)造出幾個小型的軟件系統(tǒng),這些軟件系統(tǒng)被稱為jail。這個技術被Poul-Henning Kamp采納,加入FreeBSD系統(tǒng)中。
- Solaris Containers,以及Solaris Zones,一個操作系統(tǒng)層虛擬化技術的實作,由Sun開發(fā)。2004年2月,伴隨著Solaris 10 build 51 beta首次對外發(fā)布,經(jīng)過完整測試后,在2005年,與Solaris 10一同完整發(fā)布。
- Linux-VServer是通過向Linux內(nèi)核添加操作系統(tǒng)級虛擬化功能而創(chuàng)建的虛擬專用服務器實現(xiàn)。它是作為開源軟件開發(fā)和發(fā)布的。
14.總結
隨著微服務和云原生技術的大行其道,容器的生態(tài)系統(tǒng)的吸引了越來越多的玩家。Docker作為昔日王者,受到了諸多其它后起之秀的挑戰(zhàn),我們可以預期未來更多的組織會加入到下面的這張圖里。
希望本文能夠幫助你了解容器技術的基本知識,在面對諸多容器技術的術語和各種容器運行時的時候,不再手足無措。