1. 主页 > 世界杯2021 >

cgroup学习笔记之 cgroup的简介与使用

文章导读

本文记录学习cgroup的一些历程,由浅入深,从面到点的介绍了cgroup的相关知识,并以kubernetes使用cpu,memory cgroup为切入点进行分析,破除对于cgroup技术的恐惧心理,让cgroup能够为我所用。本文包含以下内容 :

为什么需要cgroup

cgroup的使用方法

cgroup的子系统介绍

cgroup v1的局限性

cgroup v2

查看cgroup的一些方法

systemd的slice,sope,unit介绍

kubelet使用cgroup的一些坑

cgroup的需求来源

计算机的硬件资源是有限的,如cpu,memory,io,network等,然而软件对于资源的需求是贪婪的。按照我的理解,cgroup就是来实现优先保证高优先级应用的资源需求,并确保应用对于资源的使用不会超量。所以cgroup就是限制进程的资源使用,并对资源的使用情况做统计。

说出来一些具体的事项,才能更好的理解cgroup。

DPDK绑定cpu,提高程序的运行效率,这就可以用到cpuset。

之前面试时,面试官问到监控工具对于系统有没有什么影响,如执行ansible,top,pidstat,vmstat等等工具。现在就有一些思路了,可以使用cgroup来限制这些监控程序的资源使用,来保证系统的稳定性。

一个节点上多个pod,共享cpu资源,就可以用cgroup来限制pod的cpu使用情况,设置某个pod最多可以用到5C等。

付费用户可以使用更多内存资源(资源统计),就可以使用cgroup 的memory来限制普通用户的资源使用。

某些程序会有bug,有内存泄露,也可以利用cgroup的memory来限制资源使用,不会对系统造成太大的影响。

以上都是强行编造....

cgroup子系统分类

以5.4的内核为例,cgroup包括cpu,memory,blokio,network...,这里的每一个文件夹都表示cgroup的一个子系统,这里只以cpu,memory为例进行介绍。

root@iZt4n1u8u50jg1r5n6myn2Z:~# mount -t tmpfs |grep cgroup

tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)

root@iZt4n1u8u50jg1r5n6myn2Z:~# mount -t cgroup

cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)

cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)

cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)

cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)

cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)

cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)

cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)

cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)

cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)

cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)

cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)

cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)

root@iZt4n1u8u50jg1r5n6myn2Z:~# ls /sys/fs/cgroup/

blkio cpu cpuacct cpu,cpuacct cpuset devices freezer hugetlb memory net_cls net_cls,net_prio net_prio perf_event pids rdma systemd unified

root@iZt4n1u8u50jg1r5n6myn2Z:~# uname -a

Linux iZt4n1u8u50jg1r5n6myn2Z 5.4.0-91-generic #102-Ubuntu SMP Fri Nov 5 16:31:28 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

root@iZt4n1u8u50jg1r5n6myn2Z:~#

这里请注意一个细节,这里有三层挂载,一个是sysfs,挂载点是/sys/,一个是tmpfs内存文件系统,挂载点是/sys/fs/cgroup,另一个是子系统的挂载,如cpu挂载点是/sys/fs/cgroup/cpu。三层挂载就是三个文件系统,一个是kernfs,一个是tmpfs,一个是cgroup文件系统。即使是基于内存的文件系统,也都有superblock,dentry,inode等这些vfs的概念。

cgroup的使用

常规使用

1、创建cgroup子系统的子目录

2、设置资源配额

3、将需要限制的进程号写入子目录

以cpu限额为例,限制当前shell最多使用1C。这里有个知识点,tasks和cgroup.procs有什么区别呢?按照官方文档的描述,将pid写入cgroup.procs,则该pid所在的线程组及该pid的子进程等都会自动加入到cgroup中。将pid写入tasks,则只限制该pid。

root@iZt4n1u8u50jg1r5n6myn2Z:~# mkdir /sys/fs/cgroup/cpu/myshell -p

