cgroup介绍:全名为contorl group,即进程组的行为控制,它会通过对子系统配置文件的读写完成对进程及其后续的子进程组资源的控制
安装:
Yum install libcgroup –y
服务名:
Cgconfig
/etc/init.d/cgconfig start
默认路径:
/cgroup
配置文件路劲:
/etc/cgconfig
子系统:
Blkio cpu cpuacct cpuset devices freezer memory net_cls ns
Blkio:限制每个设备的输入输出 例如:磁盘,usb等
Cpu:限制cpu的访问,主要限制进程使用cpu的比重
Cpuset:主要限制进程能够使用哪些cpu和内存节点
Cpuacct:cpu的资源报告
Devices:允许或拒绝cgroup任务对设备的访问
Freezer:暂停和回复cgroup任务
Memory:限制内存的使用
Net_cls:标记每个网络包
Ns:名称空间子系统
这些子系统需要挂载在一个位置
如何挂载一个子系统以及不同的写法
1.Mount –t cgroup –o cpuset cpuset/cgroup/cpuset
2.写/etc/cgconfig
mount {
cpuset = /cgroup/cpuset;
}
3.cgcreate -g cpset:/
这三种写法的区别是什么
/cgroup/cpu/test1
tasks
/cgroup/cpu/test2
进程的管理组
什么叫做进程组
[root@master cpuset]# cat/etc/cgconfig.conf
# Configuration file generated bycgsnapshot
mount {
net_cls = /cgroup/net_cls;
}
mount {
blkio = /cgroup/blkio;
}
mount {
cpuset = /cgroup/cpuset;
}
再看一个cgconfig配置文件配置的例子
mount {
cpu = /cgroup/cpu;
}
group daemons/www {
perm {#定义组的权限
task {
uid = root;
gid= webmaster;
fperm = 770;
}
admin {
uid = root;
gid = root
fperm = 744;
}
}
cpu {
cpu.shares = "1000";
}
}
group daemons/ftp {
perm {
task {
uid = root;
gid = ftpmaster;
fperm = 774;
}
admin {
uid = root;
gid = root;
dperm = 755;
fperm = 700;
}
}
cpu {
cpu.shares = "500";
}
}
查看当前哪些子系统被挂在到哪些目录下
[root@master cgroup]# lssubsys -am
ns
cpu
cpuacct
memory
devices
freezer
perf_event
net_prio
cpuset /cgroup/cpuset
net_cls /cgroup/net_cls
blkio /cgroup/blkio
查看哪些子系统被使用了
[root@master cgroup]# lssubsys
cpuset
net_cls
blkio
被使用的子系统下都生成了哪些管理组
[root@master cgroup]# lscgroup
net_cls:/
net_cls:/test_bw
net_cls:/test
blkio:/
cpuset:/
/cgroup/net_cls/test_bw
重要的子系统说明
Cpu子系统
测试
[root@master]#echo ‘while True: pass’|python&
[1]1532
top 一下可以看到,这进程占了 100% 的 cpu
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1532 root 20 0 112m 3684 1708 R 99.6 0.7 0:30.42 python
如果我想让这个进程只是占用cpu时间的50%,
首先在cpu这个子系统下面创建一个test管理组
Cgcreate –g/cgroup/cpu/test
先把 /cgroup/cpu/test 这个控制组的限制修改一下,然后把进程加入tasks。
echo 50000 > /cgroup/cpu/test/cpu.cfs_quota_us
echo 1532 >/sys/fs/group/cpu/foo/tasks
可见,修改设置只需要写入相应文件,将进程加入 cgroup 也只需将 pid 写入到其中的 tasks 文件即可。这里将 cpu.cfs_quota_us 设为 50000,相对于cpu.cfs_period_us的 100000 即 50%,这里没有修改cpu.cfs_period_us,是因为cpu.cfs_period_us是配置cpu时间周期的,默认是100000,单位为微妙 再 top 一下看看效果。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1532 root 20 0 112m 3684 1708 R 50.2 0.7 5:00.31 python
cpu.stat - 记录cpu统计信息,包含 nr_periods(经历了几个cfs_period_us), nr_throttled (cgroup里的task被限制了几次), throttled_time (cgroup里的task被限制了多少纳秒)
举个例子
看下java.yq 37
随便找了个应用的cgroup
nr_periods 20748
nr_throttled 32
throttled_time 71595208978
来说说
cpu.rt_period_us
cpu.rt_runtime_us
与
cpu.cfs_period_us cpu.cfs_quota_us区别前者是系统自身进程用的,后者是用户进程用的,前者很少使用
如果我有很多的进程需要做限制呢
下面我起两个进程
[root@master cpu]# echo 'while True:pass'|python &
[1] 4569
[root@master cpu]# echo 'while True:pass'|python &
[2] 4571
查看状态
PIDUSER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4571root 20 0 112m 3804 1796 R 49.2 0.4 0:52.78 python
4569root 20 0 112m 3804 1796 R 48.9 0.4 0:54.65 python
如果想让pid为4569占用cpu的时间是pid为4571的2倍
1.先创建2个管理组
Mkdir/cgroup/cpu/test1 && Mkdir /cgroup/cpu/test2
还是有3种创建的方法
Echo4569 > / cgroup/cpu/test1/tasks
Echo4571 > / cgroup/cpu/test2/tasks
Echo1 > / cgroup/cpu/test1/cpu.shares
Echo2 > / cgroup/cpu/test2/cpu.shares
2.测试
PIDUSER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4571root 20 0 112m 3804 1796 R 34.3 0.4 0:52.78 python
4569root 20 0 112m 3804 1796 R 59.9 0.4 0:54.65 python
Cpuset子系统
如果我服务器有很多核,我只想让一个进程用某个cpu,或者多少到多少的cpu
cpuset.cpus 比如说
我想用第3核
Echo 3 > cpuset.cpus
如果说我想用第3或者第8核
Echo 3,8 > cpuset.cpus
如果说我想用第3-第8核
Echo 3-8 > cpuset.cpus
Cpuset.mems是内存节点的使用和cpuset.cpus的配置一致
查看我这个进程使用cpu和内存节点情况可以去/proc/pid/status文件中查看
这里由于我是自己的虚拟机是单核没有进行测试
Cpuacct子系统
主要就一个比较有用
[root@master cpuacct]# cat cpuacct.stat
user 347086
system 10475
bliko子系统
dd if=/dev/sda of=/dev/null &
[1] 2750
用 iotop 看看目前的 io
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
2750 be/4 root 66.76 M/s 0.00 B/s 0.00 % 68.53 % dd if=/dev/sda of=/dev/null
...
然后修改一下资源限制,把进程加入控制组
echo '8:0 1048576' >/sys/fs/cgroup/blkio/foo/blkio.throttle.read_bps_device
echo 2750 >/sys/fs/cgroup/blkio/foo/tasks
这里的 8:0 就是对应块设备的主设备号和副设备号。可以通过 ls -l
设备文件名查看。如
# ls -l /dev/sda
brw-rw----. 1 root disk 8, 0 Oct 24 11:27 /dev/sda
这里的 8, 0 就是对应的设备号。所以,cgroups 可以对不同的设备做不同的限制。然后来看看效果
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
2750 be/4 root 989.17 K/s 0.00 B/s 0.00 % 96.22 % dd if=/dev/sda of=/dev/null
...
可见,进程的每秒读取立马就降到了 1MB 左右。要解除限制,写入如 “8:0 0” 到文件中即可
不过需要注意的是,这种方式对小于采样间隔里产生的大量 io是没用的。比如,就算在 1s 内产生一个每秒写入 100M 的峰值,也不会因此被限制掉。
再看看 blkio.weight 。blkio 的 throttle 和 weight 方式和 cpu 子系统的quota 和 shares 有点像,都是一种是绝对限制,另一种是相对限制,并且在不繁忙的时候可以充分利用资源,权重值的范围在 10 – 1000 之间。
测试权重方式要麻烦一点。因为不是绝对限制,所以会受到文件系统缓存的影响。如在虚拟机中测试,要关闭虚机如我用的 VirtualBox 在宿主机上的缓存。如要测试读 io 的效果,先生成两个几个 G 的大文件 /tmp/file_1,/tmp/file_2 ,可以用 dd 搞。然后设置两个权重
# echo 500 >/sys/fs/cgroup/blkio/foo/blkio.weight
# echo 100 >/sys/fs/cgroup/blkio/bar/blkio.weight
测试前清空文件系统缓存,以免干扰测试结果
sync
echo 3 >/proc/sys/vm/drop_caches
在这两个控制组中用 dd 产生 io 测试效果。
# cgexec -g "blkio:foo" dd if=/tmp/file_1 of=/dev/null &
[1] 1838
# cgexec -g "blkio:bar" dd if=/tmp/file_2 of=/dev/null &
[2] 1839
还是用 iotop 看看效果
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
1839 be/4 root 48.14 M/s 0.00 B/s 0.00 % 99.21 % dd if=/tmp/file_2 of=/dev/null
1838 be/4 root 223.59 M/s 0.00 B/s 0.00 % 16.44 % dd if=/tmp/file_1 of=/dev/null
内存不建议做
Memory子系统
cgroups 中有个 memory 子系统,用于限制和报告进程的内存使用情况。
其中,很明显有两组对应的文件,一组带 memsw ,另一组不带
memory.failcnt
memory.limit_in_bytes
memory.max_usage_in_bytes
memory.usage_in_bytes
memory.memsw.failcnt
memory.memsw.limit_in_bytes
memory.memsw.max_usage_in_bytes
memory.memsw.usage_in_bytes
带 memsw 的表示虚拟内存,即物理内存加交换区。不带 memsw 的那组仅包括物理内存。其中,limit_in_bytes 是用来限制内存使用的,其他的则是统计报告。
# echo 10485760 >/sys/fs/cgroup/memory/foo/memory.limit_in_bytes
即可限制该组中的进程使用的物理内存总量不超过 10MB。对 memory.memsw.limit_in_bytes 来说,则是限制虚拟内存使用。memory.memsw.limit_in_bytes必须大于或等于 memory.limit_in_byte。这些值还可以用更方便的 100M,20G 这样的形式来设置。要解除限制,就把这个值设为 -1 即可。
这种方式限制进程内存占用会有个风险。当进程试图占用的内存超过限制,访问内存时发生缺页,又没有足够的非活动内存页可以换出时会触发 oom ,导致进程直接被杀,从而造成可用性问题。即使关闭控制组的oom killer,进程在内存不足的时候,虽然不会被杀,但是会长时间进入 D (等待系统调用的不可中断休眠)状态,无法继续执行,导致仍然无法服务。因此,我认为,用 memory.limit_in_byt es或 memory.memsw.limit_in_bytes 限制进程内存占用仅应当作为一个保险,避免在进程异常时耗尽系统资源。如,预期一组进程最多只会消耗 1G 内存,那么可以设置为 1.4G 。这样在发生内存泄露等异常情况时,可以避免造成更严重问题。
在 memory 子系统中,还有一个memory.soft_limit_in_bytes 。和 memory.limit_in_bytes 的差异是,这个限制并不会阻止进程使用超过限额的内存,只是在系统内存不足时,会优先回收超过限额的进程占用的内存,使之向限定值靠拢。
前面说控制组的 oom killer 是可以关闭的,就是通过 memory.oom_control 来实现的。cat memory.oom_control
可以看到当前设置以及目前是否触发了 oom 。echo 1>memory.oom_control
就可以禁用 oom killer。
usage_in_bytes、max_usage_in_bytes、failcnt 则分别对应当前使用量,最高使用量和发生的缺页次数。
memory 子系统中还有一个很重要的设置是 memory.use_hierarchy 这是个布尔开关,默认为 0。此时不同层次间的资源限制和使用值都是独立的。当设为 1 时,子控制组进程的内存占用也会计入父控制组,并上溯到所有memory.use_hierarchy = 1 的祖先控制组。这样一来,所有子孙控制组的进程的资源占用都无法超过父控制组设置的资源限制。同时,在整个树中的进程的内存占用达到这个限制时,内存回收也会影响到所有子孙控制组的进程。这个值只有在还没有子控制组时才能设置。之后在其中新建的子控制组默认的 memory.use_hierarchy 也会继承父控制组的设置。
memory.swappiness 则是控制内核使用交换区的倾向的。值的范围是 0 – 100。值越小,越倾向使用物理内存。设为 0 时,只有在物理内存不足时才会使用交换区。默认值是系统全局设置: /proc/sys/vm/swappiness 。
memory.stat 就是内存使用情况报告了。包括当前资源总量、使用量、换页次数、活动页数量等等。
Net_cls子系统
tc qdisc add dev $DEV root handle1: htb
tc class add dev $DEV parent 1: classid 1: htb rate 1000mbit ceil1000mbit
tc class add dev $DEV parent 1: classid 1:2 htb rate ${limit}mbit
tc filter add dev $DEV protocol ip parent 1:0 prio 1 handle 1:2cgroup
echo 0x00010003 >net_cls.classid