暂未分类 暂未分类 cgroup实践-资源控制 zphj1987 2015-03-24 2024-01-05 1、Cgroup安装 安装Cgroups需要libcap-devel和libcgroup两个相关的包
1 yum install gcc libcap-devel
2、Cgroup挂载配置
1 2 3 4 5 6 7 8 Cgroup对应服务名称为cgconfig,cgconfig默认采用“多挂载点”挂载。经过实际测试,发现在CentOS环境中应采用“单挂载点”进行挂载,因此应当卸载原有cgroup文件系统,并禁用cgconfig。 cgclear或者sudo service cgconfig stop sudo chkconfig cgconfig off 然后采用“单挂载点”方式重新挂载cgroup。 可以直接手动挂载,这样仅当次挂载成功。 mount -t cgroup none /cgroup 然后编辑/etc/fstab/,输入下列内容。这样每次开机后都会自动挂载。 none /cgroup cgroup defaults 0 0
3、常用的Cgroup相关命令和配置文件
1 2 3 4 5 6 7 service cgconfig status|start|stop|restart lssubsys –am cgclear cgconfigparser -l /etc/cgconfig.conf Cgroup默认挂载点(CentOS):/cgroup cgconfig配置文件:/etc/cgconfig.conf
4、libcgroup Man Page简介
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 man 1 cgclassify -- cgclassify命令是用来将运行的任务移动到一个或者多个cgroup。 man 1 cgclear -- cgclear 命令是用来删除层级中的所有cgroup。 man 5 cgconfig.conf -- 在cgconfig.conf文件中定义cgroup。 man 8 cgconfigparser -- cgconfigparser命令解析cgconfig.conf文件和并挂载层级。 man 1 cgcreate -- cgcreate在层级中创建新cgroup。 man 1 cgdelete -- cgdelete命令删除指定的cgroup。 man 1 cgexec -- cgexec命令在指定的cgroup中运行任务。 man 1 cgget -- cgget命令显示cgroup参数。 man 5 cgred.conf -- cgred.conf是cgred服务的配置文件。 man 5 cgrules.conf -- cgrules.conf 包含用来决定何时任务术语某些 cgroup的规则。 man 8 cgrulesengd -- cgrulesengd 在 cgroup 中发布任务。 man 1 cgset -- cgset 命令为 cgroup 设定参数。 man 1 lscgroup -- lscgroup 命令列出层级中的 cgroup。 man 1 lssubsys -- lssubsys 命令列出包含指定子系统的层级。
测试一:限制cpu的资源 测试后验证了可以做到:
限制进程的cpu占用百分比
限制多个进程组的之间的cpu使用权重
指定进程的使用的cpu和内存组(绑定cpu)
跑一个耗cpu的脚本
1 2 3 4 x=0 while [ True ];do x=$x +1 done ;
top可以看到这个脚本基本占了100%的cpu资源
1 2 3 4 5 6 7 8 9 top - 15:30:01 up 1:03, 5 users , load average: 0.30, 0.50, 0.39 Tasks: 210 total, 2 running, 208 sleeping, 0 stopped, 0 zombie Cpu(s): 6.3%us, 0.1%sy, 0.0%ni, 93.5%id , 0.2%wa, 0.0%hi, 0.0%si, 0.0%st Mem: 49461228k total, 13412644k used, 36048584k free, 75384k buffers Swap: 2097148k total, 0k used, 2097148k free, 12498636k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 11605 root 20 0 104m 1528 1016 R 99.7 0.0 2:30.48 sh 105 root 20 0 0 0 0 S 0.3 0.0 0:00.11 kworker/8:1
创建一个控制组控制这个进程的cpu资源
1 2 3 mkdir -p /cgroup/cpu/foo echo 50000 > /cgroup/cpu/foo/cpu.cfs_quota_us echo 11605 > /cgroup/cpu/foo/tasks
然后top的实时统计数据如下,cpu占用率将近50%,看来cgroups关于cpu的控制起了效果
1 2 3 4 5 6 7 8 9 top - 15:32:48 up 1:06, 5 users , load average: 0.80, 0.68, 0.48 Tasks: 210 total, 2 running, 208 sleeping, 0 stopped, 0 zombie Cpu(s): 3.2%us, 0.0%sy, 0.0%ni, 96.8%id , 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Mem: 49461228k total, 13412276k used, 36048952k free, 75400k buffers Swap: 2097148k total, 0k used, 2097148k free, 12498652k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 11605 root 20 0 104m 1724 1016 R 50.2 0.0 5:09.97 sh 11639 root 20 0 15200 1200 820 R 0.3 0.0 0:00.03 top
可以看到,进程的 cpu 占用已经被成功地限制到了 50% 。这里,测试的虚拟机只有一个核心。在多核情况下,看到的值会不一样。另外,cfs_quota_us 也是可以大于 cfs_period_us 的,这主要是对于多核情况。有 n 个核时,一个控制组中的进程自然最多就能用到 n 倍的 cpu 时间。
这两个值在 cgroups 层次中是有限制的,下层的资源不能超过上层。具体的说,就是下层的 cpu.cfs_period_us 值不能小于上层的值,cpu.cfs_quota_us 值不能大于上层的值。
另外的一组 cpu.rt_period_us、cpu.rt_runtime_us 对应的是实时进程的限制,平时可能不会有机会用到。
在 cpu 子系统中,cpu.stat 就是用前面那种方法做的资源限制的统计了。nr_periods、nr_throttled 就是总共经过的周期,和其中受限制的周期。throttled_time 就是总共被控制组掐掉的 cpu 使用时间。
还有个 cpu.shares, 它也是用来限制 cpu 使用的。但是与 cpu.cfs_quota_us、cpu.cfs_period_us 有挺大区别。cpu.shares 不是限制进程能使用的绝对的 cpu 时间,而是控制各个组之间的配额。比如
1 2 /cpu/cpu.shares : 1024 /cpu/foo/cpu.shares : 2048
那么当两个组中的进程都满负荷运行时,/foo 中的进程所能占用的 cpu 就是 / 中的进程的两倍。如果再建一个 /foo/bar 的 cpu.shares 也是 1024,且也有满负荷运行的进程,那 /、/foo、/foo/bar 的 cpu 占用比就是 1:2:1 。前面说的是各自都跑满的情况。如果其他控制组中的进程闲着,那某一个组的进程完全可以用满全部 cpu。可见通常情况下,这种方式在保证公平的情况下能更充分利用资源。
此外,还可以限定进程可以使用哪些 cpu 核心。cpuset 子系统就是处理进程可以使用的 cpu 核心和内存节点,以及其他一些相关配置。这部分的很多配置都和 NUMA 有关。其中 cpuset.cpus、cpuset.mems 就是用来限制进程可以使用的 cpu 核心和内存节点的。这两个参数中 cpu 核心、内存节点都用 id 表示,之间用 “,” 分隔。比如 0,1,2 。也可以用 “-” 表示范围,如 0-3 。两者可以结合起来用。如“0-2,6,7”。在添加进程前,cpuset.cpus、cpuset.mems 必须同时设置,而且必须是兼容的,否则会出错。例如
1 2 3 4 5 这样, /foo 中的进程只能使用 cpu0 和内存节点0。用
cgroups 除了用来限制资源使用外,还有资源统计的功能。做云计算的计费就可以用到它。有一个 cpuacct 子系统专门用来做 cpu 资源统计。cpuacct.stat 统计了该控制组中进程用户态和内核态的 cpu 使用量,单位是 USER_HZ,也就是 jiffies、cpu 滴答数。每秒的滴答数可以用 getconf CLK_TCK 来获取,通常是 100。将看到的值除以这个值就可以换算成秒。
测试二:限制进程的内存资源 测试后验证了:
测试方法:
跑一个耗内存的脚本,内存不断增长
1 2 3 4 x="a" while [ True ];do x=$x$x done ;
top看内存占用稳步上升
1 2 3 4 PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 30215 root 20 0 871m 501m 1036 R 99.8 26.7 0:38.69 sh 30215 root 20 0 1639m 721m 1036 R 98.7 38.4 1:03.99 sh 30215 root 20 0 1639m 929m 1036 R 98.6 49.5 1:13.73 sh
下面用cgroups控制这个进程的内存资源
1 2 3 mkdir -p /cgroup/memory/fooecho 1048576 > /cgroup/memory/foo/memory.limit_in_bytes echo 30215 > /cgroup/memory/foo/tasks
发现之前的脚本被kill掉
因为这是强硬的限制内存,当进程试图占用的内存超过了cgroups的限制,会触发out of memory,导致进程被kill掉。
实际情况中对进程的内存使用会有一个预估,然后会给这个进程的限制超配50%比如,除非发生内存泄露等异常情况,才会因为cgroups的限制被kill掉。
也可以通过配置关掉cgroups oom kill进程,通过memory.oom_control来实现(oom_kill_disable 1),但是尽管进程不会被直接杀死,但进程也进入了休眠状态,无法继续执行,仍然无法服务。
关于内存的控制,还有以下配置文件,关于虚拟内存的控制,以及权值比重式的内存控制等
1 2 3 4 5 6 7 [root@localhost /] cgroup.event_control memory.force_empty memory.memsw.failcnt memory.memsw.usage_in_bytes memory.soft_limit_in_bytes memory.usage_in_bytes tasks cgroup.procs memory.limit_in_bytes memory.memsw.limit_in_bytes memory.move_charge_at_immigrate memory.stat memory.use_hierarchy memory.failcnt memory.max_usage_in_bytes memory.memsw.max_usage_in_bytes memory.oom_control memory.swappiness notify_on_release
测试三:限制进程的IO资源 测试验证了:
跑一个耗io的脚本
1 2 3 4 5 6 dd if =/dev/sda of=/dev/null 通过iotop看io占用情况,磁盘速度到了284M/s 30252 be/4 root 284.71 M/s 0.00 B/s 0.00 % 0.00 % dd if =/dev/sda of=/dev/null
下面用cgroups控制这个进程的io资源
1 2 3 4 5 mkdir -p /cgroup/blkio/fooecho '8:0 1048576' > /cgroup/blkio/foo/blkio.throttle.read_bps_deviceecho 30252 > /cgroup/blkio/foo/tasks
再通过iotop看,确实将读速度降到了1M/s
1 30252 be/4 root 993.36 K/s 0.00 B/s 0.00 % 0.00 % dd if =/dev/sda of=/dev/null
对于io还有很多其他可以控制层面和方式,如下
1 2 3 4 5 6 7 [root@localhost ~] blkio.io_merged blkio.io_serviced blkio.reset_stats blkio.throttle.io_serviced blkio.throttle.write_bps_device blkio.weight cgroup.procs blkio.io_queued blkio.io_service_time blkio.sectors blkio.throttle.read_bps_device blkio.throttle.write_iops_device blkio.weight_device notify_on_release blkio.io_service_bytes blkio.io_wait_time blkio.throttle.io_service_bytes blkio.throttle.read_iops_device blkio.time cgroup.event_control tasks
blkio 子系统里东西很多。不过大部分都是只读的状态报告,可写的参数就只有下面这几个:
1 2 3 4 5 6 7 8 9 blkio.throttle.read_bps_device blkio.throttle.read_iops_device blkio.throttle.write_bps_device blkio.throttle.write_iops_device blkio.weight blkio.weight_device 这些都是用来控制进程的磁盘 io 的。很明显地分成两类,其中带“throttle”的,顾名思义就是节流阀,将流量限制在某个值下。而“weight”就是分配 io 的权重。 再看看 blkio.weight 。blkio 的 throttle 和 weight 方式和 cpu 子系统的 quota 和 shares 有点像,都是一种是绝对限制,另一种是相对限制,并且在不繁忙的时候可以充分利用资源,权重值的范围在 10 – 1000 之间。
测试权重方式要麻烦一点。因为不是绝对限制,所以会受到文件系统缓存的影响。如在虚拟机中测试,要关闭虚机如我用的 VirtualBox 在宿主机上的缓存。如要测试读 io 的效果,先生成两个几个 G 的大文件 /tmp/file_1,/tmp/file_2 ,可以用 dd 搞。然后设置两个权重
测试前清空文件系统缓存,以免干扰测试结果
1 2 sync echo 3 >/proc/sys/vm/drop_caches
在这两个控制组中用 dd 产生 io 测试效果。
还是用 iotop 看看效果
1 2 3 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
两个进程每秒读的字节数虽然会不断变动,但是大致趋势还是维持在 1:5 左右,和设定的 weight 比例一致。blkio.weight_device 是分设备的。写入时,前面再加上设备号即可。
实践记录 1、假如已经配置好一个资源组,现在想让一个服务按这个组的资源分配来运行服务,而不需要去找到进程号再写入到tasks中
这个运行以后有会自动将top进程号写入到tasks当中去
2、查询一个组里面设置的资源的限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 设置的值会显示出来,没有设置的就会提示没有找到 [root@lab8105 ~] daemons/ftp: cgget: cannot find controller 'cpuset' in group 'daemons/ftp' cpu.rt_period_us: 1000000 cpu.rt_runtime_us: 0 cpu.stat: nr_periods 0 nr_throttled 0 throttled_time 0 cpu.cfs_period_us: 5000 cpu.cfs_quota_us: -1 cpu.shares: 1000 cgget: cannot find controller 'cpuacct' in group 'daemons/ftp' cgget: cannot find controller 'memory' in group 'daemons/ftp' cgget: cannot find controller 'devices' in group 'daemons/ftp' cgget: cannot find controller 'freezer' in group 'daemons/ftp' cgget: cannot find controller 'net_cls' in group 'daemons/ftp' cgget: cannot find controller 'blkio' in group 'daemons/ftp'
3、需要用两个限制条件对进程进行限制
4、默认情况下是一个大根,然后分了几个资源系统,还支持做一个子系统组,即单独组建一个资源组,然后对这个资源组里面进行配置,具体方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 mount { cpu = /cgroup/cpu_and_mem; memory = /cgroup/cpu_and_mem; } group daemons/ftp { cpu { cpu.shares = "1000" ; cpu.cfs_period_us = "5000" ; } memory { memory.swappiness = "20" ; } }
5、需要创建控制组群,如上的daemons/ftp,想通过命令行的方式创建
1 2 3 4 5 [root@lab8105 ~] 如上命令使用后会在/cgroup/cpu/中多了zp目录,并且里面是继承的上级的cpu里面的参数,这样就创建了一个zp的组群 删除组群的方式如下(删除cgroup时,其所有任务都移动到了父组群当中): [root@lab8105 ~]
6、设置里面的配置参数
1 2 3 4 5 6 7 8 需要设置 /cgroup/cpu/daemons/ftp/cpu.shares 执行 [root@lab8105 ~] daemons/ftp路径是相对于根的,如果想设置根的这个参数那么就执行 [root@lab8105 ~] 这里需要注意,只有某些参数是可以修改的,某些参数是不能修改的 也可以直接echo 的方式进行参数的设置
7,移动某个进程到控制组群当中(动态的进行资源的调配)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 移动指定进程到指定的控制组当中,创建两个资源组,使用上面的cpu的脚本,然后运行后,使用top进行监控 group half { cpu { cpu.cfs_period_us="100000" ; cpu.cfs_quota_us="50000" ; } memory { memory.swappiness = "50" ; } } group eighty { cpu { cpu.cfs_period_us = "100000" ; cpu.cfs_quota_us="50000" ; } memory { memory.swappiness = "80" ; } } [root@lab8105 ~] top监控看到cpu的占用为50% [root@lab8105 ~] top监控看到cpu的占用为80% 注意支持多进程,多资源组同时移动 [root@lab8105 ~] 备用方法就是直接echo
8、通过规则对指定的进程进行控制
我们还可以通过设置规则来让 cgred(cgroup 规则引擎后台程序)自动将进程分配给特定组。cgred 后台程序根据 /etc/cgrules.conf 文件中的设置将任务移到 cgroup 中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@lab8105 ~] [root@lab8105 ~] root:cpu.sh cpu half/ root cpu half/ 启动监控进程服务 [root@lab8105 ~]
效果如下,运行相同的命令,所占用的cpu的资源按指定的比例进行占用
1 2 3 4 5 6 7 8 9 10 [root@lab8105 ~] top - 16:00:40 up 1 day, 1:34, 5 users , load average: 1.57, 1.13, 0.90 Tasks: 216 total, 3 running, 213 sleeping, 0 stopped, 0 zombie Cpu(s): 6.9%us, 0.0%sy, 0.0%ni, 93.1%id , 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Mem: 49461228k total, 49346312k used, 114916k free, 47374260k buffers Swap: 2097148k total, 0k used, 2097148k free, 33116k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 14648 root 20 0 104m 1716 1012 R 99.7 0.0 4:58.38 cpu1.sh 14565 root 20 0 104m 1704 1012 R 10.0 0.0 4:30.53 cpu.sh
如上所述,指定用户,可以指定进程进行控制,也可以指定用户的所有进程进行控制,后台的做的操作就是把进行的号移动到了指定的资源组的task当中去了