bingoohuang/blog

使用CGroups限制硬件资源

Opened this issue · 2 comments

image
图来自1

CGroups提供了进程级别的资源(CPU、内存、网络和磁盘等)分配机制,也就是可以限制某一个或者某一组进程的资源使用。

为什么需要这么一种技术呢?

如果你了解过docker,那应该知道容器之间是相互安全隔离的,它的底层实现就是采用的CGroups技术。资源隔离是必要的,在同一台机器上运行着非常多的进程,如果这台机器资源是共享给多个用户在使用,你肯定不想因为某个用户的程序负载过大而影响到所有其它用户,这就需要资源安全隔离,避免发生级联错误。再一个也为了保持大家都公平的使用资源,而不会出现一方过多或另一方过少的情况。

CGroup介绍

CGroup是Control Groups 的缩写,是Linux内核提供的一种可以限制、记录、隔离进程组(process groups)所使用的物力资源(如cpu memory i/o 等等) 的机制。

CGroup是将任意进程进行分组化管理的 Linux 内核功能。CGroup本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O 或内存的分配控制等具体的资源管理功能是通过这个功能来实现的。这些具体的资源管理功能称为CGroup子系统或控制器。CGroup 子系统有控制内存的Memory控制器、控制进程调度的CPU控制器等。运行中的内核可以使用的Cgroup子系统由/proc/cgroup 来确认。

CGroup 提供了一个 CGroup 虚拟文件系统,作为进行分组管理和各子系统设置的用户接口。要使用 CGroup,必须挂载 CGroup 文件系统。这时通过挂载选项指定使用哪个子系统。

Cgroups提供的功能:

  1. 限制进程组可以使用的资源数量(Resource limiting )。比如:memory子系统可以为进程组设定一个memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会出发OOM(out of memory)。
  2. 进程组的优先级控制(Prioritization )。比如:可以使用cpu子系统为某个进程组分配特定cpu share。
  3. 记录进程组使用的资源数量(Accounting )。比如:可以使用cpuacct子系统记录某个进程组使用的cpu时间
  4. 进程组隔离(Isolation)。比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。
  5. 进程组控制(Control)。比如:使用freezer子系统可以将进程组挂起和恢复。

Cgroup相关概念介绍:

  1. 任务(task)。在cgroups中,任务就是系统的一个进程;
  2. 控制族群(control group)。控制族群就是一组按照某种标准划分的进程。Cgroups中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用cgroups以控制族群为单位分配的资源,同时受到cgroups以控制族群为单位设定的限制;
  3. 层级(hierarchy)。控制族群可以组织成hierarchical的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性;
  4. 子系统(subsystem)。一个子系统就是一个资源控制器,比如cpu子系统就是控制cpu时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。

相关概念的相互关系:

每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认cgroup(我们称之为root cgroup,此 cgroup 在创建层级时自动创建,后面在该层级中创建的cgrou 都是此cgroup的后代)的初始成员;

一个子系统最多只能附加到一个层级;

一个层级可以附加多个子系统;

一个任务可以是多个cgroup的成员,但是这些cgroup必须在不同的层级;
系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在cgroup的成员。然后可根据需要将该子任务移动到不同的cgroup中,但开始时它总是继承其父任务的cgroup。
image
由上图所示,CGroup层级关系显示,CPU和Memory两个子系统有自己独立的层级系统,而又通过Task Group取得关联关系。

cGroup特点

在cgroups中,任务就是系统的一个进程。

控制族群(control group)。控制族群就是一组按照某种标准划分的进程。Cgroups 中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用cgroups以控制族群为单位分配的资源,同时受到cgroups以控制族群为单位设定的限制。

层级(hierarchy)。控制族群可以组织成hierarchical的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性。

子系统(subsytem)。一个子系统就是一个资源控制器,比如cpu子系统就是控制cpu时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。

