一文帶你搞懂 Docker 容器的核心基石 Cgroups

Cgroups 是 Linux 系統(tǒng)內(nèi)核提供的一種機(jī)制,這種機(jī)制可以根據(jù)需求將一些列系統(tǒng)任務(wù)機(jī)器子任務(wù)整合或分離到按資源劃分登記的不同組內(nèi),從而為系統(tǒng)資源管理提供一個(gè)的框架。簡單地說,cgroups 可以限制、記錄任務(wù)組所使用的物理組員(比如 CPU、Memory、IO等),為容器實(shí)現(xiàn)虛擬化提供了基本保證,是構(gòu)建 Docker 等一些列虛擬化管理工具的基石。今天我們就來詳細(xì)介紹一下 cgroups 相關(guān)的內(nèi)容。
1. 為什么要了解 Cgroups
從2013年開源的 Docker 推出、2014年開源的 Kubernetes 出現(xiàn),到現(xiàn)在的云原生技術(shù)與生態(tài)的全面普及與火熱化,容器技術(shù)已經(jīng)逐步成為主流的基礎(chǔ)云原生技術(shù)之一。使用容器技術(shù),可以很好地實(shí)現(xiàn)資源層面上的限制和隔離,這都依賴于 Linux 系統(tǒng)內(nèi)核所提供的Cgroups和 Namespace技術(shù)。
Cgroups主要用來管理資源的分配、限制;Namespace主要用來封裝抽象、限制、隔離資源,使命名空間內(nèi)的進(jìn)程擁有它們自己的全局資源。
Linux內(nèi)核提供的 Cgroups 和 Namespace 技術(shù),為容器實(shí)現(xiàn)虛擬化提供了基本保證,是構(gòu)建 Docker 等一些列虛擬化管理工具的基石。下面我們就來詳細(xì)介紹一下 Cgroups 相關(guān)的內(nèi)容。
2. Cgroups簡介
Cgroups 是 control groups 的縮寫,是 Linux 內(nèi)核提供的一種可以限制、記錄、隔離進(jìn)程組(process groups)所使用的物理資源(如 CPU、Memory、IO 等等)的機(jī)制。

通過使用 Cgroups,系統(tǒng)管理員在分配、排序、拒絕、管理和監(jiān)控系統(tǒng)資源等方面,可以進(jìn)行精細(xì)化控制。硬件資源可以在應(yīng)用程序和用戶間智能分配,從而增加整體效率。最初由 google 的工程師提出,后來被整合進(jìn) Linux 內(nèi)核。也是目前輕量級虛擬化技術(shù) XC(Linux Container)的基礎(chǔ)之一。
Cgroups 和 Namespace 類似,也是將進(jìn)程進(jìn)行分組,但它的目的和 Namespace 不一 樣,Namespace 是為了隔離進(jìn)程組之間的資源,而 Cgroups 是為了對一組進(jìn)程進(jìn)行統(tǒng)一的資源監(jiān)控和限制。
Cgroups 分 v1 和 v2 兩個(gè)版本,v1 實(shí)現(xiàn)較早,功能比較多,但是由于它里面的功能都是零零散散的實(shí)現(xiàn)的,所以規(guī)劃的不是很好,導(dǎo)致了一些使用和維護(hù)上的不便,v2 的出現(xiàn) 就是為了解決 v1 中這方面的問題,在最新的 4.5 內(nèi)核中,Cgroups v2 聲稱已經(jīng)可以用于生產(chǎn)環(huán)境了,但它所支持的功能還很有限,隨著 v2 一起引入內(nèi)核的還有 Cgroups、Namespace,v1 和 v2 可以混合使用,但是這樣會更復(fù)雜,所以一般沒人會這樣用。
3. 什么是 Cgroups?
Cgroups 是 Linux 下的一種將進(jìn)程按組進(jìn)行管理的機(jī)制,在用戶層看來,Cgroups 技術(shù)就是把系統(tǒng)中的所有進(jìn)程組織成一顆一顆獨(dú)立的樹,每棵樹都包含系統(tǒng)的所有進(jìn)程,樹的每個(gè)節(jié)點(diǎn)是一個(gè)進(jìn)程組,而每顆樹又和一個(gè)或者多個(gè) subsystem 關(guān)聯(lián),樹的作用是將進(jìn)程分組,而 subsystem 的作用就是對這些組進(jìn)行操作,Cgroups 的主體架構(gòu)提如下:

Cgroups 主要包括下面兩部分:
subsystem: 一個(gè) subsystem 就是一個(gè)內(nèi)核模塊,他被關(guān)聯(lián)到一顆 cgroup 樹之后, 就會在樹的每個(gè)節(jié)點(diǎn)(進(jìn)程組)上做具體的操作。subsystem 經(jīng)常被稱作 resource controller,因?yàn)樗饕挥脕碚{(diào)度或者限制每個(gè)進(jìn)程組的資源,但是這個(gè)說法不完全準(zhǔn) 確,因?yàn)橛袝r(shí)我們將進(jìn)程分組只是為了做一些監(jiān)控,觀察一下他們的狀態(tài),比如 perf_event subsystem。到目前為止,Linux 支持 12 種 subsystem,比如限制 CPU 的使 用時(shí)間,限制使用的內(nèi)存,統(tǒng)計(jì) CPU 的使用情況,凍結(jié)和恢復(fù)一組進(jìn)程等,后續(xù)會對它們一一進(jìn)行介紹。 hierarchy: 一個(gè) hierarchy 可以理解為一棵 cgroup 樹,樹的每個(gè)節(jié)點(diǎn)就是一個(gè)進(jìn)程 組,每棵樹都會與零到多個(gè) subsystem 關(guān)聯(lián)。在一顆樹里面,會包含 Linux 系統(tǒng)中的所有 進(jìn)程,但每個(gè)進(jìn)程只能屬于一個(gè)節(jié)點(diǎn)(進(jìn)程組)。系統(tǒng)中可以有很多顆 cgroup 樹,每棵樹 都和不同的 subsystem 關(guān)聯(lián),一個(gè)進(jìn)程可以屬于多顆樹,即一個(gè)進(jìn)程可以屬于多個(gè)進(jìn)程 組,只是這些進(jìn)程組和不同的 subsystem 關(guān)聯(lián)。
目前 Linux 支持 12 種 subsystem,如果不考慮不與任何 subsystem 關(guān)聯(lián)的情況(systemd 就屬于這種情況),Linux 里面最多可以建 12 顆 cgroup 樹,每棵樹關(guān)聯(lián)一個(gè) subsystem,當(dāng)然也可以只建一棵樹,然后讓這 棵樹關(guān)聯(lián)所有的 subsystem。當(dāng)一顆 cgroup 樹不和任何 subsystem 關(guān)聯(lián)的時(shí)候,意味著這棵樹只是將進(jìn)程進(jìn)行分組,至于要在分組的基礎(chǔ)上做些什么,將由應(yīng)用程序自己決定, systemd 就是一個(gè)這樣的例子。
4. 為什么需要 Cgroups?
在 Linux 里,一直以來就有對進(jìn)程進(jìn)行分組的概念和需求,比如 session group, progress group 等,后來隨著人們對這方面的需求越來越多,比如需要追蹤一組進(jìn)程的內(nèi)存和 IO 使用情況等,于是出現(xiàn)了 cgroup,用來統(tǒng)一將進(jìn)程進(jìn)行分組,并在分組的基礎(chǔ)上對進(jìn)程進(jìn)行監(jiān)控和資源控制管理等。
舉個(gè)例子,Linux 系統(tǒng)中安裝了殺毒軟件 ESET 或者 ClamAV,殺毒時(shí)占用系統(tǒng)資源過高,影響系統(tǒng)承載業(yè)務(wù)運(yùn)行,怎么辦?單個(gè)虛擬機(jī)進(jìn)程或者 docker 進(jìn)程使用過高的資源,怎么辦?單個(gè)Java進(jìn)行占用系統(tǒng)過多的內(nèi)存的資源,怎么辦?
cgroup 就是能夠控制并解決上述問題的工具,cgroup 在 linux 內(nèi)核實(shí)現(xiàn)、用于控制 linux 系統(tǒng)資源。
5. Cgroups 是如何實(shí)現(xiàn)的?
在 CentOS 7 系統(tǒng)中(包括 Red Hat Enterprise Linux 7),通過將 cgroup 層級系統(tǒng)與 systemd 單位樹捆綁,可以把資源管理設(shè)置從進(jìn)程級別移至應(yīng)用程序級別。默認(rèn)情況下 systemd 會自動創(chuàng)建 slice、scope 和 service 單位的層級(具體的意思稍后再解釋),來為 cgroup 樹提供統(tǒng)一結(jié)構(gòu)。
可以通過 systemctl 命令創(chuàng)建自定義 slice 進(jìn)一步修改此結(jié)構(gòu)。如果我們將系統(tǒng)的資源看成一塊餡餅,那么所有資源默認(rèn)會被劃分為 3 個(gè) cgroup:System, User 和 Machine。每一個(gè) cgroup 都是一個(gè) slice,每個(gè) slice 都可以有自己的子 slice,如下圖所示:

下面我們以 CPU 資源為例,來解釋一下上圖中出現(xiàn)的一些關(guān)鍵詞。如上圖所示,系統(tǒng)默認(rèn)創(chuàng)建了 3 個(gè)頂級 slice(System, User 和 Machine),每個(gè) slice 都會獲得相同的 CPU 使用時(shí)間(僅在 CPU 繁忙時(shí)生效),如果 user.slice 想獲得 100% 的 CPU 使用時(shí)間,而此時(shí) CPU 比較空閑,那么 user.slice 就能夠如愿以償。這三種頂級 slice 的含義如下:
1)system.slice:所有系統(tǒng) service 的默認(rèn)位置。
2)user.slice:所有用戶會話的默認(rèn)位置。每個(gè)用戶會話都會在該 slice 下面創(chuàng)建一個(gè)子 slice,如果同一個(gè)用戶多次登錄該系統(tǒng),仍然會使用相同的子 slice。
3)machine.slice:所有虛擬機(jī)和 Linux 容器的默認(rèn)位置 控制 CPU 資源使用的其中一種方法是 shares。shares 用來設(shè)置 CPU 的相對值(你可以理解為權(quán) 重),并且是針對所有的 CPU(內(nèi)核),默認(rèn)值是 1024。因此在上圖中,httpd, sshd, crond 和 gdm 的 CPU shares 均為 1024,System, User 和 Machine 的 CPU shares 也是 1024。
假設(shè)該系統(tǒng)上運(yùn)行了 4 個(gè) service,登錄了兩個(gè)用戶,還運(yùn)行了一個(gè)虛擬機(jī)。同時(shí)假設(shè) 每個(gè)進(jìn)程都要求使用盡可能多的 CPU 資源(每個(gè)進(jìn)程都很繁忙),則:
1)system.slice 會獲得 33.333% 的 CPU 使用時(shí)間,其中每個(gè) service 都會從 system.slice 分配的 資源中獲得 1/4 的 CPU 使用時(shí)間,即 8.25% 的 CPU 使用時(shí)間。
2)user.slice 會獲得 33.333% 的 CPU 使用時(shí)間,其中每個(gè)登錄的用戶都會獲得 16.5% 的 CPU 使 用時(shí)間。假設(shè)有兩個(gè)用戶:tom 和 jack,如果 tom 注銷登錄或者殺死該用戶會話下的所有進(jìn)程, jack 就能夠使用 33.333% 的 CPU 使用時(shí)間。
3)machine.slice 會獲得 33.333% 的 CPU 使用時(shí)間,如果虛擬機(jī)被關(guān)閉或處于 idle 狀態(tài),那么 system.slice 和 user.slice 就會從這 33.333% 的 CPU 資源里分別獲得 50% 的 CPU 資源,然后 均分給它們的子 slice。
6. Cgroups 的作用
Cgroups 最初的目標(biāo)是為資源管理提供的一個(gè)統(tǒng)一的框架,既整合現(xiàn)有的 cpuset 等子系統(tǒng),也為未來開發(fā)新的子系統(tǒng)提供接口?,F(xiàn)在的 cgroups 適用于多種應(yīng)用場景,從單個(gè)進(jìn)程的資源控制,到實(shí)現(xiàn)操作系統(tǒng)層次的虛擬化(OS Level Virtualization),框架圖如下:

