同步文件:https://blog.haohtml.com/archives/29974
在容器里有兩個非常重要的概念,一個是namespace,用來進(jìn)行對容器里所有進(jìn)程的隔離;另一個就是cgroup,用來對容器資源進(jìn)行限制。那cgroup又是如何實(shí)現(xiàn)對進(jìn)行資源的限制呢,今天我們來了解一下它的實(shí)現(xiàn)原理。
什么是cgroup
cgroup 是 Control Groups 的縮寫,是 Linux 內(nèi)核提供的一種可以限制、記錄、隔離 進(jìn)程組 所使用的物理資源(如 cpu、memory、磁盤IO等等) 的機(jī)制,被 LXC、docker 等很多項(xiàng)目用于實(shí)現(xiàn)進(jìn)程資源控制。cgroup 將任意進(jìn)程進(jìn)行分組化管理的 Linux 內(nèi)核功能。
cgroup 本身是提供將進(jìn)程進(jìn)行分組化管理的功能和接口的基礎(chǔ)結(jié)構(gòu),I/O 或內(nèi)存的分配控制等具體的資源管理功能是通過這個功能來實(shí)現(xiàn)的。 一定要切記,這里的限制單元為進(jìn)程組,而不是進(jìn)程。
子系統(tǒng)
上面這些具體的資源管理功能統(tǒng)稱為 cgroup 子系統(tǒng),所有子系統(tǒng)列表可以通過 cat /proc/cgroups 命令查看,主要有以下幾大子系統(tǒng):
# cat /proc/cgroups
#subsys_name hierarchy num_cgroups enabled
cpuset 4 7 1
cpu 2 89 1
cpuacct 2 89 1
blkio 3 86 1
memory 7 150 1
devices 6 84 1
freezer 5 7 1
net_cls 10 7 1
perf_event 12 7 1
net_prio 10 7 1
hugetlb 8 7 1
pids 9 94 1
rdma 11 1 1
子系統(tǒng)功能
- cpuset:若是是多核心的CPU, 這個子系統(tǒng)會為cgroup 任務(wù)分配單獨(dú)的CPU和內(nèi)存。
- cpu:使用調(diào)度程序?yàn)閏group任務(wù)提供CPU的訪問。
- cpuacct:產(chǎn)生cgroup, 任務(wù)的CPU資源報(bào)告
- blkio:設(shè)置限制每一個塊設(shè)備的輸入輸出控制。例如:磁盤,光盤以及usb 等等。
- memory: 設(shè)置每一個cgroup 的內(nèi)存限制以及產(chǎn)生內(nèi)存資源報(bào)告。
- devices:容許或拒絕cgroup任務(wù)對設(shè)備的訪問。
- freezer:暫停和恢復(fù)cgroup任務(wù)。
- net_cls: 標(biāo)記每一個網(wǎng)絡(luò)包以供cgroup 方便使用。
- ns:命名空間子系統(tǒng),能夠設(shè)置一個子系統(tǒng)的上限配額。
- perf_event: 增加了對每一個group 的監(jiān)測跟蹤的能力,能夠監(jiān)測屬于某個特定的group 的全部線程以及運(yùn)行在特定,監(jiān)控能力超出限制則進(jìn)行終止。
- net_prio 設(shè)置cgroup中進(jìn)程產(chǎn)生的網(wǎng)絡(luò)流量的優(yōu)先級
- hugetlb 限制使用的內(nèi)存頁數(shù)量
- pids 限制任務(wù)的數(shù)量
目前 docker 只是用了其中一部分子系統(tǒng),實(shí)現(xiàn)對資源配額和使用的控制。如可以使用 freezer 子系統(tǒng)對 進(jìn)行組 進(jìn)行掛起和恢復(fù)。
cgroup組件術(shù)語
- task:在cgroup中,任務(wù)就是系統(tǒng)的一個進(jìn)程
- subsystem:一個子系統(tǒng)就是一個資源控制器,比如 cpu 子系統(tǒng)就是控制 cpu 時間分配的一個控制器。子系統(tǒng)必須附加(attach)到一個層級上才能起作用,一個子系統(tǒng)附加到某個層級以后,這個層級上的所有控制族群都受到這個子系統(tǒng)的控制。
- control group:控制族群就是按照某種標(biāo)準(zhǔn)劃分的進(jìn)程。Cgroups 中的資源控制都是以控制族群為單位實(shí)現(xiàn)。一個進(jìn)程可以加入到某個控制族群,也從一個進(jìn)程組遷移到另一個控制族群。一個進(jìn)程組的進(jìn)程可以使用 cgroups 以控制族群為單位分配的資源,同時受到 cgroups 以控制族群為單位設(shè)定的限制;
- hierarchy:樹形結(jié)構(gòu)的 CGroup 層級,每個子 CGroup 節(jié)點(diǎn)會繼承父 CGroup 節(jié)點(diǎn)的子系統(tǒng)配置,每個 Hierarchy 在初始化時會有默認(rèn)的 CGroup(Root CGroup);
控制族群可以組織成 hierarchical 的形式,既一顆控制族群樹。控制族群樹上的子節(jié)點(diǎn)控制族群是父節(jié)點(diǎn)控制族群的孩子,繼承父控制族群的特定的屬性。比如一組task進(jìn)程通過cgroup1限制了CPU使用率,然后其中一個日志進(jìn)程還需要限制磁盤IO,為了避免限制磁盤IO影響到其他進(jìn)程,就可以創(chuàng)建cgroup2,使其繼承cgroup1并限制磁盤IO,這樣這樣cgroup2便繼承了cgroup1中對CPU使用率的限制并且添加了磁盤IO的限制而不影響到cgroup1中的其他進(jìn)程;
組件關(guān)系
遵循原則:
- 每次在系統(tǒng)中創(chuàng)建新層級時,該系統(tǒng)中的所有任務(wù)都是那個層級的默認(rèn) cgroup(我們稱之為 root cgroup,此 cgroup 在創(chuàng)建層級時自動創(chuàng)建,后面在該層級中創(chuàng)建的 cgroup 都是此 cgroup 的后代)的初始成員;
- 一個 subsystem 最多只能附加到一個層級 hierarchy;
- 一個層級 hierarchy 可以附加多個子系統(tǒng) subsystem;
- 一個任務(wù) task 可以是多個 cgroup 的成員,但是這些 cgroup 必須在不同的層級hierarchy;
系統(tǒng)中的進(jìn)程(任務(wù))創(chuàng)建子進(jìn)程(任務(wù))時,該子任務(wù)自動成為其父進(jìn)程所在 cgroup 的成員。然后可根據(jù)需要將該子任務(wù)移動到不同的 cgroup 中,但開始時它總是繼承其父任務(wù)的 cgroup。 - 一個進(jìn)程fork出子進(jìn)程時,該子進(jìn)程默認(rèn)自動成為父進(jìn)程所在的cgroup的成員,也可以根據(jù)情況將其移動到到不同的cgroup中.
如圖所示,CPU 和 Memory 兩個子系統(tǒng)有自己獨(dú)立的層級系統(tǒng),而又通過 Task Group 取得關(guān)聯(lián)關(guān)系


