簡單介紹cgroups以及在K8s中的應(yīng)用 - CPU

cgroups(control groups,控制組群) 是 Linux 內(nèi)核的一個功能,用來限制、控制與分離一個進程組的資源(如CPU、內(nèi)存、磁盤輸入輸出等)。它是由 Google 的兩位工程師進行開發(fā)的,自 2008 年 1 月正式發(fā)布的 Linux 內(nèi)核 v2.6.24 開始提供此能力。cgroups到目前為止,有兩個大版本, 即 v1 和 v2 。
作者:董衛(wèi)國,中國移動云能力中心軟件研發(fā)工程師,專注于云原生領(lǐng)域。
cgroups(control groups,控制組群) 是?Linux?內(nèi)核的一個功能,用來限制、控制與分離一個進程組的資源(如CPU、內(nèi)存、磁盤輸入輸出等)。它是由?Google?的兩位工程師進行開發(fā)的,自?2008?年?1?月正式發(fā)布的?Linux?內(nèi)核?v2.6.24?開始提供此能力。cgroups到目前為止,有兩個大版本, 即?v1?和?v2?。
cgroups可以限制、記錄、隔離進程組所使用的物理資源(包括:CPU、memory、IO等),為容器實現(xiàn)虛擬化提供了基本保證,是構(gòu)建docker、containerd、kubernetes等一系列容器服務(wù)的基石。
從單個進程的資源控制到操作系統(tǒng)層面的虛擬化。cgroups提供了以下四大功能
1)資源限制:cgroups可以對進程組使用的資源總額進行限制。如設(shè)定應(yīng)用運行時使用內(nèi)存的上限,一旦超過這個配額就發(fā)出OOM(Out of Memory)。
2)優(yōu)先級分配:通過分配的CPU時間片數(shù)量及硬盤IO帶寬大小,實際上就相當于控制了進程運行的優(yōu)先級。
3)資源統(tǒng)計:cgroups可以統(tǒng)計系統(tǒng)的資源使用量,如CPU使用時長、內(nèi)存用量等等,這個功能非常適用于計費。
4)進程控制:cgroups可以對進程組執(zhí)行掛起、恢復(fù)等操作。
下面我們對v1和v2版本的使用進行一些簡單的驗證,并對于cgroups在kubernetes中的一些使用進行介紹,本節(jié)我們主要介紹cgroups對CPU使用率的限制能力。
cgroups?v1介紹
我們當前的測試環(huán)境為centos 7.9?,內(nèi)核為?5.4版本,如下所示:

我們可以使用如下命令查詢當前的cgroups版本

/sys/fs/cgroup是cgroups的默認掛載目錄,對于v1版本,命令的返回值應(yīng)該是tmpfs
cgroups中最關(guān)鍵的是一個概念就是到子系統(tǒng)(subsystem),每一個subsystem代表一種資源的控制能力,每個subsystem下可以建立cgroups控制組,并寫入進程號,以起到對進行限制資源的作用。
目前為止,Linux支持13種subsystem,比如限制CPU的使用時間,限制使用的內(nèi)存,統(tǒng)計CPU的使用情況,凍結(jié)和恢復(fù)一組進程等,我們可以通過如下命令查看系統(tǒng)支持的subsystem。

從左到右,字段的含義分別是:
l支持的subsystem的名字,每種subsystem獨立地控制一種資源
lsubsystem所關(guān)聯(lián)到的cgroups樹的ID,如果多個subsystem關(guān)聯(lián)到同一顆cgroups樹,那么他們的這個字段將一樣,比如這里的cpu和cpuacct就一樣,表示他們綁定到了同一顆樹。如果出現(xiàn)下面的情況,這個字段將為0:
2當前subsystem沒有和任何cgroups樹綁定
2當前subsystem已經(jīng)和cgroups v2的樹綁定
2當前subsystem沒有被內(nèi)核開啟
lsubsystem所關(guān)聯(lián)的cgroups樹中進程組的個數(shù),也即樹上節(jié)點的個數(shù)。
l1表示開啟,0表示沒有被開啟(可以通過設(shè)置內(nèi)核的啟動參數(shù)“cgroup_disable”來控制subsystem的開啟)。
Linux內(nèi)核有一個很大的模塊叫VFS(Virtual File System)。VFS能夠把具體的文件系統(tǒng)細節(jié)隱藏起來,給用戶態(tài)進程提供一個同一個為文件系統(tǒng)API接口。也就是說當我們對這些文件進行一些寫操作,就可以按照我們的需求對進程的資源進行定制化的配置,下面我們簡單介紹幾個cgroups的文件系統(tǒng)API接口。
簡單看一下/sys/fs/cgroup/cpu的目錄結(jié)構(gòu)和內(nèi)容。

