Docker誕生於2013年,並普及了容器的概念,以至於大多數人仍然將容器的概念等同於“Docker容器”。
作為第一個吃螃蟹的人,Docker設定了新加入者必須遵守的標準。例如,Docker有一個大型系統映象庫。所有的替代方案都必須使用相同的映象格式,同時試圖改變Docker所基於的整個堆疊的一個或多個部分。
在此期間,出現了新的容器標準,容器生態系統朝著不同方向發展。現在除了Docker之外,還有很多方法可以使用容器。
在本文中,我們將介紹以下內容:
-
將Chroot、cgroups和名稱空間作為容器的技術基礎
-
定義Docker所基於的軟體堆疊
-
說明Docker和Kubernetes需要堅持和遵守的標準
-
介紹替代解決方案,這些解決方案嘗試使用具有更好更安全的元件來替換原始Docker容器。
容器的軟體堆疊
像Chroot 呼叫、 cgroups 和名稱空間等 Linux 特性幫助容器在與所有其他程式隔離的情況下執行,從而保證執行時的安全性。
Chroot
所有類似Docker的技術都起源於類似Unix作業系統(OS)的根目錄。在根目錄上方是根檔案系統和其他目錄。
從長遠來看,這是很危險的,因為根目錄中任何不需要的刪除都會影響整個作業系統。這就是為什麼存在一個系統呼叫chroot()。它建立了額外的根目錄,例如一個用於執行遺留軟體,另一個用於包含資料庫等等。
對於所有這些環境,chroot似乎是一個真正的根目錄,而是實際上,它只是將路徑名新增到任何以/開頭的名字上。真正的根目錄仍然存在,並且任何程式都可以引用指定根目錄以外的任何位置。
Linux cgroups
自2008年2.6.24版本以來,Control groups (cgroups)一直是Linux核心的一項功能。Cgroup將同時限制、隔離和測量多個程式的系統資源(記憶體、CPU、網路和I/O)使用情況。
假設我們想阻止使用者從伺服器傳送大量電子郵件。我們建立了一個記憶體限制為1GB、CPU佔用率為50%的cgroup,並將應用程式的 processid新增到該組中。當達到這些限制時,系統將限制電子郵件傳送過程。它甚至可能終止程式,這取決於託管策略。
Namespaces
Linux名稱空間是另一個有用的抽象層。名稱空間允許我們擁有許多程式層次,每個層次都有自己的巢狀“子樹(subtree)”。名稱空間可以使用全域性資源,並將其呈現給其成員,就像它是自己的資源一樣。
具體來看,Linux系統開始時的程式識別符號(PID)為1,並且所有其他程式將包含在其樹中。PID名稱空間允許我們跨越一棵新樹,它擁有自己的PID 1程式。現在有兩個值為1的PID,每個名稱空間可以產生自己的名稱空間,並且相同的過程可以附加了幾個PID。
子名稱空間中的一個程式將不知道父級的程式存在,而父名稱空間將可以訪問整個子名稱空間。
有七種型別的名稱空間:cgroup、IPC、網路、mount、PID、使用者和UTS。
Network Namespace
一些資源是稀缺的。按照慣例,有些埠具有預定義的角色,不應用於其他任何用途:埠80僅服務於HTTP呼叫,埠443僅服務於HTTPS呼叫等等。在共享主機環境中,兩個或多個站點可以監聽來自埠80的HTTP請求。第一個獲得該埠的站點不允許任何其他應用程式訪問該埠上的資料。第一個應用程式在網際網路上是可見的,而其他所有應用程式將不可見。
解決方案是使用網路名稱空間,通過網路名稱空間,內部程式將看到不同的網路介面。
在一個網路名稱空間中,同一埠可以是開放的,而在另一個網路名稱空間中,可以關閉該埠。為此,我們必須採用額外的“虛擬”網路介面,這些介面同時屬於多個名稱空間。中間還必須有一個路由器程式,將到達物理裝置的請求連線到相應的名稱空間和其中的程式。
複雜嗎?這就是為什麼Docker和類似工具如此受歡迎。現在讓我們來介紹一下Docker,以及它的替代方案。
Docker: 人人可用的容器
在容器統治雲端計算世界之前,虛擬機器非常流行。如果你有一臺Windows機器,但想為iOS開發移動應用程式,你可以購買一臺新的Mac,或者將其虛擬機器安裝到Windows硬體上。虛擬機器也可能是笨重的,它們經常吞噬不需要的資源,而且啟動速度通常很慢(長達一分鐘)。
容器是標準軟體單元,具有執行程式所需的一切:作業系統、資料庫、映象、圖示,軟體庫、程式碼和所需的其他元件。容器的執行也與所有其他容器,甚至與作業系統本身隔離。與虛擬機器相比,容器是輕量級的,所以它們可以快速啟動,並且容易被替換。
要執行隔離和保護,容器需要基於Chroot、cgroups和名稱空間。
容器的映象是在實際機器上形成應用程式的模板,能夠根據單個映象建立儘可能多的容器,一個名為Dockerfile的文字檔案包含了組裝映象所需的所有資訊。
Docker帶來的真正革命是建立了Docker映象倉庫和開發了Docker引擎,這些映象以相同的方式在各地執行,作為第一個被廣泛採用的容器映象,形成了一個不成文的世界標準,所有後來的入局者都必須關注它。
CRI and OCI
OCI 全稱為Open Container Initiative,它釋出映象和容器的規範。它於2015年由Docker發起,並被微軟、Facebook、英特爾、VMWare、甲骨文和許多其他行業巨頭接受。
OCI還提供了規範的一個實現,被稱為runc ,它可以直接使用容器,建立並執行它們等。
容器執行時介面(Container Runtime Interface,簡稱CRI)是一個Kubernetes API,它定義了Kubernetes如何與容器執行時互動。它也是標準化的,所以我們可以選擇採用哪個CRI實現。
用於CRI和OCI的容器的軟體堆疊
Linux是執行容器的軟體堆疊中最基本的部分:
請注意,Containerd和CRI-O都堅持CRI和OCI規範。對於Kubernetes而言,這意味著它可以使用Containerd或CRI-O,而使用者不會注意到其中的區別。它還可以使用我們現在要提到的任何其他替代方案——這正是建立和採用了OCI和CRI等軟體標準的目標。
Docker軟體堆疊
Docker的軟體堆疊包括:
docker-cli,面向開發者的Docker命令列介面
containerd,最初由Docker編寫,後來作為一個獨立的專案啟動; 它實現了CRI規範
runc,它實現了OCI規範
容器(使用chroot、cgroups、名稱空間等)
Kubernetes的軟體堆疊幾乎是相同的;Kubernetes使用CRI-O,而不是Containerd,這是由Red Hat / IBM和其他人建立的CRI實現。
containerd
containerd作為一個守護程式在Linux和Windows上執行。它載入映象,將其作為容器執行,監督底層儲存,並負責整個容器的執行時間和生命週期。
Containerd誕生於2014年,一開始作為Docker的一部分,2017年成為雲原生計算基金會(CNCF)中的一個專案,並於2019年年初畢業。如果你想了解一些Containerd的使用技巧,歡迎檢視下方的文章:
配置 containerd 映象倉庫完全攻略
runc
runc是OCI規範的參考實現。它建立並執行容器以及其中的程式。它使用較低階別的Linux特性,比如cgroup和名稱空間。
runc的替代方案包括Kata-Runtime、GVisor和CRI-O。
Kata-Runtime使用硬體虛擬化作為單獨的輕量級VM實現OCI規範。它的執行時與OCI、CRI-O和Containerd相容,因此它可以與Docker和Kubernetes無縫工作。
Google的gVisor建立包含自己核心的容器。它通過名為runsc的專案實現OCI,該專案與Docker和Kubernetes整合。有自己核心的容器比沒有核心的容器更安全,但它不是萬能的,而且這種方法在資源使用上要付出代價。
CRI-O是一個純粹為Kubernetes設計的容器堆疊,是CRI標準的第一個實現。它從任何容器映象倉庫中 提取映象,可以作為使用Docker的輕量級替代方案。
今天它支援runc和Kata Containers作為容器執行時,但也可以插入任何其他OC相容的執行時(至少在理論上)。
它是一個CNCF孵化專案。
Podman
Podman是一個沒有守護程式的Docker替代品。它的命令有意與Docker儘可能相容,以至於您可以在CLI介面中建立一個別名並開始使用單詞“Docker”而不是“podman”。
Podman的目標是取代Docker,因此堅持使用相同的命令集是有意義的。Podman試圖改進Docker中的兩個問題。
首先,Docker總是使用內部守護程式執行。守護程式是在後臺執行的單程式。如果它失敗了,整個系統就會失敗。
第二,Docker作為後臺程式執行,具有root許可權,所以當你給一個新的使用者訪問權時,你實際上是給了整個伺服器的訪問權。
Podman是一個遠端Linux客戶端,可直接從作業系統執行容器。你也可以以rootless模式執行它們。它從DockerHub下載映象,並以與Docker完全相同的方式執行它們,具有完全相同的命令。
Podman以root以外的使用者身份執行命令和映象,所以它比Docker更安全。另一方面,有許多為Docker開發的工具在Podman上是不可用的,如Portainer和Watchtower。擺脫Docker意味著放棄你之前建立的工作流程。
Podman的目錄結構與buildah、skopeo和CRI-I類似。它的Pod也非常類似於KubernetesPod。
Linux容器:LXC和LXD
LXC(LinuX Containers)於2008年推出,是Linux上第一個上游核心的容器。Docker的第一個版本使用了LXC,但在後來的發展中,由於已經實現了runc,所以LXC被移除了。
LXC的目標是使用一個Linux核心在一個控制主機上執行多個隔離的Linux虛擬環境。為此,它使用了cgroups功能,而不需要啟動任何虛擬機器;它還使用名稱空間,將應用程式與底層系統完全隔離。
LXC旨在建立系統容器,幾乎就像你在虛擬機器中一樣——但硬體開銷很小,因為這些硬體是被虛擬化的。
LXC不模擬硬體和軟體包,只包含需要的應用程式,所以它幾乎以裸機速度執行。相反,虛擬機器包含整個作業系統,然後模擬硬體,如硬碟、虛擬處理器和網路介面。
所以,LXC是小而快的,而虛擬機器是大而慢的。另一方面,虛擬環境不能被打包成現成的、可快速部署的機器,而且很難通過GUI管理控制檯進行管理。LXC要求技術人員有很高的技術水平,並且優化後的機器可能與其他環境不相容。
LXC VS Docker
LXC就像Linux上的一個增壓chroot,它產生的“小”伺服器啟動更快,需要更少的RAM。然而,Docker提供了更多特性:
- 跨機器的可移植部署:使用一個版本的Docker建立的物件可以傳輸並安裝到任何其他支援Docker的Linux主機上。
- 版本控制:Docker可以用一種類似git的方式跟蹤版本——您可以建立容器的新版本,將它們回滾等等。
- 重複使用元件:使用Docker,您可以將已經建立的包堆疊到新包中。如果您想要一個LAMP環境,可以安裝一次它的元件,然後將它們作為預先製作的LAMP映象重新使用。
- Docker映象存檔:可以從專用站點下載數十萬個Docker映象,並且很容易將新映象上傳到這樣的映象倉庫中。
LXC面向系統管理員,而Docker更面向開發人員。這就是Docker更受歡迎的原因所在。
LXD
LXD有一個特權守護程式,它通過本地UNIX socket和網路(如果啟用)公開REST API。您可以通過命令列工具訪問它,但它總是使用REST API呼叫進行通訊。無論客戶端是在本地機器上還是在遠端伺服器上,它的功能都是一樣的。
LXD可以從一臺本地機器擴充套件到幾千臺遠端機器。與Docker類似,它是基於映象的,所有更流行的Linux發行版都可以使用映象。Ubuntu的公司Canonical正在資助LXD的開發,因此它將始終執行在Ubuntu以及其他類似Linux作業系統的最新版本上。LXD可以與OpenNebula和OpenStack標準無縫整合。
從技術上講,LXD是站在LXC的肩膀上(兩者都使用相同的liblxc庫和Go語言建立容器),但LXD的目標是改善使用者體驗。
Docker會永遠存在嗎?
Docker擁有1100萬開發者、700萬個應用程式和每月130億次的映象下載。如果僅僅說Docker仍然是領導,那就太輕描淡寫了。然而,在這篇文章中,我們已經看到,現在已經有許多產品可以取代Docker軟體棧的一個或多個部分,並且通常情況下沒有相容性問題。而且與Docker提供的服務相比,其他軟體的主要目標是安全性。
原文連結: