cgroups(control groups)是Linux内核2.6.24引入的一个新特性 ,用来限制、分离和报告一个进程组的资源(CPU、内存、磁盘输入输出等)。常用的ulimit只能基于用户或者用户组来进行资源限制,不能够针对某一进程作出详细限制,缺乏灵活度。
cgroups的一个设计目标是为不同的应用情况提供资源限制的统一接口:
内存资源限制:可以对于一个组设置内存上限;
CPU和I/O的优先级:可以使一些组得到更高的CPU或磁盘I/O使用权;
报告:可以用来衡量系统确实把多少资源用到合适的目标;
分离:分离组的命名空间,这样一个组内不会看到另一个组的进程、网络连接和文件。
控制:冻结组或检查点和重启动
1. 安装cgroup
安装libcgroup.x86_64(Tools and libraries to control and monitor control groups)
1 |
SHELL# yum install -y libcgroup |
开机自动启动cgconfig服务:
1 |
SHELL# chkconfig cgconfig on |
该服务使用/etc/cgconfig.conf作为配置文件,该文件文档
1 |
SHELL# man cgconfig.conf |
虽然以lib开头,libcgroup中也带着所有cgroup管理和控制的可执行文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
SHELL# rpm -ql libcgroup-0.37-3.el6.i686 | grep bin /bin/cgclassify /bin/cgcreate /bin/cgdelete /bin/cgexec /bin/cgget /bin/cgset /bin/cgsnapshot /bin/lscgroup /bin/lssubsys /sbin/cgclear /sbin/cgconfigparser /sbin/cgrulesengd |
2. 子系统、层级和控制组
(1). cgroup使用各个子系统来管理系统的不同类型的资源,子系统也称为资源控制器。
使用lssubsys -am可以列出所有子系统(无论是否被挂载)。
SHELL# lssubsys -am
…
cgroup下面有如下subsys, 限制对应的资源:
blkio 这个子系统设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及usb等等。
cpu 这个子系统使用调度程序为cgroup任务提供cpu的访问。
cpuacct 产生cgroup任务的cpu资源报告。
cpuset 如果是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存。
devices 允许或拒绝cgroup任务对设备的访问。
freezer 暂停和恢复cgroup任务。
memory 设置每个cgroup的内存限制以及产生内存资源报告。
net_cls 标记数据包( 这个子系统使用等级识别符(classid)标记网络数据包,可允许 Linux 流量控制程序(tc)
识别从具体 cgroup 中生成的数据包。)。
ns 名称空间子系统。可以把一个数值写到对应的子系统目录的对应文件中。
(2). Control Group以目录层级(hierarchies)的方式来组织,每一个子系统在同一时间只能在一个目录层级下面。默认情况下,每个子系统在/cgroup目录下都有自己的目录层级。
子系统也可以手动挂载到某个目录层级下面,例如创建一个cpu_and_mem目录,该层级下同时使用cpu和mem两个子系统:
1 2 |
SHELL# mkdir /cgroup/cpu_and_mem SHELL# mount -t cgroup -o cpu,memory cpu_and_memory /cgroup/cpu_and_mem |
#也可以使用-o remount来添加/删除子系统:
1 |
SHELL# mount -t cgroup -o remount,cpu,blkio cpu_and_memory /cgroup/cpu_and_mem |
这时查看/proc/cgroups信息,可以看到cpu和blkio这两个子系统已经mount到hierarchy中。
1 2 3 4 5 6 7 8 9 10 11 12 |
SHELL# cat /proc/cgroups subsys_name hierarchy num_cgroups enabled cpuset 0 1 1 ns 0 1 1 cpu 1 1 1 cpuacct 0 1 1 memory 0 1 1 devices 0 1 1 freezer 0 1 1 net_cls 0 1 1 blkio 1 1 1 perf_event 0 1 1 |
我们可以在这个目录结构下,看到所有blkio和cpu子系统的限制参数。
1 2 |
SHELL# ls /cgroup/cpu_and_mem/ ...... |
这些参数都可以直接查看,例如:
1 2 |
SHELL# cat cpu.shares 1024 |
同理,这些值也可以使用直接修改:
1 |
SHELL# echo 100 > /cgroup/cpu/cpu.shares |
(3). 由于mount了/cgroup/cpu_and_mem这个目录层级,我们使用cgcreate在层级下创建control group,cgcreate的命令语法如下:
cgcreate [-t uid:gid] [-a uid:gid] -g subsystem[,subsystem […]]:/path_to_hierarchy [-g subsystem:/path_to_hierarchy […]]
例如:
cgcreate -g cpu:/webcpu
cpu和blkio之前我们将其mount到了同一hierarchy下面,所以在这里面只有一个子目录生成,其中也有cpu和blkio子系统的全部参数。这些参数继承了父级hierarchy(/cgroup/cpu_and_mem)的值。
也就是说无论webcpu这个control group有没有用到blkio这个组,这个目录层级下面也包含blkio和cpu的全部值。
1 2 3 |
SHELL# ls /cgroup/cpu_and_mem/webcpu/ blkio.io_merged blkio.throttle.write_iops_device ...... |
可以使用cgdelete来删除一个/多个子系统层级下的cg(control group):(比如cgdelete cpu,blkio:webcpu)
1 |
SHELL# cgdelete blkio:webcpu |
但是由于blkio和cpu挂载到同一hierarchy下面,删除blkio:webcpu这个control group会删除webcpu这个目录层级,连带着cpu:webcpu的部分也被删除了。
(4). 下面我们做另外一个测试:
新挂载一个cpuacct子系统
1 2 3 4 5 6 7 8 9 |
SHELL# mount -t cgroup -o cpuacct cpuacct /cgroup/cpuacct SHELL# cgcreate -g cpu,cpuacct:/webapp SHELL# cd /cgroup/cpuacct/webapp/ cgroup.procs cpuacct.usage notify_on_release cpuacct.stat cpuacct.usage_percpu tasks SHELL# cgdelete cpuacct:webapp SHELL# ls /cgroup/cpu_and_mem/webapp cgroup.procs cpu.cfs_period_us ...... |
这说明当cg用到的子系统不在同一目录层级下面时候,cgdelete cpuacct:webapp仅仅是删除了cpuacct的层级。
(5). 使用mkdir、rmdir来创建cg
1 2 3 4 5 6 7 8 9 10 |
cd /cgroup/cpuacct SHELL# cd /cgroup/cpuacct/ SHELL# ls cgroup.procs cpuacct.usage notify_on_release tasks cpuacct.stat cpuacct.usage_percpu release_agent SHELL# mkdir newcg SHELL# cd newcg SHELL# ls cgroup.procs cpuacct.usage notify_on_release cpuacct.stat cpuacct.usage_percpu tasks |
这说明在cgroup的子系统层级或子层级下面创建目录,相当于执行了cgroup命令。该层级下自动继承了父层级下所有的值。
同理,删除
1 |
SHELL# rmdir /cgroup/cpuacct/newcg |
3. cgroup.conf
cgconfig服务可以让cg的定义永久生效,这样重启之后cg依然可用。cgconfig服务的配置文件是/etc/cgconfig.conf。我们可以看到,配置文件里已经默认定义了子系统的mount配置:
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;
}
mount的层级也可以根据需要来修改。下面我们直接定义一个组:
group
[permissions]
…
}
…
}
例如:
group example {
cpu {
cpu.shares=100; #根据和其他cpu.shares是比例的关系
}
blkio {
blkio.throttle.read_bps_device = “252:0 1048576″;
}
}
定义好组以后,在对应的下面就会出现: /cgroup/cpu/exmaple 和/cgroup/cpu/exmaple,而相关参数的值也定义到该层级下的对应参数中。
关于permission项,root用户当然拥有该cg的全部权限,其他用户遵循下面的语法定义:
perm {
task {
uid =
gid =
}
admin {
uid =
gid =
}
}
其中,task用户和组是cg文件的属主。该用户/组可以修改cg下面的tasks文件的属主(见第4节)。那么task用户/组就可以使用使用这个cg来管理某个进程。
admin用户和组拥有该cg层级下全部文件的权限,也就是说admin用户可以修改参数值、创建子cg等。
修改完cgconfig.conf后,需要重启cgconfig服务,注意重启该服务时
pwd
不能为/cgroup层级下。SHELL# service cgroup restart
4. 使用cg管理进程
(1). 进程的cgroup
进程之前也存在层级和继承的关系。当一个进程被fork出来时,它会继承父进程的大部分属性和内存资源。其中所属的cgroup也是被继承过来的。我们可以在下面的地方找到某个进程所属的cgroup:
1 2 3 4 5 6 7 8 9 |
SHELL# /proc/1/cgroup 12:blkio:/ 11:net_cls:/ 10:freezer:/ 9:devices:/ 8:memory:/ 7:cpuacct:/ 6:cpu:/ 5:cpuset:/ |
这样我们就得到了1号进程(init)的cg信息,它位于所有子系统的根层级。
(2). 手动管理进程
有三种方法可以使用cg来管理某一个进程,当进程已经存在时,可以直接将其输出到cg层级的tasks文件中来(可以用该层级task user/group来进行)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
SHELL# cgcreate -g cpu:/cpu SHELL# echo $$ > /cgroup/cpu/ #$$ 是当前Shell的pid cgroup.procs cpu.cfs_period_us cpu.rt_period_us cpu.shares notify_on_release tasks cpu/ cpu.cfs_quota_us cpu.rt_runtime_us cpu.stat release_agent SHELL# echo $$ > /cgroup/cpu/cpu/tasks SHELL# cat /proc/11827/cgroup 12:blkio:/ 11:net_cls:/ 10:freezer:/ 9:devices:/ 8:memory:/ 7:cpuacct:/ <b>6:cpu:/cpu</b> 5:cpuset:/ |
或者使用cgclassify命令:
1 |
SHELL# cgclassify -g cpu:/bash -g memory:/bash $$ |
也可以使用cgexec来指定一个新执行的进程到cg中:
1 |
SHELL# cgexec -g cpu:/eample /path_to_program |
(3). cgred
上面的方法都缺乏灵活性,cgroup提供了一个专门的服务——cgred来将进程指定到某个cg中来。该服务也有一个对应的服务:/etc/cgrules.conf,定义格式如下:
user|@group:[program] controller cgroup
例如 把所有运行dd的进程和oracle.expdp放到blkio/bigload这个cg中,并所有apache用户下的进程指定到cpu和memory的webapp这个cg
*:dd blkio /bigload/ #一定要以/结尾
oracle:expdp blkio /bigload/
apache.* cpu,meory /example/
然后reload服务,就可以生效。
1 |
SHELL# service cgred reload |
5. 一些常用的子系统参数
(1). 内存大小
memory.limit_in_bytes和memory.limit_sw_in_bytes是最常用的限制内存大小的参数,limit_in_bytes限制了内存总量的使用,而limit_sw_in_bytes限制了交换空间和物理的内存的使用。当二者想当时,相当于禁用了交换空间。
(2). 磁盘I/O
限制读吞吐量:
blkio.throttle.read_bps_device MAJOR_DEVICE#:MINOR_DEVICE#
blkio.throttle.read_iops_device MAJOR_DEVICE#:MINOR_DEVICE#
限制写吞吐量:
blkio.throttle.write_bps_device MAJOR_DEVICE#:MINOR_DEVICE#
blkio.throttle.write_iops_device MAJOR_DEVICE#:MINOR_DEVICE#
(3). cpu.shares
包含用来指定在 cgroup 中的任务可用的相对共享 CPU 时间的整数值。例如:在两个 cgroup 中都将 cpu.shares 设定为 1 的任务将有相同的 CPU 时间,但在 cgroup 中将 cpu.shares 设定为2 的任务可使用的 CPU 时间是在 cgroup 中将 cpu.shares 设定为 1 的任务可使用的 CPU 时间的两倍。
其他的参数的详细解释可以在RHEL6 Resouce Management文档中找到。
^^
参考
wikimedia
RHEL docs
Linux Manual
应该是雕塑展http://www.elcorreo.com/fotos/culturas/201607/20/museo-bellas-artes-reabre-201678188522-mm.html#
什么时候更新啊 我都等的急死了