Cgroups提供了以下功能:
1)限制進(jìn)程組可以使用的資源數(shù)量(Resource limiting )。比如:memory子系統(tǒng)可以為進(jìn)程 組設(shè)定一個(gè)memory使用上限,一旦進(jìn)程組使用的內(nèi)存達(dá)到限額再申請內(nèi)存,就會出發(fā) OOM(out of memory)。
2)進(jìn)程組的優(yōu)先級控制(Prioritization )。比如:可以使用cpu子系統(tǒng)為某個(gè)進(jìn)程組分配特定 cpu share。
3)記錄進(jìn)程組使用的資源數(shù)量(Accounting )。比如:可以使用cpuacct子系統(tǒng)記錄某個(gè)進(jìn)程 組使用的cpu時(shí)間。
4)進(jìn)程組隔離(Isolation)。比如:使用ns子系統(tǒng)可以使不同的進(jìn)程組使用不同的 namespace,以達(dá)到隔離的目的,不同的進(jìn)程組有各自的進(jìn)程、網(wǎng)絡(luò)、文件系統(tǒng)掛載空間。
5)進(jìn)程組控制(Control)。比如:使用freezer子系統(tǒng)可以將進(jìn)程組掛起和恢復(fù)。
7. Cgroups 相關(guān)概念及相互關(guān)系
7.1 相關(guān)概念
1)任務(wù)(task):在 cgroups 中,任務(wù)就是系統(tǒng)的一個(gè)進(jìn)程。
2)控制族群(control group):控制族群就是一組按照某種標(biāo)準(zhǔn)劃分的進(jìn)程。Cgroups 中的資源控制都是以控制族群為單位實(shí)現(xiàn)。一個(gè)進(jìn)程可以加入到某個(gè)控制族群,也從一個(gè)進(jìn)程組遷移到另 一個(gè)控制族群。一個(gè)進(jìn)程組的進(jìn)程可以使用 cgroups 以控制族群為單位分配的資源,同時(shí)受到 cgroups 以控制族群為單位設(shè)定的限制。
3)層級(hierarchy):控制族群可以組織成 hierarchical 的形式,既一顆控制族群樹??刂谱?群樹上的子節(jié)點(diǎn)控制族群是父節(jié)點(diǎn)控制族群的孩子,繼承父控制族群的特定的屬性。
4)子系統(tǒng)(subsystem):一個(gè)子系統(tǒng)就是一個(gè)資源控制器,比如 cpu 子系統(tǒng)就是控制 cpu 時(shí)間分配的一個(gè)控制器。子系統(tǒng)必須附加(attach)到一個(gè)層級上才能起作用,一個(gè)子系統(tǒng)附加到某個(gè) 層級以后,這個(gè)層級上的所有控制族群都受到這個(gè)子系統(tǒng)的控制。
7.2 相互關(guān)系
1)每次在系統(tǒng)中創(chuàng)建新層級時(shí),該系統(tǒng)中的所有任務(wù)都是那個(gè)層級的默認(rèn) cgroup(我們稱之為 root cgroup ,此 cgroup 在創(chuàng)建層級時(shí)自動創(chuàng)建,后面在該層級中創(chuàng)建的 cgroup 都是此 cgroup 的后代)的初始成員。
2)一個(gè)子系統(tǒng)最多只能附加到一個(gè)層級。
3)一個(gè)層級可以附加多個(gè)子系統(tǒng)
4)一個(gè)任務(wù)可以是多個(gè) cgroup 的成員,但是這些 cgroup 必須在不同的層級。
5)系統(tǒng)中的進(jìn)程(任務(wù))創(chuàng)建子進(jìn)程(任務(wù))時(shí),該子任務(wù)自動成為其父進(jìn)程所在 cgroup 的成員。然后可根據(jù)需要將該子任務(wù)移動到不同的 cgroup 中,但開始時(shí)它總是繼承其父任務(wù)的 cgroup。
8. Cgroups 子系統(tǒng)介紹
可以看到,在 /sys/fs/cgroup 下面有很多 cpu、memory 這樣的子目錄,也就稱為子系統(tǒng) subsystem:

