容器進階:隔離與限制

Blog:博客園 個人

參考:深入剖析Kubernetes LINUX NAMESPACE(上) cgroup 原理分析

容器其實是一種沙盒技術。顧名思義,沙盒就是能夠像一個集裝箱一樣,把你的應用「裝」起來的技術。這樣,應用與應用之間,就因為有了邊界而不至於相互干擾;而被裝進集裝箱的應用,也可以被方便地搬來搬去。

容器如何去實現這個邊界來避免應用之間的互相干擾?這就涉及到Linux的兩個技術:Cgroups技術Namespace技術

  • Cgroups技術:用來製造約束
  • Namespace技術:用來修改進程視圖

Namespace技術介紹

先說結論,Docker 容器這個聽起來玄而又玄的概念,實質是指定了容器進程所需要啟用的一組Namespace參數。這樣,容器就只能「看」到當前 Namespace 所限定的資源、文件、設備、狀態,或者配置。而對於宿主機以及其他不相關的程序,它就完全看不到了。也就是說,容器是一種特殊的進程。

Namespace種類

Linux Namespace 有如下種類

分類 系統調用參數 隔離內容 相關內核版本
Mount CLONE_NEWNS 掛載點(文件系統) Linux 2.4.19
UTS CLONE_NEWUTS 主機名和域名 Linux 2.6.19
IPC CLONE_NEWIPC 信號量、消息隊列和共享內存 Linux 2.6.19
PID CLONE_NEWPID 進程編號 Linux 2.6.24
Network CLONE_NEWNET 網絡設備、網絡棧、端口等 始於Linux 2.6.24 完成於 Linux 2.6.29
User CLONE_NEWUSER 用戶和用戶組 始於 Linux 2.6.23 完成於 Linux 3.8)
Cgroup CLONE_NEWCGROUP Cgroup根路徑 Linux 4.6

相關係統調用

  • clone():實現線程的系統調用,用來創建一個新的進程,並可以通過設計上述參數達到隔離。
  • unshare() :使某進程脫離某個namespace
  • setns():把某進程加入到某個namespace
  • ioctl_ns:查看namespace的信息

在 Linux 系統中創建線程的系統調用是clone(),當我們用clone()系統調用創建一個新進程時,就可以在參數中指定 CLONE_NEWPID參數。這時,新創建的這個進程將會「看到」一個全新的進程空間,在這個進程空間里,它的 PID
是1。 但是這個進程既看不到宿主機里真正的進程空間,也看不到其他 PID Namespace 里的具體情況。

缺點

基於 Linux Namespace 的隔離機制相比於虛擬化技術也有很多不足之處,其中最主要的問題就是:隔離得不徹底

原因如下:

  • 運行在同一個宿主機上的容器共用一個操作系統內核(宿主機內核);
  • 有很多資源和對象是不能被 Namespace 化的 ,比如時間,若在某個容器中修改系統時間,則宿主機及宿主機所運行的所有容器的時間都被修改;
  • 應用「越獄」的難度自然也比虛擬機低得多 ;

Cgroups技術介紹

一個正在運行的 Docker 容器,其實就是一個啟用了多個 Linux Namespace 的應用進程,而這個進程能夠使用的資源量,則受Cgroups配置的限制。

Cgroups 是 Control Groups 的縮寫,是 Linux 內核提供的一種可以限制、記錄、隔離進程組 (process groups) 所使用的物理資源 (如 cpu memory i/o 等等) 的機制。2007 年進入 Linux 2.6.24 內核,CGroups 不是全新創造的,它將進程管理從 cpuset 中剝離出來,作者是 Google 的 Paul Menage。CGroups 也是 LXC 為實現虛擬化所使用的資源管理手段。

術語

  • task(任務):cgroups 的術語中,task 就表示系統的一個進程。
  • cgroup(控制組):cgroups 中的資源控制都以 cgroup 為單位實現。cgroup 表示按某種資源控制標準劃分而成的任務組,包含一個或多個子系統。一個任務可以加入某個 cgroup,也可以從某個 cgroup 遷移到另外一個 cgroup。
  • subsystem(子系統):cgroups 中的 subsystem 就是一個資源調度控制器(Resource Controller)。比如 CPU 子系統可以控制 CPU 時間分配,內存子系統可以限制 cgroup 內存使用量。
  • hierarchy(層級樹):hierarchy 由一系列 cgroup 以一個樹狀結構排列而成,每個 hierarchy 通過綁定對應的 subsystem 進行資源調度。hierarchy 中的 cgroup 節點可以包含零或多個子節點,子節點繼承父節點的屬性。整個系統可以有多個 hierarchy。

作用

  • 資源限制(Resource Limitation):cgroups 可以對進程組使用的資源總額進行限制。如設定應用運行時使用內存的上限,一旦超過這個配額就發出 OOM(Out of Memory)。
  • 優先級分配(Prioritization):通過分配的 CPU 時間片數量及硬盤 IO 帶寬大小,實際上就相當於控制了進程運行的優先級。
  • 資源統計(Accounting): cgroups 可以統計系統的資源使用量,如 CPU 使用時長、內存用量等等,這個功能非常適用於計費。
  • 進程控制(Control):cgroups 可以對進程組執行掛起、恢復等操作。

在 Linux 中,Cgroups 給用戶暴露出來的操作接口是文件系統,即它以文件和目錄的方式組織在操作系統的 /sys/fs/cgroup 路徑下。

[root@test ~]# mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)

總結

容器實質是一個「單進程」模型。

Namespace 的作用是「隔離」,它讓應用進程只能看到該Namespace 內的「世界」;而 Cgroups 的作用是「限制」,它給這個「世界」圍上了一圈看不見的牆。這麼一折騰,進程就真的被「裝」在了一個與世隔絕的房間里,而這些房間就是 PaaS 項目賴以生存的應用「沙盒」。
由於一個容器的本質就是一個進程,用戶的應用進程實際上就是容器里 PID=1 的進程,也是其他後續創建的所有進程的父進程。這就意味着,在一個容器中,你沒辦法同時運行兩個不同的應用,除非你能事先找到一個公共的 PID=1 的程序來充當兩個不同應用的父進程,這也是為什麼很多人都會用 systemd 或者 supervisord 這樣的軟件來代替應用本身作為容器的啟動進程。