Linux Cgroups技术介绍与实践
什么是Cgroups
Linux Cgroups (Control Groups )
提供了对 组进程及将来子进程的资源限制、控制和统
计的能力,这些资源包括 CPU、内存、存储、网络等通过Cgroups
,可以方便地限制某个进
程的资源占用,并且可以实时地监控进程的监控和统计信息。
Cgroups中的3个组件
-
cgroup
是对进程分组管理的一种机制,cgroup
包含一组进程,并可以在这个cgroup
上增加Linux subsystem
的各种参数配置,将一组进程和subsystem
的系统参数关联起来。 -
subsystem
是一组资源控制的模块,每个subsystem
会关联到定义了相应限制的cgroup
上,并对这个cgroup
中的进程做相应的限制和控制,如限制这个cgroup中的进程的内存数,cpu核心数等 -
hierarchy
的功能是把cgroup
串成一个树状的结构,一个这样的树便是hierarchy
,通过这种树状结构,Cgroups
可以做到继承。比如,系统对一组定时的任务进程通过cgroup
限制了CPU
的使用率,然后其中有一个定时dump
日志的进程还需要限制磁盘IO
,为了避免限制了磁盘IO
之后影响到其他进程,就可以创建cgroup2
,使其继承于cgroup
并限制磁盘的IO
,这样cgroup2
便继承了cgroup1
中对CPU
使用率的限制,并且增加了磁盘IO
的限制而不影响到cgroup1
中的其他进程。
三个组件的关系
- 系统在创建了新的
hierarchy
之后,系统中所有的进程都会加入这个hierarchy cgroup
根节点,这个cgroup
根节点是hierarchy
默认创建的, - 一个
subsystem
只能附加到一个hierarchy
上面 - 一个
hierarchy
可以附加多个subsystem
- 一个进程可以作为多个
cgroup
的成员,但是这些cgroup
必须在不同的hierarchy
- 一个进程
fork
出子进程时,子进程是和父进程在同一个cgroup
中的,也可以根据需要将其移动到其他cgroup
操作Cgroups
1.创建并挂在一个hierarchy(cgroup树)
mkdir cgroupTest #创建一个hierarchy挂载点
sudo mount -t cgroup -o none,name=cgroupTest cgroupTest ./cgroupTest #挂载一个hierarchy
ls cgroupTest #挂载后我们就可以看到系统在这个目录下生成了一些默认文件
cgroup.clone_children cgroup.procs cgroup.sane_behavior
notify_on_release release_agent tasks
这些文件就是这个hierarchy cgroup
根节点的配置项,上面这些文件的含义分别如下。
-
cgroup.clone_children
:cpuset
的subsystem
会读取这个配置文件,如果这个值是1(默认是0),子cgroup
才会继承父cgroup
的cpuset
配置 -
cgroup.procs:
是树中当前节点cgroup
中的进程组ID
,现在的位置是在根节点,这个文件中会有现在系统中所有进程组的ID
-
notify_on _release和release agent
会一起使用。notify on release
标识当这个cgroup
最后一个进程退出的时候是否执行了release_agent
;release_ agent
则是一个路径,通常用作进程退出之后自动清理掉不再使用的cgroup
-
tasks
标识cgroup
下面的进程ID
,如果把一个进程ID
写到tasks
文件中,便会将相应的进程加入到这个cgroup
2.创建hierarchy cgroup根节点中扩展出的两个子cgroup
sudo mkdir cgroup-1 #创建子cgroup cgroup-1
sudo mkdir cgroup-2 #创建子cgroup cgroup-2
tree
.
├── cgroup-1
│ ├── cgroup.clone_children
│ ├── cgroup.procs
│ ├── notify_on_release
│ └── tasks
├── cgroup-2
│ ├── cgroup.clone_children
│ ├── cgroup.procs
│ ├── notify_on_release
│ └── tasks
├── cgroup.clone_children
├── cgroup.procs
├── cgroup.sane_behavior
├── notify_on_release
├── release_agent
└── tasks
可以看到,在cgroup
的目录下创建文件夹时,Kernel
会把文件夹标记为这个cgroup
的子cgroup
,它们会继承父cgroup
的属性。
3.cgroup中添加和移动进程
一个进程在Cgroups hierarchy
中,只能在一个cgroup
节点上存在,系统的所有进程都会默认在根节点上存在,将进程移动到其他cgroup
节点,只需要将进程ID
到移动到的cgroup
节点的tasks
文件中即可。
cd cgroup-1
echo $$ #当前终端的进程id
27461
sudo sh -c "echo $$ >> tasks" # 将我所在的终端进程移动到cgroup-1
cat /proc/27461/cgroup
15:name=cgroupTest:/cgroup-1
14:name=systemd:/
13:rdma:/
12:pids:/
11:hugetlb:/
10:net_prio:/
9:perf_event:/
8:net_cls:/
7:freezer:/
6:devices:/
5:memory:/
4:blkio:/
3:cpuacct:/
2:cpu:/
1:cpuset:/
0::/
可以看到, 前的27461进程己经被加到cgroup-test:/cgroup-1
中了。
4.通过subsystem限制cgroup中进程的资源
在上面创建hierarchy
的时候,这个hierarchy
并没有关联到任何的subsystem
,所以没办法通过那个hierarchy
中的cgroup
节点限制进程的资源占用,其实系统默认已经为每个subsystem
创建了一个默认的hierarchy
,比如memory hierarchyo
mount | grep memory #
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
可以看到,/sys/fs/cgrou/memory
目录便是挂在了memory subsystem hierarchy
上。下面,就通过在这个 hierarchy
中创建cgroup
,限制如下进程占用的内存。
cd /sys/fs/cgroup/memory
# 首先,在不做限制的情况下,启动一个占用200MB内存的stress进程
stress --vm-bytes 200m --vm-keep -m 1
#这时打开另一个终端输入top命令
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
31132 root 20 0 208664 204952 272 R 100.0 2.5 0:12.49 stress
#可以看到%MEM占用为2.5%,因为我linux系统内存为8GB
#接下来切换终端回来
#创建一个cgroup
sudo mkdir test-limit-memory && cd test-limit-memory
#设置该cgroup的最大内存占用为1OOMB
sudo sh -c "echo "lOOm" > memory.limit_in_bytes"
#将当前进程移动到这个 cgroup
sudo sh -c "echo $$ > tasks"
#再次运行占用内存 200MB stress 进程
stress --vm-bytes 200m --vm-keep -m 1
#查看刚刚那个运行了top的终端
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
31132 root 20 0 208664 10164 272 R 100.0 1.3 0:12.49 stress
#可以看到内存占用变为1.3%,缩小为一半