子系统介绍

  1. blkio--这个子系统为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB)。
  2. cpu--这个子系统使用调度程序提供对CPU的cgroup任务访问。
  3. cpuacct--这个子系统自动生成 cgroup 中任务所使用的CPU报告。
  4. cpuset--这个子系统为cgroup中的任务分配独立CPU(在多核系统)和内存节点。
  5. devices--这个子系统可允许或者拒绝cgroup中的任务访问设备。
  6. freezer--这个子系统挂起或者恢复cgroup中的任务。
  7. memory--这个子系统设定cgroup中任务使用的内存限制,并自动生成由那些任务使用的内存资源报告。
  8. net_cls--这个子系统使用等级识别符(classid)标记网络数据包,可允许Linux流量控制程序(tc)识别从具体cgroup中生成的数据包。

参数介绍

cfs_quota_us 和 cfs_period_us 参数单位是微秒,其中前者是指一个周期内总的可用运行时间,后者是指一个周期的长度,它们可以配合起来限制CPU的运行时间,下面列举几组例子让大家更容易理解。

  1. cpu.cfs_quota_us=250000、 cpu.cfs_period_us=250000 如果period为250ms且quota为250ms,那么该控制组每250ms将获得1个CPU运行时间。
  2. cpu.cfs_quota_us=1000000、 cpu.cfs_period_us=500000 如果period为500ms且quota为1000ms,那么该控制组每500ms将获得2个CPU运行时间。
  3. cpu.cfs_quota_us=10000、 cpu.cfs_period_us=50000 如果period为50ms且quota为10ms,那么该控制组每50ms将获得1个CPU的20%运行时间。

注意

  1. 可以通过 top 系统命令并查看 %CPU 和 %MEM 列确认目标进程资源是否真的被限制住了,这个百分比只表示单核,如果机器是多核就乘以核数。
  2. 如果某个控制组已经应用在一个进程上了,那再次使用该控制组将中断原先的进程,你可以通过创建多个控制组来解决这个问题。

实验

[root@localhost bingoohuang]# md5sum /dev/urandom &
[1] 20070
[root@localhost bingoohuang]# top -p 20070
top - 12:28:50 up  1:48,  2 users,  load average: 0.50, 0.15, 0.08
Tasks: 291 total,   2 running, 289 sleeping,   0 stopped,   0 zombie
%Cpu(s):  2.0 us,  4.5 sy,  0.0 ni, 93.5 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem : 65529944 total, 64081524 free,   870492 used,   577928 buff/cache
KiB Swap: 20971516 total, 20971516 free,        0 used. 64157204 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
20070 root      20   0  107964    656    532 R 100.0  0.0   0:35.30 md5sum
[root@localhost bingoohuang]# ./cgroup.sh 20070 1
[root@localhost bingoohuang]# top -n 20070
top - 12:37:01 up  1:56,  2 users,  load average: 0.00, 0.12, 0.12
Tasks: 291 total,   2 running, 289 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.5 us,  0.9 sy,  0.0 ni, 98.6 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem : 65529944 total, 64081096 free,   869704 used,   579144 buff/cache
KiB Swap: 20971516 total, 20971516 free,        0 used. 64157520 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
20070 root      20   0  107964    656    532 R  15.9  0.0   4:03.96 md5sum

cgroup.sh:

#!/bin/bash

# https://github.com/the7winds/aucont/blob/3eef6efedfbad428227338b8cd1beca293947f7e/src/cgroup.sh

pid=$1
perc=$2
cores=`nproc`
period=1000000
let 'quota=period*cores*perc/100'

cgdir=/sys/fs/cgroup/cpu/$pid

mkdir -p $cgdir
echo $pid > $cgdir/tasks
echo $period > $cgdir/cpu.cfs_period_us
echo $quota > $cgdir/cpu.cfs_quota_us

cpu_cg_setup.sh:

#!/bin/bash
# https://github.com/wtf42/au-containers/blob/226f46f4bd2f67703e8f38de74867d93f0a69705/tools/src/scripts/cpu_cg_setup.sh

CONT_PID=$1
QUOTA=$2
# Working Directory
WD=$3

CGMOUNT=$WD/.cg_cpu
CGDIR=$CGMOUNT/aucont
CONT_CGDIR=$CGDIR/$CONT_PID
CG_TYPE=$(lssubsys -a | grep -w "cpu" || echo "cpu")