root@iZt4n1u8u50jg1r5n6myn2Z:~# echo 100000 > /sys/fs/cgroup/cpu/myshell/cpu.cfs_quota_us

root@iZt4n1u8u50jg1r5n6myn2Z:~# echo $$ > /sys/fs/cgroup/cpu/myshell/cgroup.procs

cg工具集

cgcreate创建,cgdelete删除,cgget查询,cgset设置,cgexec执行等

root@iZt4n1u8u50jg1r5n6myn2Z:~# cgcreate -g cpu:mycg

root@iZt4n1u8u50jg1r5n6myn2Z:~# cgset -r cpu.cfs.cfs_quota_us=10000 /mycg

root@iZt4n1u8u50jg1r5n6myn2Z:~# cgexec -g cpu:mycg df -h -t ext4

Filesystem Size Used Avail Use% Mounted on

/dev/vda1 40G 29G 9.0G 77% /

root@iZt4n1u8u50jg1r5n6myn2Z:~# cgdelete -g cpu:mycg

systemd

通过systemd的接口设置服务的配额,与上诉两种方式的使用原理是一样的。systemcg-top的展示也不错。

root@iZt4n1u8u50jg1r5n6myn2Z:~# systemctl set-property sshd.service CPUShares=2048

root@iZt4n1u8u50jg1r5n6myn2Z:~# cat /sys/fs/cgroup/cpu/system.slice/ssh.service/cpu.shares

2048

root@iZt4n1u8u50jg1r5n6myn2Z:~# systemd-cgtop

Control Group Tasks %CPU Memory Input/s Output/s

/ 206 - 841.2M - -

assist - - 3.1M - -

docker 1 - 11.7M - -

docker/cbf77eadcd1bd5b4810eceeee1faa643a7d05bce9ca45d60f01813d15251c135 1 - 11.7M - -

system.slice 135 - 584.0M - -

system.slice/AssistDaemon.service 8 - 2.6M - -

system.slice/accounts-daemon.service 3 - 3.0M - -

system.slice/aegis.service 28 - 130.3M - -

system.slice/aliyun.service 9 - 15.0M - -

system.slice/atd.service 1 - 516.0K - -

system.slice/chrony.service 2 - 1.8M - -

kubelet使用systemd的cgroup驱动目录层级

这里kubelet使用systemd是指kubelet调用systemd的cgroup接口去管理cgroup,systemd管理cgroup与上面的几种cgroup的方法本质上是一致的

这里有三个slice。

kubepods.slice 所有pod的资源配额,下面还有pod级别的配额,容器级别的配额等。

system.slice systemd管理的服务的资源配额

user.slice ?

[root@10 ~]# ls /sys/fs/cgroup/cpu

cgroup.clone_children cpuacct.stat cpuacct.usage_percpu cpuacct.usage_sys cpu.cfs_quota_us cpu.shares notify_on_release tasks

cgroup.procs cpuacct.usage cpuacct.usage_percpu_sys cpuacct.usage_user cpu.rt_period_us cpu.stat release_agent user.slice

cgroup.sane_behavior cpuacct.usage_all cpuacct.usage_percpu_user cpu.cfs_period_us cpu.rt_runtime_us kubepods.slice system.slice

# pod级别配额

[root@10 ~]# ls -l /sys/fs/cgroup/cpu/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod41c19abf_8aaa_4b4f_9b4a_512194662502.slice/

total 0

-rw-r--r--. 1 root root 0 Nov 28 11:16 cgroup.clone_children

-rw-r--r--. 1 root root 0 Nov 28 11:16 cgroup.procs

-r--r--r--. 1 root root 0 Nov 28 11:16 cpuacct.stat

-rw-r--r--. 1 root root 0 Nov 28 11:16 cpuacct.usage

-r--r--r--. 1 root root 0 Nov 28 11:16 cpuacct.usage_all

-r--r--r--. 1 root root 0 Nov 28 11:16 cpuacct.usage_percpu

