天天看點

容器隔離與限制

Cgroups 就是 Linux 核心中用來為程序設定資源限制的一個重要功能。 有意思的是,Google 的工程師在 2006 年發起這項特性的時候,曾将它命名為“程序容 器”(process container)。實際上,在 Google 内部,“容器”這個術語長期以來都被用于 形容被 Cgroups 限制過的程序組。後來 Google 的工程師們說,他們的 KVM 虛拟機也運作在 Borg 所管理的“容器”裡,其實也是運作在 Cgroups“容器”當中。

這和我們今天說的 Docker 容器差别很大。 Linux Cgroups 的全稱是 Linux Control Group。它最主要的作用,就是限制一個程序組能 夠使用的資源上限,包括 CPU、記憶體、磁盤、網絡帶寬等等。 此外,Cgroups 還能夠對程序進行優先級設定、審計,以及将程序挂起和恢複等操作。

在今天 的分享中,我隻和你重點探讨它與容器關系最緊密的“限制”能力,并通過一組實踐來帶你認識 一下 Cgroups。

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

$ mount -t cgroup 
cpuset on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cpu on /sys/fs/cgroup/cpu type cgroup (rw,nosuid,nodev,noexec,relatime,cpu)
cpuacct on /sys/fs/cgroup/cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct)
blkio on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
memory on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
...      

它的輸出結果,是一系列檔案系統目錄。如果你在自己的機器上沒有看到這些目錄,那你就需要 自己去挂載 Cgroups,具體做法可以自行 Google。 可以看到,在 /sys/fs/cgroup 下面有很多諸如 cpuset、cpu、 memory 這樣的子目錄,也叫 子系統。這些都是我這台機器目前可以被 Cgroups 進行限制的資源種類。而在子系統對應的資 源種類下,你就可以看到該類資源具體可以被限制的方法。比如,對 CPU 子系統來說,我們就 可以看到如下幾個配置檔案,這個指令是:

$ ls /sys/fs/cgroup/cpu
cgroup.clone_children cpu.cfs_period_us cpu.rt_period_us  cpu.shares notify_on_release
cgroup.procs      cpu.cfs_quota_us  cpu.rt_runtime_us cpu.stat  tasks      

兩個參數需要組合使用,可以用來限制程序在長度為 cfs_period 的一段時間内,隻 能被配置設定到總量為 cfs_quota 的 CPU 時間。 而這樣的配置檔案又如何使用呢? 你需要在對應的子系統下面建立一個目錄,比如,我們現在進入 /sys/fs/cgroup/cpu 目錄下:

root@ubuntu:/sys/fs/cgroup/cpu$ mkdir container
root@ubuntu:/sys/fs/cgroup/cpu$ ls container/
cgroup.clone_children cpu.cfs_period_us cpu.rt_period_us  cpu.shares notify_on_release
cgroup.procs      cpu.cfs_quota_us  cpu.rt_runtime_us cpu.stat  tasks      

這個目錄就稱為一個“控制組”。你會發現,作業系統會在你新建立的 container 目錄下,自 動生成該子系統對應的資源限制檔案。 現在,我們在背景執行這樣一條腳本:

$ while : ; do : ; done &
[1] 226      

顯然,它執行了一個死循環,可以把計算機的 CPU 吃到 100%,根據它的輸出,我們可以看到 這個腳本在背景運作的程序号(PID)是 226。 這樣,我們可以用 top 指令來确認一下 CPU 有沒有被打滿:

$ top

%Cpu0 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st

在輸出裡可以看到,CPU 的使用率已經 100% 了(%Cpu0 :100.0 us)。 而此時,我們可以通過檢視 container 目錄下的檔案,看到 container 控制組裡的 CPU quota 還沒有任何限制(即:-1),CPU period 則是預設的 100 ms(100000 us):

$ cat /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us 
-1
$ cat /sys/fs/cgroup/cpu/container/cpu.cfs_period_us 
100000      

接下來,我們可以通過修改這些檔案的内容來設定限制。

比如,向 container 組裡的 cfs_quota 檔案寫入 20 ms(20000 us):

$ echo 20000 > /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us      

結合前面的介紹,你應該能明白這個操作的含義,它意味着在每 100 ms 的時間裡,被該控制組 限制的程序隻能使用 20 ms 的 CPU 時間,也就是說這個程序隻能使用到 20% 的 CPU 帶寬。 接下來,我們把被限制的程序的 PID 寫入 container 組裡的 tasks 檔案,上面的設定就會對該 程序生效了:

$ echo 226 > /sys/fs/cgroup/cpu/container/tasks      

我們可以用 top 指令檢視一下:

$ top
%Cpu0 : 20.3 us, 0.0 sy, 0.0 ni, 79.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st      

可以看到,計算機的 CPU 使用率立刻降到了 20%(%Cpu0 : 20.3 us)。 除 CPU 子系統外,Cgroups 的每一項子系統都有其獨有的資源限制能力,比如:

blkio,為 塊 設 備 設 定 I/O 限 制,一般用于磁盤等裝置;

cpuset,為程序配置設定單獨的 CPU 核和對應的記憶體節點;

memory,為程序設定記憶體使用的限制。

Linux Cgroups 的設計還是比較易用的,簡單粗暴地了解呢,它就是一個子系統目錄加上一組 資源限制檔案的組合。而對于 Docker 等 Linux 容器項目來說,它們隻需要在每個子系統下 面,為每個容器建立一個控制組(即建立一個新目錄),然後在啟動容器程序之後,把這個程序 的 PID 填寫到對應控制組的 tasks 檔案中就可以了。 而至于在這些控制組下面的資源檔案裡填上什麼值,就靠使用者執行 docker run 時的參數指定 了,比如這樣一條指令:

$ docker run -it --cpu-period=100000 --cpu-quota=20000 ubuntu /bin/bash      

在啟動這個容器後,我們可以通過檢視 Cgroups 檔案系統下,CPU 子系統中,“docker”這 個控制組裡的資源限制檔案的内容來确認:

$ cat /sys/fs/cgroup/cpu/docker/5d5c9f67d/cpu.cfs_period_us
100000
 $ cat /sys/fs/cgroup/cpu/docker/5d5c9f67d/cpu.cfs_quota_us 
20000