為什麼建議一個容器中只執行一個程式

JasonCeng發表於2021-05-26

在雲原生與容器化時代浪潮下,大多數新手的普遍認識是“容器=虛擬機器”,既然容器等同於虛擬機器,那麼在容器中想執行多少個程式就執行多少個程式。

作為從新手村走過來的人,筆者想為這個想法糾偏,避免大家和我走一樣的彎路。有兩個概念我們要理清:第一,容器不等同於虛擬機器;第二,容器中不建議執行多個程式。

本文以Docker容器為主要討論展開。

為什麼說容器不等同於虛擬機器呢?

我們來看一個較為學術的定義:A docker container is not a full virtual machine to run a complete stack of application instances and services. Docker container is the application; or more accurately a service that helps to make up the application. 即,Docker容器不是一個執行完整的應用程式例項和服務堆疊的完整虛擬機器。Docker容器是一種應用程式,或更準確地說是一種有助於構建應用程式的服務。

雖然容器不等同於虛擬機器,但它們也有相似之處,容器與虛擬機器擁有著類似的使命:對應用程式及其關聯性進行隔離,從而構建起一套能夠隨處執行的自容納單元。此外,容器與虛擬機器還擺脫了對物理硬體的直接控制,允許我們更為高效地使用計算資源,從而提升能源效率與成本效益。[1]

那麼,容器和虛擬機器的不同點究竟在哪呢?筆者在這裡做了一個通俗形象的類比。首先,對於物理機、虛擬機器和容器在基礎架構上的不同,可以類比為獨棟別墅、小區樓盤和膠囊式公寓的區別。

物理機

物理機就像獨棟別墅,擁有獨立的基礎設施,水管、電線、地基等都由自己獨享,也就是說,網路卡、記憶體、CPU等硬體都由自身作業系統獨佔。

物理機上的硬體可以看作是基礎設施(Infrastructure),沒有基礎設施一切都無從談起,硬體之上則是主作業系統(Host Operating System)。除此之外,還有各類基礎軟體,如各類硬體配套的驅動軟體。

這裡還要再重點介紹的是Hypervisor,它(也稱為虛擬機器監視器或VMM)是建立和執行虛擬機器(VM)的軟體。虛擬機器管理程式通過虛擬共享其資源(例如記憶體和CPU),使一臺主機可以支援多個Guest OS

虛擬機器

虛擬機器就像小區樓盤,所有房子共享地基,但房子內部有自己獨立了電線、水管,雖然最終也是走的統一水電管網出小區。也就是說,多個虛擬機器之間,網路卡、記憶體、CPU等硬體雖然最終是共用一套,但在虛擬機器內部是由獨立的一套虛擬硬體進行運轉的。

前面我們介紹了hypervisorhypervisor是一種虛擬化伺服器的軟體,這是在物理機及宿主機作業系統上執行虛擬機器VM的基礎,它幫助我們對硬體進行虛擬化。

虛擬機器裡的作業系統我們稱為Guest OS,這是一個完整的作業系統,只要安裝應用程式執行所必需的二進位制檔案和庫,在其之上便可以執行應用程式,且能確保各個虛擬機器之間、虛擬機器與宿主機之間的環境完全獨立、資源相互隔離。

由於虛擬機器裡執行的是一個完整的作業系統,且需要虛擬出Guest OS執行時所需的所有硬體的虛擬副本,這意味著虛擬機器會佔用大量系統資源,特別是CPU和記憶體的資源佔用率會比較高,且虛擬機器的空間佔用可以高達數GB。不過在合理範圍內,虛擬機器可以利用固有的硬體資源虛擬出多臺完整VM,其存在仍然有價值,與單獨執行的物理機相比仍然是經濟的。

虛擬機器是偉大的,它通過抽象來增加並行,服務於多作業系統的使用,並提供業界最好的安全性。但對於隔離,它們相當昂貴。[2]

圖1. 虛擬機器層級圖

容器

容器就像膠囊式公寓,所有隔間不僅共享地基,連電線、水管也都是共享的,且每個隔間的空間有限,彼此之間也相互隔離。當然,他們是共用大房間裡的基礎設施,如公共空間。

也就是說,容器是共用物理網路卡、記憶體、CPU的。只有當他們之間需要通訊時,才會採用容器層面的網橋docker0,而對於其他硬體,可以簡單認為每個容器佔有了限定範圍內的資源(如RAM、CPU等)。

容器只是執行在宿主機上的一種特殊程式,多個容器之間使用的還是同一個宿主機的作業系統核心。可以認為,容器是一個不依賴於作業系統,執行應用程式的環境。

容器通過Linux的NamespaceCgroups技術對應用程式程式進行隔離和資源限制。