cpu子系統(tǒng)是cgroups用來限制進程如何使用CPU的時間的subsystem,它提供了3種調(diào)度辦法,并且這3種調(diào)度辦法都可以在啟動容器時進行配置,分別是:
1)share?:相對權(quán)重的CPU調(diào)度
2)cfs?:完全公平調(diào)度
3)rt?:實時調(diào)度
以cfs為例簡單說明,cfs根據(jù)cpu.cfs_quota_us?和?cpu.cfs_period_us?兩個文件實現(xiàn)公平調(diào)度,這兩個文件內(nèi)容組合使用可以限制進程在長度為?cfs_period_us?的時間內(nèi),只能被分配到總量為?cfs_quota_us?的?CPU?時間。
tasks文件是當前?cgroups?包含的pid (Process Identifier,進程號)列表,把某個進程的?pid?添加到這個文件中就等于把進程移到該?cgroups控制組中。
進程的cgroups屬組
本小節(jié)我們簡單介紹下如何查看一個進程屬于哪個cgroup組,方便我們后續(xù)追蹤容器進程的cgroup情況。
每個進程在/proc/[pid]目錄下有一個cgroup文件,這個文件內(nèi)保存了一個進程和cgroup的對應(yīng)關(guān)系。查看當前shell的cgroups

從左到右,字段的含義分別是:
lcgroups樹的ID, 和/proc/cgroups文件中的ID一一對應(yīng)。
l和cgroups樹綁定的所有subsystem,多個subsystem之間用逗號隔開。這里name=systemd表示沒有和任何subsystem綁定,只是給他起了個名字叫systemd。
l進程在cgroups樹中的路徑,即進程所屬的cgroups,這個路徑是相對于掛載點的相對路徑。
第三列是相對路徑,補全就是/sys/fs/cgroup/systemd/user.slice/user-0.slice/session-672.scope/tasks,我們切換到對應(yīng)的路徑并查看tasks內(nèi)容,查看內(nèi)容如下:

可以看到PID?8125在tasks內(nèi)。
由于當前沒有對進程做資源限制,因此/proc/[pid]/cgroups中cpu/memory等限制均為空。
cgoups資源限制測試
在一臺centos系統(tǒng)運行如下一個耗cpu的命令

新打開一個會話,使用top命令容易觀察到,有一個bash進程(即當前會話的進程)CPU使用率為100%,如下所示:

創(chuàng)建一個名為test01的cgroups控制組控制這個進程的CPU資源,并將當前會話的進程加入資源組(即寫入task文件),如下所示

當前cgroups限制在100000微秒內(nèi)可以申請使用10000微秒的CPU,也就是使用了一個CPU線程的10%,再次執(zhí)行shell命令

使用top命令觀察cpu占用率如下所示

符合預(yù)期。
換一個命令測試,shell命令如下

查看當前的CPU消耗如下所示

可以看到shell的兩個子進程加起來也使用了10%的CPU,查看task文件

可以看到兩個子進程已經(jīng)自動加入了task中。
把當前shell的pid移動到cpu的/sys/fs/cgroup/cpu中,并使用rmdir命令清理,如下所示:

可以看到之前創(chuàng)建的cgroups控制組已經(jīng)被刪除。
Kubernetes中的資源限制
Kubernetes?通過配置?cgroups來限制容器或者pod?能使用的最大資源量。這個配置有兩種實現(xiàn)方式, 在?Kubernetes?中稱為?cgroup runtime driver:
lcgroupfs
這種比較簡單直接,kubelet往?cgroup?文件系統(tǒng)中寫?limit?就行了。這也是目前?Kubernetes?的默認方式。
lsystemd
所有?cgroup-writing?操作都必須通過?systemd?的接口,不能手動修改?cgroup?文件。適用于?Kubernetes cgroup v2?模式。
在Kubernetes中啟動一個deployment查看效果

