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和内存节点

Cpuacctcpu的资源报告

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 

如果想让pid4569占用cpu的时间是pid45712

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。这些值还可以用更方便的 100M20G 这样的形式来设置。要解除限制,就把这个值设为 -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_bytesmax_usage_in_bytesfailcnt 则分别对应当前使用量,最高使用量和发生的缺页次数。

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