Kubernetes 主要管理在容器中運行的應用程式。

2.1 介紹容器

不同的微服務在相同的作業系統中可能需要不同、潛在衝突的動態連結庫版本或者不同的環境需求。

當一個系統由少量應用程式組成時,可以為每個應用程式分配一個專用VM並在各自的作業系統中運行每個應用程式。 但是隨著微服務變得越小並且它們的數量開始增長,如果您想維持較低的硬體成本並且不浪費資源,您可能無法為每個微服務提供專屬VM。

這不只是浪費硬體資源的問題 —— 通常每個VM都需要單獨設定和管理,這表示運行更多數量的VM也會導致更多的人力並且需要更好、通常更複雜的自動化系統。 由於轉向微服務架構,其中系統由數百個部署的應用程式實例組成,因此需要VM的替代方案。容器就是替代方案。

大多數開發和維運團隊現在更喜歡使用容器,而不是使用VM來隔離單個微服務(或一般的軟體程序)的環境。 容器允許您在同一台host電腦上運行多個服務,同時保持它們彼此隔離。與VM類似,但硬體開銷要少得多。

與運行多個系統程序的單個作業系統的VM不同,在容器中運行的程序會運行在現有host的主機作業系統上。 因為只有一個作業系統,所以沒有重複的系統程序。儘管所有應用程式的程序都在同一個作業系統中運行,但它們的環境是彼此隔離的,儘管隔離方面不如在單台VM中運行應用程式時那樣好。 對於容器中的程序,這種隔離使得在電腦上看起來就像沒有其他程序存在。

與VM相比,容器比較輕量,因為它們不需要各自的資源pool或任何額外OS-level程序。 然而每個VM通常會運行自己的一組系統程序,除了使用者應用程式本身的程序的資源消耗之外,VM上系統程序還需要額外的計算資源。 而容器只不過是運行在現有host主機作業系統上的一個隔離的程序,它只消耗應用程式本身的資源消耗,幾乎沒有額外資源開銷。

下圖2.1 顯示了兩台裸機,一台運行兩台VM,另一台運行容器。 後者有額外容器的空間,因為它只運行一個作業系統,而第一個運行三個作業系統(一個主機OS和兩個客戶OS)。

圖2.1 使用VM隔離應用程式與使用容器隔離應用程式

圖2.1 使用VM隔離應用程式與使用容器隔離應用程式

您經常將多個應用程式分組到每個VM之中,但由於VM的資源額外開銷,您可能負擔不起給每個應用程式提供整個專屬VM的費用。 但是容器不會引起任何額外開銷,這表示您可以給每個應用程式建立一個單獨容器。 事實上,你永遠不應該在同一個容器中運行多個應用程式,因為這使得管理容器中的程序變得更加困難。 此外,目前所有處理容器的軟體,包括k8s本身,都是在容器中只有一個應用程式的前提下設計的。 但k8s提供了一種將相關應用程式放在一起運行的方法,但仍將它們維持在個別容器之中。

除了運行時較低的開銷外,容器還可以更快地啟動應用程式,因為只需要啟動應用程式程序本身。不需要像新VM開機時需要先啟動其他系統程序。

你會同意容器在資源使用方面顯然更好,但也有缺點。當您在VM中運行應用程式時,每個VM都運行自己的作業系統和kernel。 在這些VM下面是hypervisor(可能還會有一個額外的作業系統),它將物理硬體資源分成更小的虛擬資源集合,每個VM中的作業系統可以使用這些虛擬資源。 如圖 2.2 所示,在這些VM中運行的應用程式對VM中的客戶作業系統kernel進行系統呼叫(sys-calls),然後kernel在虛擬CPU上執行的機器指令是透過hypervisor轉發到host主機上實體CPU去執行。

圖2.2 應用程式在VM中與在容器中運行時如何使用硬體

圖2.2 應用程式在VM中與在容器中運行時如何使用硬體

另一方面,容器都是在host主機作業系統中單個kernel上進行系統呼叫。這個單一kernel是唯一在host主機CPU上執行指令的kernel。CPU不需要像VM那樣需要處理任何類型的虛擬化轉換。 查看下圖,在裸機上運行三個應用程式、在VM中運行三個應用程式、在容器中運行三個應用程式之間的區別。

圖2.3 在裸機、VM和容器中運行應用程式的差別

圖2.3 在裸機、VM和容器中運行應用程式的差別

在第一種情況下,所有三個應用程式都使用相同的kernel並且根本沒有隔離。 在第二種情況下,應用程式 A 和 B 在同一個 VM 中運行,因此A跟B共用kernel,而應用程式 C 與其他兩個完全隔離,因為它使用自己的kernel。只會跟前兩個應用程式共用硬體。 在第三種情況下,顯示了相同的三個應用程式運行在容器中。儘管它們都使用相同的kernel,但它們彼此隔離,完全不知道應用程式的存在。隔離是由kernel本身提供的。每個應用程式只看到物理硬體的一部分,並將自己視為作業系統中唯一運行的程序,儘管它們都運行在同一個作業系統中。

使用VM而不是容器的主要優勢是VM提供的完全隔離,因為每個VM都擁有自己的Linux kernel,而容器都使用相同的kernel。 這顯然會帶來安全風險。如果kernel有bug,則一個容器中的應用程式可能會使用此bug來讀取其他容器中的應用程式的記憶體資料。 如果應用程式在不同的VM中運行,因此只會共用硬體,這類的攻擊可能性要低得多。當然,想要完全隔離只能將應用程式運行在獨立的實體機器上來達成。

此外,容器共用記憶體空間,而每個VM使用自己的記憶體區塊(chunk)。因此,如果您不限制容器可以使用的記憶體用量,這可能會導致其他容器記憶體不足或者導致它們的資料被swap到硬碟。

這在k8s中不會發生,因為k8s要求所有節點禁用swap。