-r--r--r--. 1 root root 0 Nov 28 11:16 cpuacct.usage_percpu_sys

-r--r--r--. 1 root root 0 Nov 28 11:16 cpuacct.usage_percpu_user

-r--r--r--. 1 root root 0 Nov 28 11:16 cpuacct.usage_sys

-r--r--r--. 1 root root 0 Nov 28 11:16 cpuacct.usage_user

-rw-r--r--. 1 root root 0 Nov 28 11:16 cpu.cfs_period_us

-rw-r--r--. 1 root root 0 Nov 28 11:16 cpu.cfs_quota_us

-rw-r--r--. 1 root root 0 Nov 28 11:16 cpu.rt_period_us

-rw-r--r--. 1 root root 0 Nov 28 11:16 cpu.rt_runtime_us

-rw-r--r--. 1 root root 0 Nov 28 11:16 cpu.shares

-r--r--r--. 1 root root 0 Nov 28 11:16 cpu.stat

drwxr-xr-x. 2 root root 0 Nov 28 11:18 docker-095cb69032f07a3848e130014b2b574fda73818261ca3074065e3ae53b8f564d.scope

drwxr-xr-x. 2 root root 0 Nov 28 11:18 docker-f791c73f1fb7841079ea2ad89cee7c10f2926f6410f1ab2a01fbf8a65d9d1068.scope

# 容器级别配额

[root@10 ~]# ls -l /sys/fs/cgroup/cpu/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod41c19abf_8aaa_4b4f_9b4a_512194662502.slice/docker-095cb69032f07a3848e130014b2b574fda73818261ca3074065e3ae53b8f564d.scope/

total 0

-rw-r--r--. 1 root root 0 Nov 28 11:18 cgroup.clone_children

-rw-r--r--. 1 root root 0 Nov 28 11:18 cgroup.procs

-r--r--r--. 1 root root 0 Nov 28 11:18 cpuacct.stat

-rw-r--r--. 1 root root 0 Nov 28 11:18 cpuacct.usage

-r--r--r--. 1 root root 0 Nov 28 11:18 cpuacct.usage_all

-r--r--r--. 1 root root 0 Nov 28 11:18 cpuacct.usage_percpu

-r--r--r--. 1 root root 0 Nov 28 11:18 cpuacct.usage_percpu_sys

-r--r--r--. 1 root root 0 Nov 28 11:18 cpuacct.usage_percpu_user

-r--r--r--. 1 root root 0 Nov 28 11:18 cpuacct.usage_sys

-r--r--r--. 1 root root 0 Nov 28 11:18 cpuacct.usage_user

-rw-r--r--. 1 root root 0 Nov 28 11:18 cpu.cfs_period_us

-rw-r--r--. 1 root root 0 Nov 28 11:18 cpu.cfs_quota_us

-rw-r--r--. 1 root root 0 Nov 28 11:18 cpu.rt_period_us

-rw-r--r--. 1 root root 0 Nov 28 11:18 cpu.rt_runtime_us

-rw-r--r--. 1 root root 0 Nov 28 11:18 cpu.shares

-r--r--r--. 1 root root 0 Nov 28 11:18 cpu.stat

-rw-r--r--. 1 root root 0 Nov 28 11:18 notify_on_release

-rw-r--r--. 1 root root 0 Nov 28 11:18 tasks

查看cgroup个数

/proc/cgroups可以查看当前系统挂载了多少子系统,每个子系统的cgroup个数。

其中hierarchy一列是按照mount的顺序生成的一个编号。num_cgroups表示该子系统下有多少个cgroup目录,可以使用find命令查看。

内核函数入口 :proc_cgroupstats_show

[root@10 ~]# cat /proc/cgroups |column -t

#subsys_name hierarchy num_cgroups enabled

cpuset 7 308 1

cpu 4 310 1

cpuacct 4 310 1

blkio 5 310 1

memory 10 314 1

