OCI 執行時(OCI runtime)
/ OH-see-eye /
當你打一道命令去啟動一個容器時,必須有某個東西去做那件「把下載來的映像變成一個活生生、被隔離的行程」的實際底層工作。那個東西就是容器執行時(container runtime),而開放容器倡議(OCI)把它必須做什麼標準化了,好讓任何工具都能驅動任何相容的執行時。OCI 定義了兩份彼此契合的規範:映像規範(容器映像長什麼樣,另條目處理)與執行時規範(如何拿一個解開的映像加上一份設定、真的把它跑起來)。那個參考級的底層執行時是一個叫 runc 的小程式。
把執行時做的事走一遍,因為它把這整個領域串了起來。它被交給一個目錄——映像解開後的根檔案系統——以及一份 config.json,描述所要的容器:要建立哪些命名空間、套用哪些 cgroup 限制、掛載什麼、以哪個使用者身分執行、以及要執行的命令。執行時接著做你已經見過的那些核心呼叫:它建立所要求的命名空間(PID、net、mount 等等)讓行程得到它被隔離的視野、把行程放進 cgroups 使其資源有界、設好根檔案系統、卸下特權、最後執行容器的進入行程。從核心的角度看,根本沒有一個「容器」物件——只有一個穿著命名空間與 cgroups 的行程,完全照執行時安排的那樣。
這個分層為何重要:這個乾淨的切分意味著你日常使用的高階工具(Docker、Podman、透過其 CRI 的 Kubernetes)處理映像、網路與編排,再把最後那一步「真的把容器建出來」委派給一個小巧、可替換的 OCI 執行時,例如 runc。因為這個介面被標準化了,你可以用一個建立更強沙箱的不同執行時去取代 runc,而完全不必更動它之上的任何東西——microVM 執行時與使用者空間核心執行時正是這樣插進來的。誠實的定位是:OCI 執行時是那塊薄薄的、無趣卻不可或缺的零件,把一份規範轉換成真實的命名空間與 cgroups;容器的魔法大半其實是它所喚起的那些核心功能,被標準化了,好讓整個生態系互通。
給定一個根檔案系統與一份 config.json,命令 runc run mycontainer 便建立設定裡指名的命名空間與 cgroups,並執行所列的行程。像 Docker 這樣的高階工具準備好映像與設定,再呼叫一個 runc 相容的執行時去做這最後一步。
runc 把一個映像加一份 config.json 變成真實的命名空間與 cgroups,再執行那個行程。
核心裡沒有一個「容器」物件——OCI 執行時只是繞著一個行程組裝命名空間、cgroups 與一個根檔案系統。因為這個介面被標準化了,runc 可以被換成一個隔離更強的執行時(一個 microVM 或使用者空間核心沙箱),而不必更動它之上的任何東西。