本文主要記錄 Linux Cgroup V2 版本基本使用操作,包括 cpu、memory 子系統演示。
1. 開啟 Cgroup V2
版本檢查
透過下面這條命令來檢視當前系統使用的 Cgroups V1 還是 V2
stat -fc %T /sys/fs/cgroup/
如果輸出是cgroup2fs
那就是 V2,就像這樣
root@tezn:~# stat -fc %T /sys/fs/cgroup/
cgroup2fs
如果輸出是tmpfs
那就是 V1,就像這樣
[root@docker cgroup]# stat -fc %T /sys/fs/cgroup/
tmpfs
啟用 cgroup v2
如果當前系統未啟用 Cgroup V2,也可以透過修改核心 cmdline 引導引數在你的 Linux 發行版上手動啟用 cgroup v2。
如果你的發行版使用 GRUB,則應在 /etc/default/grub
下的 GRUB_CMDLINE_LINUX
中新增 systemd.unified_cgroup_hierarchy=1
, 然後執行 sudo update-grub
。
具體如下:
1)編輯 grub 配置
vi /etc/default/grub
內容大概是這樣的:
GRUB_DEFAULT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=0
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX=""
對最後一行GRUB_CMDLINE_LINUX
進行修改
GRUB_CMDLINE_LINUX="quiet splash systemd.unified_cgroup_hierarchy=1"
2)然後執行以下命令更新 GRUB 配置
sudo update-grub
3)最後檢視一下啟動引數,確認配置修改上了
cat /boot/grub/grub.cfg | grep "systemd.unified_cgroup_hierarchy=1"
4)然後就是重啟
reboot
重啟後檢視,不出意外切換到 cgroups v2 了
root@cgroupv2:~# stat -fc %T /sys/fs/cgroup/
cgroup2fs
發行版推薦
不過,推薦的方法仍是使用一個預設已啟用 cgroup v2 的發行版。
有關使用 cgroup v2 的 Linux 發行版的列表,
- Container-Optimized OS(從 M97 開始)
- Ubuntu(從 21.10 開始,推薦 22.04+)
- Debian GNU/Linux(從 Debian 11 Bullseye 開始)
- Fedora(從 31 開始)
- Arch Linux(從 2021 年 4 月開始)
- RHEL 和類似 RHEL 的發行版(從 9 開始)
2. 基本使用
cgroup v2 使用上和 v1 版本基本一致,v2 版本也是預設在/sys/fs/cgroup/
目錄。
root@mydocker:~# ls /sys/fs/cgroup/
cgroup.controllers cgroup.subtree_control init.scope system.slice
cgroup.max.depth cgroup.threads io.cost.model user.slice
cgroup.max.descendants cpu.pressure io.cost.qos
cgroup.procs cpuset.cpus.effective io.pressure
cgroup.stat cpuset.mems.effective memory.pressure
- 建立 sub-cgroup: 只需要建立一個子目錄
cd /sys/fs/cgroup
mkdir $CGROUP_NAME
- 將程序移動到指定 cgroup:將 PID 寫入到相應 cgroup 的 cgroup.procs 檔案即可,就像這樣:
echo 1001 > /sys/fs/cgroup/test/cgroup.procs
- 刪除 cgroup/sub-cgroup: 也是直接刪除對應目錄即可
- 如果一個cgroup 已經沒有任何children或活程序,那直接刪除對應的資料夾就刪除該cgroup了
- 如果一個cgroup已經沒有children,但是還有殭屍程序,也認為這個cgroup是空的,可以直接刪除
rmdir /sys/fs/cgroup/test
- 修改 cpu、memory 限制:往對應配置檔案寫入配置內容即可
- cpu.max 用於配置 cpu 使用限制
- memory.max 則用於配置 記憶體使用限制
- ...
建立 cgroup
接下來,以 cpu、memory 為例,簡單演示一下 cgroup v2 版本使用
root@mydocker:~# cd /sys/fs/cgroup/
root@mydocker:/sys/fs/cgroup# mkdir test
root@mydocker:/sys/fs/cgroup# cd test
root@mydocker:/sys/fs/cgroup/test# ls
cgroup.controllers cpu.uclamp.max memory.current
cgroup.events cpu.uclamp.min memory.events
cgroup.freeze cpu.weight memory.events.local
cgroup.max.depth cpu.weight.nice memory.high
cgroup.max.descendants cpuset.cpus memory.low
cgroup.procs cpuset.cpus.effective memory.max
cgroup.stat cpuset.cpus.partition memory.min
cgroup.subtree_control cpuset.mems memory.oom.group
cgroup.threads cpuset.mems.effective memory.pressure
cgroup.type io.max memory.stat
cpu.max io.pressure pids.current
cpu.pressure io.stat pids.events
cpu.stat io.weight pids.max
CPU
啟動一個死迴圈
root@mydocker:/sys/fs/cgroup/test# while : ; do : ; done &
[1] 90482
不出意外的話,應該佔用了 100% cpu
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
90482 root 20 0 10160 1772 0 R 99.3 0.1 0:05.01 bash
接下來使用 cgroup v2 限制該程序只能使用 20% cpu
1)修改配置
echo 2000 10000 > cpu.max
含義是在 10000 微秒的 CPU 時間週期內,有 2000 微秒是分配給本 cgroup 的,也就是本 cgroup 管理的程序在單核 CPU 上的使用率不會超過 20%。
2)將程序加入當前 cgroup
echo 90482 > cgroup.procs
再次檢視
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
90482 root 20 0 10160 1772 0 R 20.2 0.1 2:07.78 bash
可以看到,已經被限制到了 20%
Memory
接下來演示記憶體限制,使用以下程式碼來模擬記憶體消耗,
cat <<EOF > ~/mem-allocate.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MB (1024 * 1024)
int main(int argc, char *argv[])
{
char *p;
int i = 0;
while(1) {
p = (char *)malloc(MB);
memset(p, 0, MB);
printf("%dM memory allocated\n", ++i);
sleep(1);
}
return 0;
}
EOF
編譯
gcc ~/mem-allocate.c -o ~/mem-allocate
然後啟動該檔案
root@mydocker:/sys/fs/cgroup/test# ~/mem-allocate
1M memory allocated
2M memory allocated
3M memory allocated
4M memory allocated
5M memory allocated
6M memory allocated
7M memory allocated
8M memory allocated
9M memory allocated
10M memory allocated
11M memory allocated
12M memory allocated
^C
可以看到,每秒會消耗 1M 記憶體,若不停止會一直執行直到 OOM。
接下來使用 cgroup v2 限制最多消耗 10M 記憶體。
1)修改配置
單位為位元組 10485760= 10 * 1024 * 1024
echo 10485760 > memory.max
也就是本 cgroup 管理的程序記憶體使用不會超過 10M
2)將程序加入當前 cgroup
#將當前bash加入到test中,這樣這個bash建立的所有程序都會自動加入到test中
sh -c "echo $$ >> cgroup.procs"
再次檢視
root@mydocker:/sys/fs/cgroup/test# ~/mem-allocate
1M memory allocated
2M memory allocated
3M memory allocated
4M memory allocated
5M memory allocated
6M memory allocated
7M memory allocated
8M memory allocated
9M memory allocated
Killed
可以看到,到 10M 時就因為達到記憶體上限而被 Kill 了。
刪除 cgroup
演示完成,把 cgroup 刪除。
首先把程序 kill 一下
root@mydocker:/sys/fs/cgroup# cat test/cgroup.procs
90444
90630
root@mydocker:/sys/fs/cgroup# kill -9 90630
root@mydocker:/sys/fs/cgroup# kill -9 90444
然後刪除目錄
root@mydocker:/sys/fs/cgroup# rmdir test
這樣 cgroup 就刪除了。
3. v1 v2 對比
v1 的 cgroup 為每個控制器都使用獨立的樹(目錄)
[root@docker cgroup]# ls /sys/fs/cgroup/
blkio cpu cpuacct cpuacct,cpu cpu,cpuacct cpuset devices freezer hugetlb memory net_cls net_cls,net_prio net_prio perf_event pids rdma systemd
每個目錄就代表了一個 cgroup subsystem,比如要限制 cpu 則需要到 cpu 目錄下建立子目錄(樹),限制 memory 則需要到 memory 目錄下去建立子目錄(樹)。
比如 Docker 就會在 cpu、memory 等等目錄下都建立一個名為 docker 的目錄,在 docker 目錄下在根據 containerID 建立子目錄來實現資源限制。
各個 Subsystem 各自為政,看起來比混亂,難以管理
因此最終的結果就是:
- 使用者空間最後管理著多個非常類似的 hierarchy,
- 在執行 hierarchy 管理操作時,每個 hierarchy 上都重複著相同的操作。
v2 中對 cgroups 的最大更改是將重點放在簡化層次結構上
- v1 為每個控制器使用獨立的樹(例如
/sys/fs/cgroup/cpu/GROUPNAME
和/sys/fs/cgroup/memory/GROUPNAME
)。 - v2 將統一
/sys/fs/cgroup/GROUPNAME
中的樹,如果程序 X 加入/sys/fs/cgroup/test
,則啟用 test 的每個控制器都將控制程序 X。
更多 v1 和 v2 差異見 v1
存在的問題及 v2
的設計考慮
【從零開始寫 Docker 系列】持續更新中,搜尋公眾號【探索雲原生】訂閱,閱讀更多文章。
4. 小結
本文主要分享了 Linux cgroup v2 版本的基本使用,以及 v1 和 v2 版本的差異。
更多 cgroup v2 資訊推薦閱讀:Control Group v2 及其譯文 Control Group v2(cgroupv2 權威指南)(KernelDoc, 2021)