devices 6 310 1

freezer 13 308 1

net_cls 2 308 1

perf_event 12 308 1

net_prio 2 308 1

hugetlb 3 308 1

pids 8 310 1

rdma 11 1 1

misc 9 1 1

// 与上面/proc/cgroups的输出是一致的。

[root@10 ~]# find /sys/fs/cgroup/cpu/ -type d | wc -l

310

[root@10 ~]# find /sys/fs/cgroup/blkio/ -type d | wc -l

310

查看进程的所有cgroup信息

内核函数入口 :proc_cgroup_show

root@iZt4n1u8u50jg1r5n6myn2Z:~# cat /proc/38538/cgroup

12:net_cls,net_prio:/

11:devices:/system.slice/ssh.service

10:hugetlb:/

9:rdma:/

8:memory:/system.slice/ssh.service

7:pids:/system.slice/ssh.service

6:blkio:/system.slice/ssh.service

5:cpuset:/

4:freezer:/

3:cpu,cpuacct:/system.slice/ssh.service

2:perf_event:/

1:name=systemd:/system.slice/ssh.service

0::/system.slice/ssh.service

kubelet使用cgroup的坑

宿主机上除了跑pod的应用,还有其他系统组件,如sshd,kubelet,docker,containerd等服务,且系统组件的优先级应该比pod应用更高,所以可以通过kubelet的资源预留功能为系统组件预留一部分的cpu和memory资源。既可以为system预留资源,也可以单独为kubelet应用预留资源,这里只以system预留为例说明。

systemReserved 配置为系统组件的预留资源,cpu和memory

systemReservedCgroup 配置预留资源的cgroup路径

enforceNodeAllocatable 这里要配置上pods,systemReserved,高版本的kubernetes,如果这里不加上pods这个配置项,将导致预留项不生效。

验证cgroup预留资源是否生效的方法也很简单,以memory为例,kubepods的memorylimit + system的memorylimt = 宿主机的memory即表明预留资源生效。

cgroupv2

众所周知,cgroupv1无法限制bufferio的写入。下面将演示如何启用cgroupv2以及使用cgroupv2限制bufferio的写入。从使用体验上,cgroupv2比cgroupv1更简单。

关闭cgroupv1的内核入口函数 __setup("cgroup_no_v1=", cgroup_no_v1);

// 关闭cgroupv1

root@iZt4n1u8u50jg1r5n6myn2Z:~/lugl# cat /boot/grub/grub.cfg | grep cgroup

linux /boot/vmlinuz-5.4.0-91-generic root=UUID=12e5697d-887b-4790-a160-75cf814081a6 ro vga=792 console=tty0 console=ttyS0,115200n8 net.ifnames=0 noibrs quiet cgroup_no_v1=all

// 创建cgroup

root@iZt4n1u8u50jg1r5n6myn2Z:~/lugl# mkdir /sys/fs/cgroup/unified/iotest

// 使能io + memory

root@iZt4n1u8u50jg1r5n6myn2Z:~/lugl# echo "+io +memory" > /sys/fs/cgroup/unified/cgroup.subtree_control

// 将当前进程挪到iotest的cgroup中

root@iZt4n1u8u50jg1r5n6myn2Z:~/lugl# echo $$ >/sys/fs/cgroup/unified/iotest/cgroup.procs

// 设置最大写入速度10M/s

root@iZt4n1u8u50jg1r5n6myn2Z:~/lugl# echo "252:0 wbps=10485760" > /sys/fs/cgroup/unified/iotest/io.max

root@iZt4n1u8u50jg1r5n6myn2Z:~/lugl# fio -iodepth=1 -rw=write -ioengine=libaio -bs=4k -size=300M -numjobs=1 -name=./fio.test

./fio.test: (g=0): rw=write, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=1

fio-3.16

Starting 1 process

Jobs: 1 (f=1): [W(1)][47.8%][w=11.4MiB/s][w=2928 IOPS][eta 00m:12s]