從機器虛擬機到容器核心:Linux cgroups 與 Namespaces 如何重新定義作業系統邊界?#
三種隔離的系譜:VM、容器與沙盒的憲法分野#
要理解容器,先看清隔離技術的光譜。2010 年代的「雲原生革命」實質上是三條隔離路線的競爭與合流:
flowchart TD
A[隔離技術光譜] --> B[虛擬機 hypervisor]
A --> C[容器 OS 級虛擬化]
A --> D[應用沙盒]
B --> B1[硬體虛擬化 VT-x<br/>每 VM 一個完整核心<br/>重但隔離最強<br/>VMware 1999 / Xen 2003 / KVM 2007]
C --> C1[共享核心 + namespaces/cgroups<br/>輕量到秒級啟動<br/>chroot 1979 / FreeBSD jail 2000<br/>LXC 2008 / Docker 2013]
D --> D1[行程級權限約束<br/>行動沙盒 9.3 節<br/>Chrome site isolation 2012]容器不是新發明——chroot(1979,Bill Joy 的 UNIX 第七版)已能改變行程的根目錄;FreeBSD jail(2000)與 Solaris Zones(2004)完善了 OS 級虛擬化;Google 從 2003-2006 年開始在內部把一切工作負載跑進容器(Borg 系統,2015 年論文公開,其基因直接孕育 Kubernetes)。真正的歷史轉折是 2013 年 3 月,Solomon Hykes 在 PyCon 的五分鐘演示:Docker 把 Linux 核心的 namespaces/cgroups 打包成「docker run 一行命令」——技術早已存在,Docker 發明的是開發者體驗與鏡像分發(Registry、Dockerfile、不可變鏡像層)。六個月內 Docker 成為 GitHub 最熱門專案,2014 年 AWS 推出 ECS,雲原生革命正式啟動。
Namespaces:偽造一整個作業系統的視野#
Namespace 的本質是對系統全域資源做「按行程分組的視圖隔離」——每個 namespace 內的行程以為自己獨享整台機器。Linux 陸續提供的六(後加至八)種 namespace 構成容器的憲法條款:
| Namespace | 隔離內容 | 歷史 |
|---|---|---|
| mount | 檔案系統掛載點 | 2002(Linux 2.4.19) |
| UTS | 主機名與網域 | 2006 |
| IPC | System V IPC 物件 | 2006 |
| PID | 行程編號空間(容器內 PID 1) | 2008(2.6.24) |
| network | 網卡、路由、防火牆規則 | 2008 |
| user | UID/GID 映射(root 降權) | 2012-2013 |
| cgroup(ns) | cgroup 視圖 | 2016 |
| time | 時鐘偏移 | 2020 |
這份表格藏著一段漫長的制度演化:Linux 的 namespace 支援是自 2002 年起、由虛擬化需求(OpenVZ/VServer 等前輩專案的實踐)與安全需求(user namespace 讓容器內 root 並非真 root)逐條推動的——它不是一次性的設計,而是十五年「以核心補丁促主線接納」的漸進立法。容器內的 PID 1 之謎(init 行程的角色與訊號處理)、mount namespace 與 overlayfs(多層鏡像:唯讀基層+CoW 寫層,8.3 節的檔案系統轉世)等細節,共同把「一個行程」包裝成「一台機器」的幻覺。
cgroups:資源的配額憲法#
Namespace 解決「看得見什麼」,cgroups(control groups)解決「用得了多少」。2006 年 Google 工程師 Paul Menage 與 Rohit Seth 提交了「process containers」補丁,2007 年併入主線時更名 cgroups;2016 年的 cgroup v2 統一了層級語義。它為每組行程提供四類配額:
- CPU:shares/配額(底層就是 CFS 的組群排程,7.3 節!cgroup 是 CFS group scheduling 的直接用戶);
- 記憶體:上限+OOM 行為(8.1 節的記憶體管理擴展);
- block I/O:磁碟頻寬;
- 裝置存取:device whitelist。
cgroups 與 namespaces 的組合,在宏核心內部實現了微核心夢寐以求的隔離(5.3 節的判決在此獲得歷史平反)——只是隔離的單位不是「伺服器行程」而是「資源視圖+配額」。Docker 容器的本質,用一行話說:一組被 namespaces 重新框定視野、被 cgroups 限定預算、以 overlayfs 疊層提供檔案系統的普通 Linux 行程。沒有第二個核心、沒有硬體虛擬化——輕如鴻毛(毫秒級啟動、個位數 MB 開銷),重如鐵幕(生產環境的隔離等級卻低於 VM,2019 年 runC 漏洞 CVE-2019-5736、2022 年容器逃逸案例提醒世人:共享核心即共享命運,安全關鍵場景仍需 gVisor/Kata 這類「沙盒容器」補強)。
重新定義作業系統邊界:三個哲學後果#
容器革命的深遠之處,在於它改寫了三個作業系統的憲政前提:
1. 「一台機器一個 OS」的崩塌。 傳統憲法下,OS 是機器的唯一主權者;VM 讓多個 OS 共存,容器則更進一步——多個「準系統」在同一核心內共存。OS 的定義從「管理硬體的軟體」變成「管理資源視圖的軟體」:邊界不再由硬體劃定,而由核心的會計帳本劃定。
2. 應用與環境的綁定(immutability)。 「在我的機器上能跑」是軟體工程永恆的痛(依賴地獄,呼應 3.2 節的組合困境);Docker 鏡像把應用+運行時+函式庫+設定凍結為不可變成品——部署單位從「程式」變成「檔案系統快照」。這是函數式程式設計的不可變性思想在部署層的勝利,也是 DevOps 運動(2009 年起)「基礎設施即代碼」的技術基石。
3. 排程與計費的對象上移。 雲端廠商按容器/VM 小時計費(AWS Lambda 更以函式毫秒計費,2014),cgroup 的會計資料成為商業計費的原始憑證——CTSS 的按量計費(2.2 節)在六十年後以 docker stats 的形式回歸,只是計量的對象從 CPU 秒擴展到 CPU、記憶體、網路、GPU 的全維度。
timeline
title 容器演化
1979 : chroot(UNIX v7)
2000 : FreeBSD jail
2003-06 : Google Borg 內部容器化
2006-08 : cgroups 併入 Linux 主線<br/>namespaces 陸續就位
2008 : LXC
2013 : Docker 發布
2014 : AWS ECS;Kubernetes 開源
2015 : OCI 標準成立
2016 : cgroup v2
2019-20 : Kubernetes 成為事實標準<br/>→ 10.2 節結語:容器的父親是誰#
容器革命的因果鏈橫跨四十年:chroot 給了根目錄、jail/Zones 給了實踐、Google Borg 給了規模驗證、cgroups/namespaces 給了核心機制、Docker 給了開發者體驗、Kubernetes 給了編排與治理(下一節)。每一代人都以為自己在發明新東西,實際上都在為同一個古老想法——「安全地共享一台機器」——添磚加瓦。而當容器成為部署的原子單位,下一個問題自然浮現:成千上萬的容器散布在成千上萬的機器上,誰來當它們的政府?——分散式作業系統的時代來臨了。