Pod變?yōu)?/span>Running狀態(tài)后,登錄到Pod所在節(jié)點執(zhí)行如下shell命令查看容器cgroupstest的內(nèi)存限制

容器的資源限制如下所示

執(zhí)行如下shell查看CPU限制

先看cpu.shares結(jié)果大致如下

對于CPU,requests?經(jīng)過轉(zhuǎn)換之后會寫入?cpu.share, 表示這個?cgroups最少可以使用的?CPU。
在Kubernetes中一個CPU線程相當于1024?share,使用如下命令可以查看

CPU?資源的單位m?是?millicores?的縮寫,表示千分之一核,一個CPU線程可以分為1000個等份,容易得到如下公式

可以看到pod的resource.request中CPU為50m,通過上面的公式轉(zhuǎn)化為share結(jié)果為51,?cpu?share是一個相對值,可以參考下面的解釋:

容易知道,cpu.shares是一個相對值,也是一個軟限制,在空閑時,CPU占用也很容易超過request申請的值,因此只能作為resource.request,不能作為resource.limits。
而resource.limits?則通過.cfs_quota_us和cpu.cfs_period_us?兩個文件來控制,表示cgroups最多可以使用的?CPU。
lcpu.cfs_period_us?此參數(shù)可以設(shè)定重新分配?cgroup?可用?CPU?資源的時間間隔,單位為微秒
lcpu.cfs_quota_us?此參數(shù)可以設(shè)定在某一階段(由?cpu.cfs_period_us?規(guī)定)某個?cgroup?中所有任務(wù)可運行的時間總量,單位為微秒。一旦?cgroup?中任務(wù)用完按配額分得的時間,它們就會被在此階段的時間提醒限制流量,并在進入下階段前禁止運行。
如果?cgroups?中任務(wù)在每?1?秒內(nèi)有?0.2?秒,可對單獨?CPU?進行存取,可以將?cpu.cfs_quota_us?設(shè)定為?200000,cpu.cfs_period_us?設(shè)定為?1000000。查看如下結(jié)果

由此容易得到測試容器的CPU上限使用率為100m。
通過top命令查看也符合預(yù)期

使用如下命令修改pod和容器的資源上限:

注意在修改cgroups時候,先修改pod的cgroups,再修改容器的,因為在子?cgroups中對相關(guān)子系統(tǒng)進行修改時,該子系統(tǒng)的相關(guān)屬性小于父?cgroups屬性的相應(yīng)值。
查看結(jié)果如下:

資源使用率提升至100%,符合預(yù)期。
前面已經(jīng)介紹過,Kubernetes spec?里的?requests/limits?是打在?container?上的,并沒有打在?pod?上。因此?pod?的?requests/limits?需要由?kubelet?綜合統(tǒng)計?pod?的所有?container?的?request/limits?計算得到。CPU?和內(nèi)存的計算方式如下:

注意,
1)如果其中某個?container?的?CPU?字段只設(shè)置了?request?沒設(shè)置?limit, 則?pod?將只設(shè)置?cpu.shares,不設(shè)置?cpu.cfs_quota_us。
2)如果所有?container?都沒有設(shè)置?cpu request/limit(等效于?requests==limits==0), 則將?pod?cpu.share?將設(shè)置為?Kubernetes?定義的最小值?2。這種?pod?在?node?空閑時最多能使用整個?node?的資源;但?node?資源緊張時,也最先被驅(qū)逐。
cgroups?v2介紹
cgroups v2?在?Linux Kernel 4.5中被引入,并且考慮到其它已有程序的依賴,V2?會和?V1?并存幾年。當前很多的操作系統(tǒng)版本中,默認的cgroups仍是v1版本,下面我們進行切換,并進行測試。
測試使用cgroups v2
Centos7啟動cgroups?v2
這里筆者基于自己較為熟悉的centos7進行測試,首先升級systemd,我們參考如下命令先升級systemd

升級內(nèi)核至4.5版本以上,筆者的測試機器升級到了5.4版本,然后重啟機器
然后修改?/etc/default/grub?文件,在變量GRUB_CMDLINE_LINUX最后追加如下內(nèi)容

更新引導(dǎo),重啟機器

重啟后,檢查掛載,發(fā)現(xiàn)當前已使用cgroups v2,且cgroups?v1已關(guān)閉

