2008 - cgroups 與 LXC(容器的地基)#
案件摘要#
2008 年,cgroups(control groups)進入 Linux 核心 2.6.24,同年 LXC(Linux Containers)0.1 發布——
\[ \text{cgroups(資源限制)} + \text{namespaces(隔離視野)} + \text{LXC(包裝)} = \text{容器的地基}. \]
Docker(2013)的全部技術都在 2008 年就緒——Docker 的功勞是把它們變成一個指令。
前因 -- 為什麼會有這個案子#
- Google 的需求:Google 內部所有服務(包括搜尋)早已跑在「容器」裡(Borg 系統,2003–)——Google 工程師(Paul Menage 等)把 cgroups 貢獻給主線核心——雲端巨頭的需求逼出核心機制。
- namespaces 的逐步積累:2.4.19(2002)mount ns → 2.6.24(2008)PID/NET ns → 2.6.30(2009)IPC/USER ns——六維隔離花了 7 年才齊備。
- 彌補的缺陷:Unix 傳統無法限制一群行程的資源(nice 只能調優先級、ulimit 只能限制單一行程)——cgroups 讓「群組級資源配額」成為一等公民。
線索與推理 -- 技術面貌(2008)#
| 技術 | 狀態 | 版本年份脈絡 | 彌補的缺陷 |
|---|---|---|---|
| cgroups | 核心 2.6.24(2008.1) | Google 貢獻 | 無群組級資源配額 |
| namespaces | PID/NET 齊備 | 2.4.19(2002)起逐步 | chroot 只隔離檔案系統 |
| LXC | 0.1(2008.8) | liblxc + 工具 | chroot 太陽春 |
| KVM | 核心 2.6.20(2007.2) | 見 2007-KVM虛擬化.md | VMware 專閉源 |
| ext4 | 穩定化中(2008.10 正式,2.6.28) | extent + 延遲分配 | ext3 的效能上限 |
| GNOME/KDE | GNOME 2.22、KDE 4.0 | 桌面成熟 | 桌面可用性 |
Shell 範例:cgroups 的直接使用#
# cgroup v1(2008):手動掛載與限制
$ mount -t cgroup -o memory none /sys/fs/cgroup/memory
$ mkdir /sys/fs/cgroup/memory/mygroup
$ echo 500M > /sys/fs/cgroup/memory/mygroup/memory.limit_in_bytes
$ echo $$ > /sys/fs/cgroup/memory/mygroup/tasks # 把自己加進群組
$ # 之後這個 shell 的所有行程記憶體上限 500M
# cgroup v2(2016 核心 4.6 正式):統一階層
$ echo "+memory +cpu" > /sys/fs/cgroup/cgroup.subtree_control
$ systemd-run --scope -p MemoryMax=500M myjob # systemd 整合(2014+)
Python 範例:namespaces 的直接呼叫#
import os
# unshare(2008+):建立新的 namespace——隔離視野
# CLONE_NEWPID: 新 PID namespace(容器有自己的 pid 1)
pid = os.fork()
if pid == 0:
os.unshare(os.CLONE_NEWNS | os.CLONE_NEWPID)
print("在新 namespace 中,pid:", os.getpid())
else:
os.wait()
C 範例:LXC 的本質——clone 帶 namespace 旗標#
/* LXC 風格:clone() + namespaces 旗標 = 容器的誕生 */
#define _GNU_SOURCE
#include <sched.h>
#include <sys/wait.h>
char child_stack[65536];
int child_main(void *arg) {
/* 在新 PID/NET/MNT namespace 中執行 */
execl("/bin/sh", "sh", NULL);
return 0;
}
int main(void) {
int flags = CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWNS;
pid_t pid = clone(child_main, child_stack + 65536, flags, NULL);
waitpid(pid, NULL, 0);
return 0;
}
Dockerfile 範例:2008 年的地基 + 2013 年的包裝#
# 2008 年(LXC 时代):手動設定
$ lxc-create -n mycontainer -t ubuntu
$ lxc-start -n mycontainer
# 2013 年(Docker 时代):一個指令
$ docker run -it ubuntu bash # 同樣的 clone+ns+cgroups,體驗天差地遠
結案 -- 後果與影響#
- Docker(2013)與 Kubernetes(2014):全部站在 cgroups/namespaces 的地基上——雲端原生時代。
- systemd(2014):用 cgroups 做服務監督——cgroups 從容器反哺到 init 系統。
- Android(2008):同時用 cgroups 做低記憶體管理(lmkd)。
- cgroup v2(2016,核心 4.6):統一階層、修正 v1 的碎片化——第一版設計的教訓反哺第二版。
- 歷史教訓:地基(核心機制)與包裝(Docker)是兩回事——但包裝決定普及。
關鍵人物與文獻#
- P. Menage, P. Jackson(Google):cgroups (2008, 2.6.24)。
- Linux 核心:namespaces (2002–2009)。
- D. Lezcano, S. Soltesz:LXC、cgroup v2。
- 相關案件:
../UNIX/2013-Docker容器.md、2007-KVM虛擬化.md。