Namespace的作用是環境隔離,它讓應用程式只看到該Namespace內的世界。而Cgroups 的作用是限制分配給程式的宿主機資源。不過,對於宿主機來說,這些被“隔離”了的程式跟其他程式並沒有太大區別。

對於Namespace技術,這裡做一個稍微深入一點的解讀。通過Mount Namespace,容器可以修改程式對自己的檔案系統“掛載點”的認知。在容器程式啟動之前重新掛載它的整個根目錄"/",這個掛載在容器根目錄上、用來為容器程式提供隔離後執行環境的檔案系統,就是所謂的“容器映象”。它還有一個更為專業的名字,叫作:rootfs(根檔案系統)。rootfs只是一個作業系統所包含的檔案、配置和目錄,並不包括作業系統核心。同一臺機器上的所有容器,都共享宿主機作業系統的核心。[3]

Linux CGroup全稱Linux Control Group,是Linux核心的一個功能,用來限制,控制與分離一個程式組群的資源(如CPU、記憶體、磁碟輸入輸出等)。Cgroup可讓您為系統中所執行任務(程式)的使用者定義組群分配資源—比如CPU時間、系統記憶體、網路頻寬或者這些資源的組合。您可以監控您配置的Cgroup,拒絕Cgroup訪問某些資源,甚至在執行的系統中動態配置您的Cgroup[4]

回過頭來談談作業系統核心,由於同一臺宿主機上的所有容器都共享宿主機的作業系統核心,那麼如果有一個容器裡的應用程式需要配置核心引數,跟核心進行直接互動,則這些引數對所有容器來說就像一個“全域性變數”,牽一髮而動全身。

這也是容器劣勢的主要原因,正是因為容器共享宿主機作業系統核心,因此不能像虛擬機器一樣模擬出完整的硬體機器充當沙盒,從而實現完全隔離。也就是說,容器是程式級的隔離,它可以通過影響宿主機作業系統核心來影響其他容器。

但容器還是有很多優勢。首先,容器的空間佔用比虛擬機器小很多,甚至可以小到10MB,和虛擬機器動則數GB相比十分小巧;其次,容器能輕鬆限制記憶體和CPU使用率,相比虛擬機器採用hypervisor來實現虛擬化更加輕量;同時,正是由於容器體積小、採用的技術(Containerzation Engine)更輕量,使得它啟動十分迅速,這十分有利於快速擴充套件。

值得一提的是,在DevOps理念日益流行的時代,容器對於持續整合和持續部署(CI/CD)實施也是極好的選擇,它使得開發人員更容易構建、分發以及快速部署他們的應用程式。

圖2. 容器層級圖

為什麼說容器中不建議執行多個程式呢?

聊過虛擬機器與容器的區別之後,讓我們暫時忘記架構和軟體工程哲學。在前面的討論中我們已經知道,容器其實是應用程式抽象出來的可相互隔離的執行緒。儘管單個容器中確實可以執行多個應用程式,但出於實際原因,您可能需要考慮遵循“每個容器一個應用程式”的經驗法則。

1、每個容器中只執行一個應用程式,則水平伸縮將變得十分容易。例如,當你需要一個Tomcate容器,可以從現有的容器再擴充套件出一個,但如果你的這個容器中不僅有Tomcate,還有MySQL等其他應用程式,事情就會變得複雜起來。

2、每個容器中只執行一個應用程式,可以輕鬆地將其重新用於其他專案或目的,極大增加複用度。

3、每個容器中只執行一個應用程式,出現故障時開發人員能方便地對該故障容器進行問題排查,而不必對整個系統的各個部分進行排查,這也使得其更具有可移植性和可預測性。

4、每個容器中只執行一個應用程式,升級程式時能夠將影響範圍控制再更小的粒度,極大增加應用程式生命週期管理的靈活性,避免在升級某個服務時中斷相同容器中的其他程式。

5、每個容器中只執行一個應用程式,從安全性和隔離性角度來看,能夠提供更安全的服務和應用程式間的隔離,以保持強大的安全狀態或遵守PCI之類的規定。[5]

話說回來,容器本身的設計,就是希望容器和服務/應用能夠具備相同的生命週期。即:一個容器對應一個程式。這樣,才能夠最好地應用容器編排來管理好容器和服務。

綜上,建議單個容器中只執行一個獨立的程式。

參考

[1] https://blog.csdn.net/qq_24624539/article/details/103445229

[2] http://dockone.io/article/723

[3] https://zhuanlan.zhihu.com/p/157749762

[4] https://coolshell.cn/articles/17049.html

[5] https://devops.stackexchange.com/questions/447/why-it-is-recommended-to-run-only-one-process-in-a-container

(全文完)

更多關於大資料、分散式、儲存、區塊鏈、Linux相關文章請關注微信公眾號:asympTech漸進線實驗室

Github、知乎、部落格園、CSDN、簡書全網唯一id:JasonCeng

相關文章