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