如果掛載情況和v1版本一樣,則需要重新檢查內(nèi)核版本,確認切換到正確的高版本內(nèi)核后,再次重啟。
簡單了解cgroups關(guān)鍵文件
在cgroups?v2版本的根目錄下會有三個?cgroups核心文件:
lcgroup.controllers:?該文件列出當前?cgroup支持的所有?controller,如: cpu io memory
lcgroup.procs:?在剛掛載時,root?cgroup目錄下的?cgroup.procs?文件中會包含系統(tǒng)當前所有的Proc PID(除了僵尸進程)。同樣,可以通過將?Proc PID?寫入?cgroup.procs?來將?Proc?加入到?cgroup
lcgroup.subtree_control:?用于控制該?cgroup下?controller開關(guān),只有列在?cgroup.controllers?中的?controller?才可以被開啟,默認情況下所有的?controller都是關(guān)閉的。
查看cgroups v2支持的哪些資源限制,如下所示:

可以看到當前版本支持cpuset、cpu、io、memory、pids、rdma等幾種資源,除了rdma其他幾種都是常見的資源限制,這里面沒有了cgroups?v1中的net_cls。
嘗試新建一個名為test的cgroups組,然后查看目錄,如下所示

可以看到,當前cgroups可以支持cpuset cpu io memory pids的資源限制。
cgroup.subtree_control:這個文件內(nèi)容應(yīng)是cgroup.controllers的子集。其作用是限制在當前cgroups目錄層級下創(chuàng)建的子目錄中的cgroup.controllers內(nèi)容。就是說,子層級的cgroups資源限制范圍被上一級的cgroup.subtree_control文件內(nèi)容所限制。
所以,如果我們想創(chuàng)建一個可以支持cpuset cpu io memory pids全部五種資源限制能力的cgroups組的話,應(yīng)該做如下操作:

執(zhí)行之后再次檢查,如下所示

此時我們創(chuàng)建的test?cgroups組就有cpu,cpuset,io,memory,pids五種常見的資源限制能力了。另外要注意,被限制進程只能添加到葉子結(jié)點的組中,不能添加到中間結(jié)點的組內(nèi)。
CPU資源隔離
新版cgroups簡化了cpu配額的配置方法。用一個文件就可以進行配置了:cpu.max。該文件支持2個值,格式為:$MAX $PERIOD。這個含義是,在?$PERIOD所表示的時間周期內(nèi),有?$MAX是分給本cgroups的。也就是配置了本cgroups的CPU占用在單核上不超過50%。
執(zhí)行如下命令設(shè)置資源限制

執(zhí)行腳本如下命令進行測試:

打開一個會話,用top命令查看結(jié)果

可以看到CPU使用率被限制在了50%。
CPU還有一個軟性的限制,叫做?cpu.weight?,相當于v1版本cgroups中的cpu.shares。還有一個相關(guān)的值為?cpu.weight.nice?,這個值隨cpu.weight的變化為變化,cgroups?v2官方文檔里說這個值的取值范圍是[-20, 19],從nice名字和取值范圍看,大概相當于linux中的nice命令(一個調(diào)整進程優(yōu)先級的命令)的作用。
Kubernetes中的資源限制
啟用cgroups?v2
Kubernetes?自?v1.25?起?cgroup2?特性正式?stable,根據(jù)官方文檔,我們可以知道,cgroup2?相比?cgroups v1?有以下優(yōu)勢:
lAPI?中單個統(tǒng)一的層次結(jié)構(gòu)設(shè)計
l更安全的子樹委派給容器
l更新的功能特性,?例如壓力阻塞信息(Pressure Stall Information,PSI)
l跨多個資源的增強資源分配管理和隔離
l統(tǒng)一核算不同類型的內(nèi)存分配(網(wǎng)絡(luò)內(nèi)存、內(nèi)核內(nèi)存等)
l考慮非即時資源變化,例如頁面緩存回寫
推薦在使用?Kubernetes v1.25及以上版本時,?使用支持?cgroups v2?的linux?和?CRI.?并啟用?Kubernetes?的cgroups v2?功能.
首先在kubelet?使用?systemd cgroup?驅(qū)動。kubeadm?支持在執(zhí)行?kubeadm init?時,傳遞一個?KubeletConfiguration?結(jié)構(gòu)體。KubeletConfiguration?包含?cgroupDriver?字段,可用于控制?kubelet?的?cgroup?驅(qū)動。如下所示