mkdir -p $CGMOUNT
mountpoint -q $CGMOUNT || sudo mount -t cgroup -o $CG_TYPE aucont_cpu_cg $CGMOUNT

sudo mkdir -p $CGDIR
echo "CGDIR:$CGDIR"
sudo chown -R $(id -u):$(id -g) $CGDIR

mkdir -p $CONT_CGDIR
echo $CONT_PID > $CONT_CGDIR/tasks

# NPROC=$(nproc)
CG_PERIOD=1000000
# CG_QUOTA=$((CG_PERIOD * NPROC * QUOTA / 100))
CG_QUOTA=$((CG_PERIOD * QUOTA / 100))

echo $CG_PERIOD > $CONT_CGDIR/cpu.cfs_period_us
echo $CG_QUOTA > $CONT_CGDIR/cpu.cfs_quota_us

资源限制测试

对IO限制进行测试

消耗IO命令行:

  1. dd if=/dev/sda of=/dev/null 使用iotop工具进行查看。
  2. Simulating high CPU usage : sha1sum /dev/zero &​
  3. killall sha1sum​

对内存限制进行测试

消耗内存的mysql脚本文件:
x='a'
while [ True ];do
x=$x$x
done;
使用top工具进行查看。(跑消耗内存的脚本文件命令窗口直接关闭)

对CPU限制进行测试

运用mysqlslap性能测试工具对已安装的mysql进行压力测试
mysqlslap?--concurrency(并发数量)=150?--iterations(测试次数)=1?--number-int-cols(创建测试表的int型字段数量)=8?
--auto-generate-sql(用系统生成SQL脚本进行测试)?--auto-generate-sql-load-type(读写混合)=mixed?--engine=innodb?--number-of-queries=100000?
-ujesse?-pjesse?--number-char-cols=35?--auto-generate-sql-add-autoincrement?--debug-info?-P3306?-h127.0.0.1

对进程带宽限制进行测试

用iperf指令进行打流,运用cgroup和tc进行流控。

参考资源

  1. Limit memory usage for a single Linux process
    cgcreate -g memory:myGroup
    echo 500M > /sys/fs/cgroup/memory/myGroup/memory.limit_in_bytes
    echo 5G > /sys/fs/cgroup/memory/myGroup/memory.memsw.limit_in_bytes
    cgexec -g memory:myGroup pdftoppm
  2. 视频笔记:容器是什么?让我们用 Go 写一个吧! - Liz Rice
  3. stress, 生成指定cpu压力和内存占用a tool for maing cpu load and memory usage with specified ratio
  4. 在CentOS上使用CGroups隔离硬件资源
  5. Linux下cGroup调研.docx
  6. 资源限制测试.txt
  7. execc is a simple example of a container runtime. It simply runs a command in a container. It uses the cgcreate, cgexec, cgset, cgdelete, unshare, and chroot commands to initialize the container.
    Run a shell in a busybox container limited to 100 milli-cores and 1 megabyte of memory.
    $ mkdir rootfs
    $ docker export $(docker create busybox) | gzip -c > busybox.tar.gz
    $ sudo execc -c 100 -m 1000000 --rootfs busybox.tar.gz /bin/sh
    / # echo "Hello from inside a container!"
    Hello from inside a container!
    #
  8. github cfs_period_us search
  9. github cgexec search
  10. Linux的cgroup功能(二):资源限制cgroup v1和cgroup v2的详细介绍
  11. docker 容器基础技术:linux cgroup 简介
  12. cgexec - Execute commands in a Linux Control Group (cgroups v2)
  13. cgroup-v1
    As an example of a scenario (originally proposed by vatsa@in.ibm.com)​
    that can benefit from multiple hierarchies, consider a large​
    university server with various users - students, professors, system​
    tasks etc. The resource planning for this server could be along the​
    following lines:​
    ​
           CPU :          "Top cpuset"​
                           /       \​
                   CPUSet1         CPUSet2​
                      |               |​
                   (Professors)    (Students)​
    ​
                   In addition (system tasks) are attached to topcpuset (so​
                   that they can run anywhere) with a limit of 20%​
    ​
           Memory : Professors (50%), Students (30%), system (20%)​
    ​
           Disk : Professors (50%), Students (30%), system (20%)​
    ​
           Network : WWW browsing (20%), Network File System (60%), others (20%)​
                                   / \​
                   Professors (15%)  students (5%)​
    
    

