2 限制容器
Linux Namespace建立了一個“容器”,為什麼還要對容器做“限制”?
以PID Namespace為例:
雖然容器内的第1号程序在“障眼法”幹擾下隻能看到容器裡的情況,但在主控端,它作為第100号程序與其他所有程序之間依然是平等競争關系。
這意味着,雖然第100号程序表面上被隔離,但它所能夠使用到的資源(比如CPU、記憶體),可随時被主控端其他程序(或容器)占用。當然,該100号程序也可能自己就把所有資源吃光。這些顯然都不是一個“沙盒”的合理行為。
于是,就有了下面的
3 Cgroups( control groups)
2006由Google工程師發起,曾将其命名為“程序容器”(process container)。實際上,在Google内部,“容器”這個術語長期以來都被用于形容被Cgroups限制過的程序組。後來Google的工程師們說,他們的KVM虛拟機也運作在Borg所管理的“容器”裡,其實也是運作在Cgroups“容器”當中。這和我們今天說的Docker容器差别很大。
2008年并入 Linux Kernel 2.6.24。它最主要的作用,就是限制一個程序組能夠使用的資源上限,包括CPU、記憶體、磁盤、網絡帶寬等等。
Docker實作CPU、記憶體、網絡的限制也均通過cgroups實作。
此外,Cgroups還能夠對程序進行優先級設定、審計,以及将程序挂起和恢複等操作。隻探讨它與容器關系最緊密的“限制”能力,并通過一組實踐來認識一下Cgroups。
在Linux中,Cgroups給使用者暴露出來的操作接口是檔案系統,即它以檔案和目錄的方式組織在os的/sys/fs/cgroup路徑。
在筆者的 CentOS7 VM裡,可以用mount指令把它們展示出來

它的輸出結果,是一系列檔案系統目錄(如果你在自己的機器上沒有看到這些目錄,那你就需要自己去挂載Cgroups)
在/sys/fs/cgroup下面有很多諸如cpuset、cpu、 memory這樣的子目錄,也叫子系統
這些都是我這台機器目前可以被Cgroups進行限制的資源種類。
而在子系統對應的資源種類下,你就可以看到該類資源具體可以被限制的方法。
譬如,對CPU子系統來說,就可以看到如下配置檔案
注意到cfs_period和cfs_quota這樣的關鍵詞,這兩個參數需要組合使用,可用來
限制程序在長度為cfs_period的一段時間内,隻能被配置設定到總量為cfs_quota的CPU時間
這樣的配置檔案如何使用呢?
需要在對應的子系統下面建立一個目錄
比如,我們現在進入/sys/fs/cgroup/cpu目錄下:
這個目錄就稱為一個“控制組”。
OS會在你新建立的container目錄下,自動生成該子系統對應的資源限制檔案!
4 Cgroups實戰
4.1 建立 CPU 100%的程序
- 執行腳本
死循環可緻CPU 100%,top确認:
此時,可以檢視container目錄下的檔案,看到
- container控制組裡的CPU quota還沒有任何限制(即:-1)
CPU period則是預設的100 ms(100000 us):
4.2 限制該程序
接下來修改這些檔案的内容來設定限制。
- 向container組裡的cfs_quota檔案寫入20 ms(20000 us)
- 即100ms,被該控制組限制的程序隻能使用20ms的CPU,即該程序隻能使用到20%的CPU帶寬。
- 接下來把被限制的程序的PID寫入container組裡的tasks檔案,上面的設定就會對該程序生效
Docker容器實戰(六) - Docker是如何實作隔離的?(下)2 限制容器3 Cgroups( control groups)4 Cgroups實戰5 Docker中如何限制?6 總結 - top,可見CPU使用率立刻降到20%
除CPU子系統外,Cgroups的每一項子系統都有其獨有的資源限制能力,比如:
- blkio,為塊裝置設定I/O限制,一般用于磁盤等裝置
- cpuset,為程序配置設定單獨的CPU核和對應的記憶體節點
- memory,為程序設定記憶體使用的限制
5 Docker中如何限制?
Cgroups 就是一個子系統目錄加上一組資源限制檔案的組合
而對于Docker等Linux容器,隻需在每個子系統下面,為每個容器建立一個控制組(即建立一個新目錄),然後在啟動容器程序之後,把這個程序的PID填寫到對應控制組的tasks檔案中!
而至于在這些控制組下面的資源檔案裡填上什麼值,就靠使用者執行docker run時的參數指定
- Docker ≥1.13
docker run -it --cpus=".5" ubuntu /bin/bash
- Docker ≤1.12
docker run -it --cpu-period=100000
--cpu-quota=50000 ubuntu /bin/bash
啟動容器後,可通過檢視Cgroups檔案系統下,CPU子系統中,“docker”這個控制組裡的資源限制檔案的内容來确認:
cat /sys/fs/cgroup/cpu/docker/5d5c9f67d/cpu.cfs_period_us
xxx
cat /sys/fs/cgroup/cpu/docker/5d5c9f67d/cpu.cfs_quota_us
xxx
6 總結
容器隻是一種特殊的程序,一個正在運作的Docker容器,就是一個啟用了多個Linux Namespace的應用程序,而該程序能夠使用的資源量,則受Cgroups限制。即容器是一個“單程序”模型。
由于一個容器本質就是一個程序,使用者的應用程序實際上就是容器裡PID=1的程序,也是其他後續建立的所有程序的父程序。
這意味着,在一個容器,無法同時運作兩個不同應用,除非你能事先找到一個公共的PID=1的程式充當兩個不同應用的父程序,這也解釋了為何很多人會用systemd或supervisord這樣的軟體代替應用本身作為容器的啟動程序。
容器本身設計就是希望容器和應用能同生命周期,這對容器的編排很重要。否則,一旦出現類似于“容器是正常運作的,但是裡面的應用早已經挂了”的情況,編排系統處理起來就非常麻煩了。
跟Namespace的情況類似,Cgroups對資源的限制能力也有很多不完善的地方,被提及最多的就是/proc檔案系統的問題。
如果在容器裡執行top,會發現它顯示的資訊是主控端的CPU和記憶體資料,而不是目前容器的。造成這個問題的原因就是,/proc檔案系統并不知道使用者通過Cgroups給這個容器做了什麼樣的資源限制,即:/proc檔案系統不了解Cgroups限制的存在。
在生産環境中,這個問題必須修正,否則應用程式在容器裡讀取到的CPU核數、可用記憶體等資訊都是主控端上的資料,這會給應用的運作帶來非常大的困惑和風險。這也是在企業中,容器化應用碰到的一個常見問題,也是容器相較于虛拟機另一個不盡如人意的地方
參考
- Docker官網
- Docker實戰
- 深入剖析Kubernetes
- https://tech.meituan.com/2015/03/31/cgroups.html