CGroup 技術(shù)可以被用來在操作系統(tǒng)底層限制物理資源,起到 Container 的作用。上圖中每一個 JVM 進(jìn)程對應(yīng)一個 Container Cgroup 層級,通過 CGroup 提供的各類子系統(tǒng),可以對每一個 JVM 進(jìn)程對應(yīng)的線程級別進(jìn)行物理限制,這些限制包括 CPU、內(nèi)存等等許多種類的資源。
cgroup實(shí)戰(zhàn)
在 Linux 中,cgroups 給用戶暴露出來的操作接口是文件系統(tǒng),即它以文件和目錄的方式組織在操作系統(tǒng)的 /sys/fs/cgroup 路徑下。在 Ubuntu 16.04 機(jī)器里,可以用 mount 指令把它們展示出來:
$ mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
它的輸出是一些文件系統(tǒng)目錄,這些目錄名就是當(dāng)前系統(tǒng)所支持的子系統(tǒng),這些子系統(tǒng)都在 /sys/fs/cgroup/目錄內(nèi),如對于cpu子系統(tǒng)來說,相關(guān)的幾個配置文件為
$ ls /sys/fs/cgroup/cpu
aegis cgroup.procs cpu.cfs_quota_us cpuacct.stat cpuacct.usage_percpu cpuacct.usage_sys kubepods.slice system.slice user.slice
assist cgroup.sane_behavior cpu.shares cpuacct.usage cpuacct.usage_percpu_sys cpuacct.usage_user notify_on_release tasks
cgroup.clone_children cpu.cfs_period_us cpu.stat cpuacct.usage_all cpuacct.usage_percpu_user init.scope release_agent test
其中 cpu.cfs_quota_us 和 cpu.cfs_period_us 是經(jīng)常使用的兩個配置項(xiàng),兩者必須組合使用,表示一個進(jìn)程組在 cpu.cfs_period_us 段時間內(nèi),分配給CPU的時間比例為 cpu.cfs_quota_us。
另外輸出結(jié)果中包含一些子目錄,如 aegis、assist、kubepods.slice、system.slice、user.slice、test 和 init.scope。
現(xiàn)在我們看下這些子系統(tǒng)配置文件如何使用,首先我們在 /sys/fs/cgroup/cpu/ 目錄下創(chuàng)建一個目錄 mycontainer,這個目錄稱為cgroup,即"控制組"。
$ cd /sys/fs/cgroup/cpu/
$ mkdir mycontainer
$ sys/fs/cgroup/cpu# ls mycontainer/
cgroup.clone_children cpu.cfs_period_us cpu.shares cpu.uclamp.max cpuacct.stat cpuacct.usage_all cpuacct.usage_percpu_sys cpuacct.usage_sys notify_on_release
cgroup.procs cpu.cfs_quota_us cpu.stat cpu.uclamp.min cpuacct.usage cpuacct.usage_percpu cpuacct.usage_percpu_user cpuacct.usage_user tasks
會發(fā)現(xiàn)mycontainer目錄時會自動出現(xiàn)一些cpu配置文件,有些配置文件內(nèi)容為-1,表示不限制,其中tasks文件里表示要控制的進(jìn)程pid。
我們現(xiàn)在做個實(shí)現(xiàn)執(zhí)行一下死循環(huán)腳本,便其完全占用CPU達(dá)到100%,然后再對此PID進(jìn)行CPU限制,看下效果如果。
$ while : ; do : ; done &
[1] 1626025
執(zhí)行top查看
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1626025 root 20 0 12724 1768 0 R 100.0 0.0 0:28.60 bash
發(fā)現(xiàn)這個進(jìn)程的CPU已經(jīng)達(dá)到了100%,下面我們對其進(jìn)行一下限制。先將進(jìn)程PID寫到 mycontainer 控制組下的tasks文件里,然后限制cpu使用率
$ echo 1626025 > /sys/fs/cgroup/cpu/mycontainer/tasks
$ cat /sys/fs/cgroup/cpu/mycontainer/tasks
1626025
現(xiàn)在我們已成功將其進(jìn)程號寫入tasks文件。上面我們提到過對cpu的限制主要使用兩個文件,分別為cpu.cfs_quota_us 和 cpu.cfs_quota_us, 先看一下他們的默認(rèn)值。
$ cat /sys/fs/cgroup/cpu/mycontainer/cpu.cfs_quota_us
-1
$ cat /sys/fs/cgroup/cpu/mycontainer/cpu.cfs_period_us
100000
表示在100ms內(nèi)分配給cpu的機(jī)會為不限制,也就是表示100%的資源。我們要做一下限制,讓其在100ms時間內(nèi),只分配給 20% 的cpu機(jī)會
$ echo 20000 > /sys/fs/cgroup/cpu/mycontainer/cpu.cfs_quota_us
然后再執(zhí)行一下top命令發(fā)現(xiàn)cpu使用率立即降下來了,最多為20%左右,可能會有一點(diǎn)點(diǎn)的超出,這個很正常。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1626025 root 20 0 12724 1768 0 R 20.0 0.0 12:43.38 bash
這里我們只對cpu做了限制,你也可以做內(nèi)在memory做一下限制,由于這里的腳本只會占用cpu,所以不再演示。對于我們經(jīng)常使用docker run 命令啟動一個容器的時候,其實(shí)都有一個配置參數(shù)與配置文件相對應(yīng),如
$ docker run -it --cpu-period=100000 --cpu-quota=20000 ubuntu /bin/bash
如果你到容器目錄查看配置文件會發(fā)現(xiàn)相應(yīng) cpu.cfs_period_us 和 cpu.vfs_quota_us 的值都已被修改。
參考鏈接
https://blog.csdn.net/xwy9526/article/details/110594876
http://edsionte.com/techblog/archives/4322
https://www.cnblogs.com/plxx/p/5129245.html