​ ```

将某些进程限制为CPU% - Linux

我不记得了,并且不认为在unix调度程序中有这样的东西。 您需要一个控制其他进程的小程序,并执行以下操作:

loop
    wait for some time tR
    send SIGSTOP to the process you want to be scheduled
    wait for some time tP
    send SIGCONT to the process.
loopEnd

比率tR / tP控制cpu负载。

这是一个小概念证明。 “忙”是一个耗尽你的cpu时间的程序,你希望通过“slowDown”减慢你的速度:

> cat > busy.c:
    main() { while (1) {}; }

> cc -o busy busy.c
> busy &
> top

Tasks: 192 total,   3 running, 189 sleeping,   0 stopped,   0 zombie
Cpu(s): 76.9% us,  6.6% sy,  0.0% ni, 11.9% id,  4.5% wa,  0.0% hi,  0.0% si
Mem:   6139696k total,  6114488k used,    25208k free,   115760k buffers
Swap:  9765368k total,  1606096k used,  8159272k free,  2620712k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
26539 cg        25   0  2416  292  220 R 90.0  0.0   3:25.79 busy
...

> cat > slowDown
while true; do
 kill -s SIGSTOP $1
 sleep 0.1
 kill -s SIGCONT $1
 sleep 0.1
done

> chmod +x slowDown
> slowDown 26539 &
> top
Tasks: 200 total,   4 running, 192 sleeping,   4 stopped,   0 zombie
Cpu(s): 48.5% us, 19.4% sy,  0.0% ni, 20.2% id,  9.8% wa,  0.2% hi,  2.0% si
Mem:   6139696k total,  6115376k used,    24320k free,    96676k buffers
Swap:  9765368k total,  1606096k used,  8159272k free,  2639796k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
26539 cg        16   0  2416  292  220 T 49.7  0.0   6:00.98 busy
...

好吧,那个脚本需要更多的工作(例如,要关心INTR-up,并让受控过程继续,以防它在那一刻被停止),但你明白了。 我也会在C或类似的地方编写那个小脚本,并从命令行参数计算cpu比率....

用 docker 执行 while 循环,演示 CPU CGroup

运行一个 busybox 容器,并限制只允许使用 20% 的 CPU,while 循环可以模拟跑满 CPU,操作如下:

[root@centos ~]# docker run -d --cpu-period=100000 --cpu-quota=20000 --name busybox busybox:1.29.3 /bin/sh -c "while : ; do : ; done"
52f0ea4715b26f56bb27b46aedaaa326c24040afe520f840e18ace3f7bf99e19

查看宿主机 top

[root@centos ~]# top
top - 17:36:55 up  1:53,  1 user,  load average: 0.01, 0.06, 0.16
Tasks:  99 total,   2 running,  97 sleeping,   0 stopped,   0 zombie
%Cpu0  :  0.3 us,  0.3 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :  7.3 us, 13.0 sy,  0.0 ni, 79.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  3881016 total,  3492996 free,   146688 used,   241332 buff/cache
KiB Swap:  4063228 total,  4063228 free,        0 used.  3475308 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                            
 4517 root      20   0    1236      4      0 R  20.3  0.0   0:06.48 sh

查看 /sys/fs/cgroup

[root@centos ~]# cat /sys/fs/cgroup/cpu/system.slice/docker-52f0ea4715b26f56bb27b46aedaaa326c24040afe520f840e18ace3f7bf99e19.scope/cpu.cfs_period_us 
100000
[root@centos ~]# cat /sys/fs/cgroup/cpu/system.slice/docker-52f0ea4715b26f56bb27b46aedaaa326c24040afe520f840e18ace3f7bf99e19.scope/cpu.cfs_quota_us
20000

最后记得关闭 busybox 容器

[root@centos ~]# docker container rm -f busybox

作者:ConanLi
链接:https://www.jianshu.com/p/4199e63cf472