容器技術的根本在於 OverlayFS,它透過結合上層和下層檔案系統,創造出一個合併的檢視,實作了映像分層。每個映像層都是唯讀的,確保了不可變性,這也是雲端原生應用程式佈署的關鍵原則。當容器啟動時,會在唯讀映像層之上建立一個可寫的精簡層,用於儲存容器執行時的變更。由於這個可寫層是臨時的,因此資料持久化需要透過外部儲存方案,例如繫結掛載或網路儲存。這種設計有效地隔離了應用程式和其執行環境,同時提升了佈署效率和可移植性。然而,容器安全仍需額外考量,例如強制存取控制 (SELinux 或 AppArmor)、Capabilities 和安全計算模式 (Seccomp),以限制容器程式的許可權並防止潛在的攻擊。容器引擎和執行時則負責自動化容器的建立、啟動和管理,簡化了容器操作的複雜性。容器引擎扮演協調者的角色,而容器執行時則負責實際執行容器。這兩個元件的協同運作,確保了容器技術的效率和可靠性。
容器技術:疊加檔案系統與不可變性
容器技術的核心在於隔離性與一致性。其中,疊加檔案系統(OverlayFS)扮演了關鍵角色。根據核心檔案,OverlayFS 將兩個檔案系統合併,一個是「上層」檔案系統,另一個是「下層」檔案系統。這允許我們組合多個目錄樹,形成一個獨特的、壓縮的檢視。
這些目錄被稱為層(layers),分別是 lowerdir 和 upperdir,代表底層目錄和堆積疊在其之上的目錄。合併後的檢視稱為 merged。OverlayFS 最多支援 128 層。
OverlayFS 本身並不知道容器映像的概念;它僅僅是用作基礎技術,以實作 OCI 映像所使用的多層解決方案。OCI 映像也實作了不可變性的概念。映像的層都是唯讀的,無法修改。要更改底層的內容,唯一的辦法是使用適當的更改重建映像。
不可變性是雲端運算方法的重要支柱。它意味著基礎設施(例如例項、容器,甚至是複雜的叢集)只能被不同的版本替換,而不能被修改以實作目標佈署。因此,我們通常不會更改執行中容器內的任何內容(例如,手動安裝套件或更新設定檔),即使在某些情況下這是可行的。相反,我們會用新的更新版本替換其基礎映像。這也確保了執行中容器的每個副本都與其他副本保持同步。
當容器執行時,會在映像之上建立一個新的讀/寫精簡層。這個層是臨時的,因此其上的任何更改都會在容器銷毀後遺失。
這引出了另一個重要的結論:我們不會將任何東西儲存在容器內。它們的唯一目的是為我們的應用程式提供一個工作與一致的執行環境。資料必須從外部存取,方法是在容器內使用繫結掛載或網路儲存(例如網路檔案系統 (NFS)、簡單儲存服務 (S3)、Internet 小型電腦系統介面 (iSCSI) 等)。
容器的掛載隔離和映像分層設計提供了一致的不可變基礎設施,但還需要更多的安全限制,以防止具有惡意行為的程式逃脫容器沙箱,竊取主機的敏感資訊或使用主機攻擊其他機器。以下小節介紹了安全注意事項,以展示容器執行時如何限制這些行為。
容器安全:你需要知道的真相
從安全的角度來看,有一個殘酷的現實需要分享:在容器內執行的程式並不一定比其他程式更安全。惡意攻擊者仍然可以透過主機檔案系統和記憶體資源進行攻擊。為了實作更好的安全隔離,可以使用其他功能:
- 強制存取控制:SELinux 或 AppArmor 可用於強制容器隔離,以防止其影響父主機。這些子系統及其相關的命令列工具使用根據策略的方法,以更好地隔離執行中的程式,包括檔案系統和網路存取。
- Capabilities:當系統中執行非特權程式(意味著有效 UID 與
0不同的程式)時,它會受到根據程式憑證(其有效 UID)的許可權檢查。這些許可權或特權稱為 Capabilities,可以獨立啟用,從而為非特權程式分配有限的特權許可權以存取特定資源。執行容器時,我們可以新增或刪除 Capabilities。 - 安全計算模式 (Seccomp):這是一種原生核心功能,可用於限制程式能夠從使用者空間向核心空間發出的系統呼叫。透過識別程式執行所需的嚴格必要的特權,管理員可以應用 seccomp 設定檔來限制攻擊面。
手動應用上述安全功能並非總是容易和直接的,因為其中一些功能需要一定的學習曲線。能夠自動化和簡化(可能以宣告方式)這些安全約束的工具具有很高的價值。我們將在本章後面的章節中更詳細地討論安全主題。
容器引擎與執行時:協同運作的藝術
雖然手動執行和保護容器在學習角度上是可行的與特別有用,但這是一種不可靠與複雜的方法。它很難在生產環境中重現和自動化,並且容易導致不同主機之間的組態漂移。
這就是容器引擎和執行時誕生的原因——幫助自動化容器的建立以及最終以執行中容器告終的所有相關任務。這兩個概念截然不同,並且往往容易混淆,因此需要釐清:
-
容器引擎是一種軟體工具,它接受和處理來自使用者的請求,以建立具有所有必要引數和引數的容器。它可以被看作是一種協調器,因為它負責將所有必要的動作放到位,以使容器啟動並執行;但它不是容器的有效執行者(容器執行時的角色)。
引擎通常解決以下問題:
- 提供命令列和/或 REST 介面以供使用者互動
- 提取和提取容器映像(將在本章後面討論)
- 管理容器掛載點和繫結掛載提取的映像
- 處理容器元資料
- 與容器執行時互動
我們已經說過,當例項化一個新容器時,會在映像之上建立一個精簡的 R/W 層;此任務由容器引擎完成,該引擎負責向容器執行時呈現合併目錄的工作堆積疊。容器生態系統提供了廣泛的容器引擎選擇。毫無疑問,Docker 是最著名的引擎實作(儘管不是第一個),還有 Podman(本章的核心主題)、CRI-O、rkt 和 LXD。
-
容器執行時是一種低階軟體,容器引擎使用它來在主機中執行容器。容器執行時提供以下功能:
- 使用一組自定義元資料在目標掛載點(通常由容器引擎提供)中啟動容器化程式
- 管理 cgroup 的資源分配
- 管理強制存取控制策略(SELinux 和 AppArmor)和 Capabilities
如今有許多容器執行時,其中大多數都實作了 OCI 執行時規範參考(https://github.com/opencontainers/runtime-spec)。這是一個行業標準,定義了執行時應如何表現以及它應實作的介面。
最常見的 OCI 執行時是 runc,大多數著名的引擎都使用它,以及其他實作,例如 crun、kata-containers、railcar、rkt 和 gVisor。
這種模組化方法讓容器引擎可以根據需要交換容器執行時。例如,當 Fedora 33 出現時,它引入了一個新的預設 cgroups 層次結構,稱為 cgroups V2。runc 最初不支援 cgroups V2,Podman 只是將 runc 與另一個已與新層次結構相容的 OCI 相容容器執行時(crun)交換。現在 runc 最終支援 cgroups V2,Podman 將能夠再次安全地使用它,而不會對最終使用者產生影響。
在介紹了容器執行時和引擎之後,現在是容器介紹期間最受爭論和詢問的問題之一——容器與虛擬機器之間的差異。
容器 vs. 虛擬機器:本質區別
到目前為止,我們已經討論了使用原生作業系統功能實作的隔離,並透過容器引擎和執行時進行了增強。許多使用者可能會被誤導,認為容器是一種虛擬化形式。
事實並非如此;容器不是虛擬機器。那麼,容器與虛擬機器之間的主要區別是什麼?在回答之前,我們可以看看下圖:
玄貓觀點
玄貓認為,容器技術的普及得益於其輕量級、高效和可移植的特性。然而,安全問題始終是容器技術需要關注的重點。選擇合適的容器引擎和執行時,並組態適當的安全策略,對於保護容器化應用程式至關重要。
總之,容器技術透過疊加檔案系統實作了映像分層和不可變性,並透過容器引擎和執行時實作了自動化管理。雖然容器不是虛擬機器,但它們提供了類別似的隔離性和資源管理能力,同時具有更高的效率和靈活性。理解容器技術的核心概念和安全注意事項,對於開發和佈署容器化應用程式至關重要。
玄貓解析容器技術:虛擬化與容器化的深度對比
在探討容器技術之前,讓玄貓先帶大家瞭解一個核心概念:儘管容器是隔離的,但它所執行的行程實際上是透過系統呼叫與主機核心直接互動。這個行程可能感知不到主機的名稱空間,但它仍然需要切換到核心空間,才能執行像是 I/O 存取等操作。
另一方面,虛擬機器(VM)始終執行在 Hypervisor 之上,擁有自己的客作業系統,包含檔案系統、網路、儲存(通常以映像檔形式存在)和核心。Hypervisor 是一種軟體,它為客作業系統提供了一層硬體抽象和虛擬化,使得單一台裸機(bare-metal machine)能夠例項化多個虛擬機器。客作業系統核心所看到的硬體,大部分是虛擬化的硬體,當然也有一些例外。
這意味著,當一個行程在虛擬機器內部執行系統呼叫時,總是會被導向到客作業系統的核心。
簡單來說,容器與主機分享同一個核心,而虛擬機器則有自己的客作業系統核心。這個看似簡單的陳述,實際上引發了很多值得深入思考的導向。
從安全形度來看,虛擬機器提供了更好的隔離,能有效防禦潛在的攻擊。然而,近年來出現的一些根據 CPU 的攻擊(最著名的像是 Spectre 或 Meltdown),可能會利用 CPU 的漏洞來存取虛擬機器的位址空間。
容器技術也在不斷改進其隔離功能,並且可以透過嚴格的安全策略進行組態(例如 CIS Docker、NIST、HIPAA 等),使其更難被利用。
玄貓觀點:為何容器比虛擬機器更具擴充套件性?
從擴充套件性的角度來看,容器的啟動速度比虛擬機器快得多。如果容器映像檔已經存在於主機上,那麼啟動一個新的容器例項只需幾毫秒。這種快速啟動的優勢,也得益於容器的無核心特性。虛擬機器則必須啟動核心和 initramfs,切換到根檔案系統,執行某種型別的 init 程式(例如 systemd),並啟動數量不定的服務。
通常,虛擬機器會比容器消耗更多的資源。為了啟動一個客作業系統,我們通常需要分配更多的記憶體、CPU 和儲存空間,這都比啟動一個容器所需的資源要多。
虛擬機器和容器之間另一個重要的區別在於對工作負載的關注點。對於容器來說,最佳實踐是為每個特定的工作負載啟動一個容器。另一方面,一個虛擬機器可以同時執行不同的工作負載。
以 LAMP 或 WordPress 架構為例:在非生產環境或小型生產環境中,將所有東西(Apache、PHP、MySQL 和 WordPress)都安裝在同一台虛擬機器上是很常見的做法。但如果採用容器技術,這個設計可以被拆分為一個多容器(或多層)架構,其中一個容器執行前端(Apache-PHP-WordPress),另一個容器執行 MySQL 資料函式庫。執行 MySQL 的容器可以存取儲存卷來持久化資料函式庫檔案。同時,擴充套件或縮減前端容器的規模也會變得更加容易。
現在我們瞭解了容器的工作原理,以及它們與虛擬機器的區別,接下來玄貓將帶領大家探討下一個重要問題:為什麼需要容器?
玄貓解析容器的價值:技術與商業的雙重效益
這個問題可以換個方式問:在生產環境中採用容器的價值是什麼?
在當今快速發展、市場驅動的 IT 環境中,變更往往是由業務和技術的改進所驅動。在採用新興技術時,企業總是希望獲得投資回報率(ROI),同時努力將總擁有成本(TCO)控制在合理的範圍內。但這並非總是容易實作。
接下來,玄貓將探討容器技術最重要的優勢。
玄貓經驗:開源如何推動容器技術創新
推動容器技術發展的底層技術是開源的,並且已經成為被許多供應商或社群廣泛採用的開放標準。如今,開源軟體被大型企業、供應商和雲端供應商廣泛採用,具有許多優勢,並為企業提供巨大的價值。開源通常與高價值和創新的解決方案聯絡在一起,而這確實是事實!
首先,社群驅動的專案通常具有強大的進化動力,有助於使程式碼更加成熟,並不斷帶來新的功能。開源軟體可以公開取得,並且可以被檢查和分析。這是一個很好的透明度特性,也會對軟體的可靠性產生影響,無論是在穩健性還是安全性方面。
其中一個關鍵導向是,它促進了一種進化正規化,只有最好的軟體才被採用、貢獻和支援;而容器技術正是這種行為的一個完美範例。
玄貓洞察:容器的可移植性如何降低廠商鎖定風險
我們已經提到,容器是一種讓使用者能夠將應用程式與其整個執行時環境(意味著執行所需的所有檔案)封裝和隔離的技術。這個特性解鎖了一個關鍵優勢:可移植性。
這意味著,無論底層的作業系統發行版是什麼,容器映像檔都可以在任何執行容器引擎的主機上提取和執行。一個 CentOS 或 nginx 映像檔可以從執行容器引擎的 Fedora 或 Debian Linux 發行版中提取,並以相同的組態執行,而不會有任何差異。
同樣地,如果我們擁有一組相同的伺服器,我們可以選擇在其中一台伺服器上排程應用程式例項(例如,使用負載指標來選擇最適合的伺服器),並且確信在執行容器時會得到相同的結果。
容器的可移植性還可以降低廠商鎖定風險,並提供更好的平台互通性。
玄貓實戰:容器如何促進 DevOps 協作
正如之前所說,在將應用程式佈署到生產環境時,容器有助於解決開發團隊和維運團隊之間「在我的機器上可以執行」的老問題。
總結來說,容器技術不僅僅是一種技術趨勢,更是現代 IT 系統中不可或缺的一部分。它透過提高資源利用率、加速應用程式交付、簡化佈署流程和促進 DevOps 協作,為企業帶來了顯著的價值。玄貓認為,隨著雲原生應用的普及,容器技術將在未來扮演更加重要的角色。
容器技術:為何成為現代應用程式的根本?
在現代軟體開發領域,容器技術已成為不可或缺的一環。它們不僅簡化了應用程式的佈署和管理,還促進了開發和營運團隊之間的協作。本文將探討容器技術的優勢、歷史演進以及在現代微服務架構中的應用。
容器:應用程式的智慧包裝解決方案
容器提供了一種簡潔而有效的應用程式封裝方案。對於開發者而言,容器能夠建立自包含的套件,其中包含了應用程式執行所需的所有二進位檔案和組態,確保應用程式在任何環境中都能無縫執行。
對於營運團隊來說,容器隔離了程式,保證了名稱空間和資源使用的分離。這消除了維護複雜依賴關係的需要,並避免了將每個應用程式隔離到虛擬機器中的麻煩。
玄貓認為,從這個角度來看,容器有助於DevOps最佳實踐的實作,開發者和營運者可以更緊密地協作,佈署和管理應用程式,而無需嚴格的職責劃分。
開發者若想構建自己的容器映像,需要更加了解映像中包含的作業系統層,並且營運團隊緊密合作,定義構建範本和自動化流程。
雲端就緒:容器與不可變基礎設施
容器是為雲端而生的,其設計理念根據不可變性。不可變性模式明確指出,基礎設施的變更(無論是單個容器還是複雜的叢集)都必須透過重新佈署修改後的版本來實作,而不是修補現有版本。這有助於提高系統的可預測性和可靠性。
當需要推出新的應用程式版本時,會將其構建到新的映像中,並佈署一個新的容器來取代之前的版本。可以實施構建管道來管理複雜的工作流程,從應用程式構建和映像建立,到映像倉函式庫推播和標記,再到目標主機中的佈署。這種方法大大縮短了組態時間,同時減少了不一致性。
容器協調解決方案(如Kubernetes)提供了自動化大規模主機排程模式的方法,並使容器化工作負載易於佈署、監控和擴充套件。
基礎設施最佳化:輕量級與高效能
與虛擬機器相比,容器具有輕量級的特性,這大大提高了計算和記憶體資源的利用效率。透過簡化工作負載執行的方式,容器的採用帶來了巨大的成本文約。
IT資源的最佳化是透過降低應用程式的計算成本來實作的。如果一個在虛擬機器上執行的應用程式伺服器可以被容器化,並且其他容器一起在主機上執行(具有專用的資源限制和請求),則可以節省和重複使用計算資源。
整個基礎設施都可以用這種新模式重新調整。以前組態為Hypervisor的裸機可以重新分配為容器協調系統的工作節點,該系統可以簡單地將更細粒度的容器化應用程式作為容器執行。
微服務架構:容器的理想歸宿
微服務架構將應用程式拆分為多個服務,這些服務執行細粒度的功能,並且是整個應用程式的一部分。
傳統應用程式採用單體架構,其中所有功能都是同一個例項的一部分。微服務的目的是將單體拆分為更小的部分,這些部分可以獨立互動。
單體應用程式非常適合容器,但微服務應用程式與容器的比對度更高。為每個微服務提供一個容器有助於實作重要的優勢,例如:
- 微服務的獨立擴充套件性
- 開發團隊雲端存取程式更明確的職責
- 在不同的微服務上採用不同技術堆積疊的可能性
- 更好地控制安全性方面(例如,導向公眾的公開服務、mTLS連線等)
在處理大型與複雜的架構時,協調微服務可能是一項艱鉅的任務。採用Kubernetes等協調平台、Istio或Linkerd等服務網格解決方案以及Jaeger和Kiali等追蹤工具對於實作對複雜性的控制至關重要。
容器的歷史淵源:從Chroot到FreeBSD Jails
容器技術並非電腦產業中的新課題。它在作業系統歷史中根深蒂固。
Chroot與Unix V7
如果我們想為容器歷史中的旅行時間建立一個事件時間表,那麼第一個也是最古老的目的地是1979年——Unix V7的年份。當時,在1979年,一個重要的系統呼叫被引入到Unix核心中——chroot系統呼叫。
重要提示
系統呼叫(或syscall)是應用程式用來向作業系統核心請求某些東西的方法。
這個系統呼叫允許應用程式更改其自身及其子行程的執行副本的根目錄,從而消除了執行軟體逃脫該jail的任何能力。此功能允許您禁止執行應用程式存取給定子樹之外的任何型別的檔案或目錄,這在當時確實是一個改變遊戲規則的因素。
幾年後,在1982年,這個系統呼叫也被引入到BSD系統中。
不幸的是,此功能的構建並未考慮安全性,並且多年來,作業系統檔案和安全文獻強烈建議不要使用chroot jails作為實作隔離的安全機制。
Chroot只是在*nix系統中實作完整行程隔離的旅程中的第一個里程碑。從歷史的角度來看,下一個是FreeBSD jails的引入。
FreeBSD jails
在我們的歷史之旅中向前邁進幾步,我們跳回(或向前,取決於我們從哪裡看)到2000年,當時FreeBSD作業系統批准並發布了一個新概念,該概念擴充套件了舊的與良好的chroot系統呼叫——FreeBSD jails。
重要提示
FreeBSD是一個免費與開源的類別Unix作業系統,於1993年首次發布,源自Berkeley Software Distribution,後者最初根據Research Unix。
正如我們之前簡要報導的那樣,chroot在80年代是一個很棒的功能,但它建立的jail很容易被逃脫並且有很多限制,因此它不適合複雜的場景。因此,FreeBSD jails構建在chroot syscall之上,旨在擴充套件和擴大其功能集。
在標準的chroot環境中,執行行程僅在檔案系統級別具有限制和隔離;所有其他內容,例如執行行程、系統資源、網路子系統和系統使用者,都由chroot內部的行程和主機系統的行程分享。
FreeBSD jails的主要功能是網路子系統、系統使用者及其行程的虛擬化;正如您可以想像的那樣,這大大提高了解決方案的靈活性和整體安全性。
讓我們先概括FreeBSD jail的四個關鍵功能:
- 目錄子樹:這也是我們在chroot jail中已經看到的。基本上,一旦定義為子樹,執行行程就被限制為該子樹,並且無法從中逃脫。
- IP地址:這是一場偉大的革命;最後,我們可以為我們的jail定義一個獨立的IP地址,並讓我們的執行行程甚至與主機系統隔離。
- 主機名稱:在jail內部使用,這當然與主機系統不同。
- 命令:這是正在執行的可執行檔案,並且有一個選項可以在系統jail內部執行。可執行檔案具有一個相對路徑,該路徑包含在jail中。
這種jail的一個優點是,每個例項也有自己的使用者和root帳戶,這些帳戶對其他jails或底層主機系統沒有任何特權或許可權。
FreeBSD jails的另一個有趣的功能是,我們有兩種安裝/建立jail的方法:
- 從二進位檔案-反映了我們可以使用底層作業系統安裝的二進位檔案
- 從原始碼-從頭開始構建最終應用程式所需的內容
Solaris Containers(也稱為Solaris Zones)
回到我們的時間機器,我們只需要跳幾年到2004年,才能最終遇到我們可以識別的第一個措辭——Solaris Containers。
重要提示
Solaris是一個專有的Unix作業系統,於1993年從SunOS誕生,最初由Sun Microsystems開發。
容器技術
容器技術的發展歷程,從最初的chroot到現在的Kubernetes,體現了軟體開發和佈署方式的持續演進。容器不僅簡化了應用程式的封裝和佈署,還促進了微服務架構的發展,提高了資源利用率,並加速了DevOps的實踐。玄貓認為,隨著雲原生技術的普及,容器技術將在未來繼續扮演關鍵角色,推動軟體產業的創新和發展。