也可以自行在kubelet的config配置中修改參數(shù):cgroupDriver: systemd
containerd?使用?systemd cgroup?驅(qū)動,編輯?/etc/containerd/config.toml:
修改或增加如下內(nèi)容

cgroups v2?使用一個與?cgroups v1?不同的?API,因此如果有任何應(yīng)用直接訪問?cgroups文件系統(tǒng), 則需要將這些應(yīng)用更新為支持?cgroups v2?的版本。例如:
l一些第三方監(jiān)控和安全代理可能依賴于?cgroups文件系統(tǒng)。你要將這些代理更新到支持?cgroups v2?的版本。
l如果以獨立的?DaemonSet?的形式運行?cAdvisor?以監(jiān)控?Pod?和容器, 需將其更新到?v0.43.0?或更高版本。
l如果你使用?JDK,推薦使用?JDK 11.0.16?及更高版本或?JDK 15?及更高版本, 以便完全支持?cgroups v2。
測試效果
在Kubernetes中啟動一個deployment查看效果

登錄到節(jié)點上執(zhí)行如下命令

查看結(jié)果如下:

其中,cpu.weight的計算是通過如下公式

可以看到,容器內(nèi)存被限制在了128Mb,CPU被限制在了0.1C,即limit配置中100m。
總結(jié)
本文對cgroups?v1和v2版本進行了介紹和和CPU使用率限制的驗證,并對其在Kubernetes中的使用進行了一些初步的調(diào)研。
本文僅做了一些cgroups基礎(chǔ)能力的調(diào)研,沒有做深入學習。在功能上,一些文檔中看到cgroups甚至可以在線程級別進行限制,在原理上,cgroups涉及到各個資源的分配、調(diào)度,比如CPU的資源限制就涉及到linux下的進程調(diào)度算法,本文也沒有進一步去探究。
最后,由于筆者能力和時間所限,難免存在一些錯漏,還請諒解。
參考
1)一篇搞懂容器技術(shù)的基石:?cgroup,https://zhuanlan.zhihu.com/p/434731896
2)Linux cgroups:深入理解cgroups v1版本,?https://www.testerfans.com/archives/linux-cgroups-learn-more
3)Linux CFS and task group,?https://mechpen.github.io/posts/2020-04-27-cfs-group/index.html
4)深入理解?Kubernetes?資源限制:CPU,?https://icloudnative.io/posts/understanding-resource-limits-in-kubernetes-cpu-time/
5)CFS Bandwidth Control,https://www.kernel.org/doc/html/v5.4/scheduler/sched-bwc.html?highlight=cpu%20cfs_quota_us
6)Cgroup詳解,https://juejin.cn/post/6921299245685276686
7)Cgroup限制內(nèi)存與節(jié)點的刪除,https://chaochaogege.com/2019/09/11/6/
8)容器內(nèi)存分析,https://blog.csdn.net/u012986012/article/details/105291831
9)控制組詳解,https://blog.gmem.cc/cgroup-illustrated
10)Cgroup V2 and writeback support,http://hustcat.github.io/cgroup-v2-and-writeback-support/
11)Linux CGroup?基礎(chǔ),https://wudaijun.com/2018/10/linux-cgroup/
12)詳解Cgroup V2,https://zorrozou.github.io/docs/%E8%AF%A6%E8%A7%A3Cgroup%20V2.html
13)centos 7?升級?systemd,https://lqingcloud.cn/post/systemd-01/
14)容器-cgroup-blkio-cgroup,http://119.23.219.145/posts/%E5%AE%B9%E5%99%A8-cgroup-blkio-cgroup/
15)打通IO棧:一次編譯服務(wù)器性能優(yōu)化實戰(zhàn), https://mp.weixin.qq.com/s?__biz=Mzg2OTc0ODAzMw==&mid=2247502495&idx=1&sn=26950b22cba383b14052b441cd356516&source=41#wechat_redirect
16)pod資源限制和QoS探索,?https://www.zerchin.xyz/2021/01/31/pod%E8%B5%84%E6%BA%90%E9%99%90%E5%88%B6%E5%92%8CQoS%E6%8E%A2%E7%B4%A2/