它是一組資源控制模塊,一般包含如下幾項(xiàng):
1)net_cls:將 cgroup 中進(jìn)程產(chǎn)生的網(wǎng)絡(luò)包分類,以便 Linux 的 tc(traffic controller) 可以根據(jù)分類區(qū)分出來自某個(gè) cgroup 的包并做限流或監(jiān)控。這個(gè)子系統(tǒng)使用等級識別符(classid)標(biāo)記網(wǎng)絡(luò)數(shù)據(jù)包,可允許 Linux 流量控制程序 (tc)識別從具體 cgroup 中生成的數(shù)據(jù)包。
2)net_prio:設(shè)置 cgroup 中進(jìn)程產(chǎn)生的網(wǎng)絡(luò)流量的優(yōu)先級。
3)memory:控制 cgroup 中進(jìn)程的內(nèi)存占用。
4)cpuset:在多核機(jī)器上設(shè)置 cgroup 中進(jìn)程可以使用的 cpu 和內(nèi)存。這個(gè)子系統(tǒng)為 cgroup 中的任務(wù)分配獨(dú)立 CPU(在多核系統(tǒng))和內(nèi)存節(jié)點(diǎn)。
5)freezer:掛起(suspend)和恢復(fù)(resume) cgroup 中的進(jìn)程。這個(gè)子系統(tǒng)掛起或者恢復(fù) cgroup 中的任務(wù)。
6)blkio:設(shè)置對塊設(shè)備(如硬盤)輸入輸出的訪問控制。這個(gè)子系統(tǒng)為塊設(shè)備設(shè)定輸入/輸出限制,比如物理設(shè)備(磁盤,固態(tài)硬盤,USB 等等)。
7)cpu:設(shè)置 cgroup 中進(jìn)程的 CPU 占用。這個(gè)子系統(tǒng)使用調(diào)度程序提供對 CPU 的 cgroup 任務(wù)訪問。
8)cpuacct:統(tǒng)計(jì) cgroup 中進(jìn)程的 CPU 占用。這個(gè)子系統(tǒng)自動生成 cgroup 中任務(wù)所使用的 CPU 報(bào)告。
9)devices:控制 cgroup 中進(jìn)程對設(shè)備的訪問 16 這個(gè)子系統(tǒng)可允許或者拒絕 cgroup 中的任務(wù)訪問設(shè)備。
8.1 如何查看當(dāng)前系統(tǒng)支持哪些 subsystem?
可以通過查看 /proc/cgroups(since Linux 2.6.24)知道當(dāng)前系統(tǒng)支持哪些 subsystem,下面 是一個(gè)例子:
#subsys_name hierarchy num_cgroups enabled
cpuset 11 1 1
cpu 3 64 1
cpuacct 3 64 1
blkio 8 64 1
memory 9 104 1
devices 5 64 1
freezer 10 4 1
net_cls 6 1 1
perf_event 7 1 1
net_prio 6 1 1
hugetlb 4 1 1
pids 2 68 1
每一列的說明:
1)subsys_name:subsystem 的名字
2)hierarchy:subsystem 所關(guān)聯(lián)到的 cgroup 樹的 ID,如果多個(gè) subsystem 關(guān)聯(lián)到同一顆 cgroup 樹,那么他們的這個(gè)字段將一樣,比如這里的 cpu 和 cpuacct 就一樣,表示他們綁定到了同一顆樹。如果出現(xiàn)下面的情況,這個(gè)字段將為0:
當(dāng)前 subsystem 沒有和任何 cgroup 樹綁定
當(dāng)前 subsystem 已經(jīng)和 cgroup v2 的樹綁定
當(dāng)前 subsystem 沒有被內(nèi)核開啟
3)num_cgroups:subsystem 所關(guān)聯(lián)的 cgroup 樹中進(jìn)程組的個(gè)數(shù),也即樹上節(jié)點(diǎn)的個(gè)數(shù)
4)enabled:1 表示開啟,0 表示沒有被開啟(可以通過設(shè)置內(nèi)核的啟動參數(shù)
cgroup_disable來控制 subsystem 的開啟)。
8.2 Cgroups 下的 CPU 子系統(tǒng)
cpu 子系統(tǒng)用于控制 cgroup 中所有進(jìn)程可以使用的 cpu 時(shí)間片。cpu subsystem 主要涉及5接口:cpu.cfs_period_us、cpu.cfs_quota_us、cpu.shares、cpu.rt_period_us、cpu.rt_runtime_us.cpu。
1)cfs_period_us:cfs_period_us 表示一個(gè) cpu 帶寬,單位為微秒。系統(tǒng)總 CPU 帶寬:
cpu核心數(shù) * cfs_period_us cpu。2)cfs_quota_us:cfs_quota_us 表示 Cgroup 可以使用的 cpu 的帶寬,單位為微秒。cfs_quota_us 為-1,表示使用的 CPU 不受 cgroup 限制。cfs_quota_us 的最小值為1ms(1000),最大值為1s。結(jié)合 cfs_period_us,就可以限制進(jìn)程使用的 cpu。例如配置 cfs_period_us=10000,而 cfs_quota_us=2000。那么該進(jìn)程就可以可以用2個(gè) cpu core。
3)cpu.shares:通過 cfs_period_us 和 cfs_quota_us 可以以絕對比例限制 cgroup 的 cpu 使用,即
cfs_quota_us/cfs_period_us等于進(jìn)程可以利用的 cpu cores,不能超過這個(gè)數(shù)值。而 cpu.shares 以相對比例限制 cgroup 的 cpu。例如:在兩個(gè) cgroup 中都將 cpu.shares 設(shè)定為 1 的任務(wù)將有相同的 CPU 時(shí)間,但在 cgroup 中將 cpu.shares 設(shè)定為 2 的任務(wù)可使用的 CPU 時(shí)間 是在 cgroup 中將 cpu.shares 設(shè)定為 1 的任務(wù)可使用的 CPU 時(shí)間的兩倍。4)cpu.rt_runtime_us:以微秒(μs,這里以“us”代表)為單位指定在某個(gè)時(shí)間段中 cgroup 中的任務(wù)對 CPU 資源的最長連續(xù)訪問時(shí)間。建立這個(gè)限制是為了防止一個(gè) cgroup 中的任務(wù)獨(dú)占 CPU 時(shí)間。如果 cgroup 中的任務(wù)應(yīng)該可以每 5 秒中可有 4 秒時(shí)間訪問 CPU 資源,請將 cpu.rt_runtime_us 設(shè)定為 4000000,并將 cpu.rt_period_us 設(shè)定為 5000000。
5)cpu.rt_period_us:以微秒(μs,這里以“us”代表)為單位指定在某個(gè)時(shí)間段中 cgroup 對 CPU 資源訪問重新分配的頻率。如果某個(gè) cgroup 中的任務(wù)應(yīng)該每 5 秒鐘有 4 秒時(shí)間可訪問 CPU 資源,則請將 cpu.rt_runtime_us 設(shè)定為 4000000,并將 cpu.rt_period_us 設(shè)定為 5000000。注意
sched_rt_runtime_us是實(shí)時(shí)任務(wù)的保證時(shí)間和最高占用時(shí)間,如果實(shí)時(shí)任務(wù)沒有使用,可以分配給非實(shí)時(shí)任務(wù),并且實(shí)時(shí)任務(wù)最終占用的時(shí)間不能超過這個(gè)數(shù)值,參考 Linux-85 關(guān)于sched_rt_runtime_us和sched_rt_period_us。對cpu.rt_period_us參數(shù)的限制是必須小于父目錄中的同名參數(shù)值。對cpu.rt_runtime_us的限制是:Sum_{i} runtime_{i} / global_period <= global_runtime / global_period即:
Sum_{i} runtime_{i} <= global_runtime當(dāng)前的實(shí)時(shí)進(jìn)程調(diào)度算法可能導(dǎo)致部分實(shí)時(shí)進(jìn)程被餓死,如下A和B是并列的,A的運(yùn)行時(shí)時(shí)長正好覆蓋了B的運(yùn)行時(shí)間:
* group A: period=100000us, runtime=50000us
- this runs for 0.05s once every 0.1s
* group B: period= 50000us, runtime=25000us
- this runs for 0.025s twice every 0.1s (or once every 0.05 sec).Real-Time group scheduling 中提出正在開發(fā) SCHED_EDF (Earliest Deadline First scheduling),優(yōu)先調(diào)度最先結(jié)束的實(shí)時(shí)進(jìn)程。
8.3 在 CentOS 中安裝 Cgroups
#若系統(tǒng)未安裝則進(jìn)行安裝,若已安裝則進(jìn)行更新。
yum install libcgroup
#查看運(yùn)行狀態(tài),并啟動服務(wù)
[root@localhost ~] service cgconfig status
Stopped
[root@localhost ~] service cgconfig start
Starting cgconfig service: [ OK ]
service cgconfig status 9 Running 1011
#查看是否安裝cgroup
[root@localhost ~] grep cgroup /proc/filesystems
8.4 查看 service 服務(wù)在哪個(gè) cgroup 組
systemctl status [pid] | grep CGroup 23
cat /proc/[pid]/cgroup
cd /sys/fs/ && find * ‐name "*.procs" ‐exec grep [pid] {} /dev/null \; 2> /dev/null
#查看進(jìn)程cgroup的最快方法是使用以下bash腳本按進(jìn)程名:
#!/bin/bash
THISPID=`ps ‐eo pid,comm | grep $1 | awk '{print $1}'`
cat /proc/$THISPID/cgroup
9. 如何使用 Cgroups
9.1 通過 systemctl 設(shè)置 cgroup
在使用命令 systemctl set-property 時(shí),可以使用 tab 補(bǔ)全:
$ systemctl set‐property user‐1000.slice
AccuracySec= CPUAccounting= Environment= LimitCPU= LimitNICE= LimitSIGPEN DING= SendSIGKILL=
BlockIOAccounting= CPUQuota= Group= LimitDATA= LimitNOFILE= LimitSTACK= U ser=
BlockIODeviceWeight= CPUShares= KillMode= LimitFSIZE= LimitNPROC= MemoryA ccounting= WakeSystem=
BlockIOReadBandwidth= DefaultDependencies= KillSignal= LimitLOCKS= LimitR SS= MemoryLimit=
BlockIOWeight= DeviceAllow= LimitAS= LimitMEMLOCK= LimitRTPRIO= Nice=
BlockIOWriteBandwidth= DevicePolicy= LimitCORE= LimitMSGQUEUE= LimitRTTIM E= SendSIGHUP=
這里有很多屬性可以設(shè)置,但并不是所有的屬性都是用來設(shè)置 cgroup 的,我們只需要關(guān)注 Block, CPU 和 Memory。
如果你想通過配置文件來設(shè)置 cgroup,service 可以直接在 /etc/systemd/system/xxx.service.d 目錄下面創(chuàng)建相應(yīng)的配置文件,slice 可以直接在 /run/systemd/system/xxx.slice.d 目錄下面創(chuàng)建相應(yīng)的配置文件。事實(shí)上通過 systemctl 命令行工具設(shè)置 cgroup 也會寫到該目錄下的配置文件中:
$ cat /run/systemd/system/user‐1000.slice.d/50‐CPUQuota.conf
[Slice]
CPUQuota=20%
9.2 設(shè)置 CPU 資源的使用上限
如果想嚴(yán)格控制 CPU 資源,設(shè)置 CPU 資源的使用上限,即不管 CPU 是否繁忙,對 CPU 資源的使用都不能超過這個(gè)上限。可以通過以下兩個(gè)參數(shù)來設(shè)置:
1)cpu.cfs_period_us = 統(tǒng)計(jì) CPU 使用時(shí)間的周期,單位是微秒(us) 2)cpu.cfs_quota_us = 周期內(nèi)允許占用的 CPU 時(shí)間(指單核的時(shí)間,多核則需要在設(shè)置時(shí)累加)
systemctl 可以通過 CPUQuota 參數(shù)來設(shè)置 CPU 資源的使用上限。例如,如果你想將用戶 tom 的 CPU 資源使用上限設(shè)置為 20%,可以執(zhí)行以下命令:
$ systemctl set‐property user‐1000.slice CPUQuota=20%
9.3 通過配置文件設(shè)置 cgroup(/etc/cgconfig.conf)
cgroup 配置文件所在位置 /etc/cgconfig.conf,其默認(rèn)配置文件內(nèi)容
mount {
cpuset = / cgroup / cpuset ;
cpu = / cgroup / cpu ;
cpuacct = / cgroup / cpuacct ;
memory = / cgroup / memory ;
devices = / cgroup / devices ;
freezer = / cgroup / freezer ;
net_cls = / cgroup / net_cls ;
blkio = / cgroup / blkio ;
}
相當(dāng)于執(zhí)行命令:
mkdir /cgroup/cpuset
mount ‐t cgroup ‐o cpuset red /cgroup/cpuset
……
mkdir /cgroup/blkio
[root@localhost ~] vi /etc/cgrules.conf
[root@localhost ~] echo 524288000 > /cgroup/memory/foo/memory.limit_in_b ytes
使用 cgroup 臨時(shí)對進(jìn)程進(jìn)行調(diào)整,直接通過命令即可,如果要持久化對進(jìn)程進(jìn)行控制,即重啟后依然有效,需要寫進(jìn)配置文件 /etc/cgconfig.conf 及 /etc/cgrules.conf。
10. 查看 Cgroup
10.1 通過 systemd 查看 cgroup
1)systemd-cgls 命令:通過 systemd-cgls 命令來查看,它會返回系統(tǒng)的整體 cgroup 層級,cgroup 樹的最高層 由 slice 構(gòu)成,如下所示:
$ systemd‐cgls ‐‐no‐page
├─1 /usr/lib/systemd/systemd ‐‐switched‐root ‐‐system ‐‐deserialize 22
├─user.slice
│ ├─user‐1000.slice
│ │ └─session‐11.scope
│ │ ├─9507 sshd: tom [priv]
│ │ ├─9509 sshd: tom@pts/3
│ │ └─9510 ‐bash
│ └─user‐0.slice
│ └─session‐1.scope
│ ├─ 6239 sshd: root@pts/0
│ ├─ 6241 ‐zsh
│ └─11537 systemd‐cgls ‐‐no‐page
└─system.slice 15 ├─rsyslog.service
│ └─5831 /usr/sbin/rsyslogd ‐n
├─sshd.service 18 │ └─5828 /usr/sbin/sshd ‐D
├─tuned.service
│ └─5827 /usr/bin/python2 ‐Es /usr/sbin/tuned ‐l ‐P 21 ├─crond.service
│ └─5546 /usr/sbin/crond ‐n
可以看到系統(tǒng) cgroup 層級的最高層由 user.slice 和 system.slice 組成。因?yàn)橄到y(tǒng)中沒有 運(yùn)行虛擬機(jī)和容器,所以沒有 machine.slice,所以當(dāng) CPU 繁忙時(shí),user.slice 和 system.slice 會各獲得 50% 的 CPU 使用時(shí)間。
user.slice 下面有兩個(gè)子 slice:user-1000.slice 和 user-0.slice,每個(gè)子 slice 都用 User ID (UID) 來命名,因此我們很容易識別出哪個(gè) slice 屬于哪個(gè)用戶。例如從上面的輸出信息中可以看出 user-1000.slice 屬于用戶 tom,user-0.slice 屬于用戶 root。
2)systemd-cgtop 命令:systemd-cgls 命令提供的只是 cgroup 層級的靜態(tài)信息快照,要想查看 cgroup 層級的動 態(tài)信息,可以通過 systemd-cgtop 命令查看:
$ systemd‐cgtop
Path Tasks %CPU Memory Input/s Output/s
/ 161 1.2 161.0M ‐ ‐ 5 /system.slice ‐ 0.1 ‐ ‐ ‐
/system.slice/vmtoolsd.service 1 0.1 ‐ ‐ ‐
/system.slice/tuned.service 1 0.0 ‐ ‐ ‐
/system.slice/rsyslog.service 1 0.0 ‐ ‐ ‐
/system.slice/auditd.service 1 ‐ ‐ ‐ ‐
/system.slice/chronyd.service 1 ‐ ‐ ‐ ‐
/system.slice/crond.service 1 ‐ ‐ ‐ ‐
/system.slice/dbus.service 1 ‐ ‐ ‐ ‐
/system.slice/gssproxy.service 1 ‐ ‐ ‐ ‐
/system.slice/lvm2‐lvmetad.service 1 ‐ ‐ ‐ ‐
/system.slice/network.service 1 ‐ ‐ ‐ ‐
/system.slice/polkit.service 1 ‐ ‐ ‐ ‐
/system.slice/rpcbind.service 1 ‐ ‐ ‐ ‐
/system.slice/sshd.service 1 ‐ ‐ ‐ ‐
/system.slice/system‐getty.slice/[email protected] 1 ‐ ‐ ‐ ‐
/system.slice/systemd‐journald.service 1 ‐ ‐ ‐ ‐
/system.slice/systemd‐logind.service 1 ‐ ‐ ‐ ‐
/system.slice/systemd‐udevd.service 1 ‐ ‐ ‐ ‐
/system.slice/vgauthd.service 1 ‐ ‐ ‐ ‐
/user.slice 3 ‐ ‐ ‐ ‐
/user.slice/user‐0.slice/session‐1.scope 3 ‐ ‐ ‐ ‐
/user.slice/user‐1000.slice 3 ‐ ‐ ‐ ‐
/user.slice/user‐1000.slice/session‐11.scope 3 ‐ ‐ ‐ ‐
/user.slice/user‐1001.slice/session‐8.scope
scope systemd-cgtop 提供的統(tǒng)計(jì)數(shù)據(jù)和控制選項(xiàng)與 top 命令類似,但該命令只顯示那些開啟了 資源統(tǒng)計(jì)功能的 service 和 slice。
如果你想開啟 sshd.service 的資源統(tǒng)計(jì)功能,可以進(jìn)行如下操作:
$ systemctl set‐property sshd.service CPUAccounting=true MemoryAccounting=true
#該命令會在 /etc/systemd/system/sshd.service.d/ 目錄下創(chuàng)建相應(yīng)的配置文件:
$ ll /etc/systemd/system/sshd.service.d/
總用量 8
4 ‐rw‐r‐‐r‐‐ 1 root root 28 5月 31 02:24 50‐CPUAccounting.conf
4 ‐rw‐r‐‐r‐‐ 1 root root 31 5月 31 02:24 50‐MemoryAccounting.conf
$ cat /etc/systemd/system/sshd.service.d/50‐CPUAccounting.conf
[Service]
CPUAccounting=yes 1415
$ cat /etc/systemd/system/sshd.service.d/50‐MemoryAccounting.conf
[Service]
MemoryAccounting=yes 1819
#配置完成之后,再重啟 sshd 服務(wù):
$ systemctl daemon‐reload 21 $ systemctl restart sshd
這時(shí)再重新運(yùn)行 systemd‐cgtop 命令,就能看到 sshd 的資源使用統(tǒng)計(jì)了。
10.2 通過 proc 查看 cgroup
如何查看當(dāng)前進(jìn)程屬于哪些 cgroup 可以通過查看 /proc/[pid]/cgroup(since Linux 2.6.24)知道指定進(jìn)程屬于哪些cgroup,如下:
$ cat /proc/777/cgroup
11:cpuset:/
10:freezer:/
9:memory:/system.slice/cron.service
8:blkio:/system.slice/cron.service
7:perf_event:/ 7 6:net_cls,net_prio:/
5:devices:/system.slice/cron.service
4:hugetlb:/
3:cpu,cpuacct:/system.slice/cron.service
2:pids:/system.slice/cron.service
1:name=systemd:/system.slice/cron.service
每一行包含用冒號隔開的三列,他們的意思分別是:
cgroup樹的ID :和cgroup樹綁定的所有subsystem :進(jìn)程在cgroup樹中的路徑
1)cgroup 樹的 ID,和
/proc/cgroups文件中的 ID 一一對應(yīng)。2)和 cgroup 樹綁定的所有 subsystem,多個(gè) subsystem 之間用逗號隔開。這里
name=systemd表示沒有和任何 subsystem 綁定,只是給他起了個(gè)名字叫 systemd。3)進(jìn)程在 cgroup 樹中的路徑,即進(jìn)程所屬的 cgroup,這個(gè)路徑是相對于掛載點(diǎn)的相對路徑。
10.3 通過 /sys 查看 cgroup
查看 cgroup 下 CPU 資源的使用上限:
$ cat /sys/fs/cgroup/cpu,cpuacct/user.slice/user‐1000.slice/cpu.cfs_perio d_us
100000
$ cat /sys/fs/cgroup/cpu,cpuacct/user.slice/user‐1000.slice/cpu.cfs_quota _us
20000
這表示用戶 tom 在一個(gè)使用周期內(nèi)(100 毫秒)可以使用 20 毫秒的 CPU 時(shí)間。不管 CPU 是否空閑,該用戶使用的 CPU 資源都不會超過這個(gè)限制。
CPUQuota 的值可以超過 100%,例如:如果系統(tǒng)的 CPU 是多核,且 CPUQuota 的值為 200%,那么該 slice 就能夠使用 2 核的 CPU 時(shí)間。
作者丨dvlinker
來源丨網(wǎng)址:https://blog.csdn.net/chenlycly/article/details/125956805
K8s技術(shù)圈社群歡迎廣大技術(shù)人員投稿,投稿郵箱:[email protected]
▲ 點(diǎn)擊上方卡片關(guān)注K8s技術(shù)圈,掌握前沿云原生技術(shù)

