使用 CGroups 限制硬件资源
图来自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 提供的功能
- 限制进程组可以使用的资源数量(Resource limiting )。比如:memory 子系统可以为进程组设定一个 memory 使用上限,一旦进程组使用的内存达到限额再申请内存,就会出发 OOM(out of memory)。
- 进程组的优先级控制(Prioritization )。比如:可以使用 cpu 子系统为某个进程组分配特定 cpu share。
- 记录进程组使用的资源数量(Accounting )。比如:可以使用 cpuacct 子系统记录某个进程组使用的cpu时间
- 进程组隔离(Isolation)。比如:使用 ns 子系统可以使不同的进程组使用不同的 namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。
- 进程组控制(Control)。比如:使用 freezer 子系统可以将进程组挂起和恢复。
Cgroup 相关概念介绍
- 任务(task)。在 cgroups 中,任务就是系统的一个进程;
- 控制族群(control group)。控制族群就是一组按照某种标准划分的进程。Cgroups 中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用 cgroups 以控制族群为单位分配的资源,同时受到 cgroups 以控制族群为单位设定的限制;
- 层级(hierarchy)。控制族群可以组织成 hierarchical 的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性;
- 子系统(subsystem)。一个子系统就是一个资源控制器,比如 cpu 子系统就是控制 cpu 时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。
相关概念的相互关系
每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认 cgroup(我们称之为 root cgroup,此 cgroup 在创建层级时自动创建,后面在该层级中创建的cgrou 都是此 cgroup 的后代)的初始成员;
一个子系统最多只能附加到一个层级;
一个层级可以附加多个子系统;
一个任务可以是多个 cgroup 的成员,但是这些 cgroup 必须在不同的层级;
系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的成员。然后可根据需要将该子任务移动到不同的cgroup中,但开始时它总是继承其父任务的 cgroup。
由上图所示,CGroup 层级关系显示,CPU 和 Memory 两个子系统有自己独立的层级系统,而又通过 Task Group 取得关联关系。
cGroup 特点
在 cgroups 中,任务就是系统的一个进程。
控制族群(control group)。控制族群就是一组按照某种标准划分的进程。Cgroups 中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用 cgroups 以控制族群为单位分配的资源,同时受到 cgroups 以控制族群为单位设定的限制。
层级(hierarchy)。控制族群可以组织成 hierarchical 的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性。
子系统(subsytem)。一个子系统就是一个资源控制器,比如 cpu 子系统就是控制 cpu 时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。
子系统介绍
- blkio--这个子系统为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB)。
- cpu--这个子系统使用调度程序提供对 CPU 的 cgroup 任务访问。
- cpuacct--这个子系统自动生成 cgroup 中任务所使用的CPU报告。
- cpuset--这个子系统为 cgroup 中的任务分配独立 CPU(在多核系统)和内存节点。
- devices--这个子系统可允许或者拒绝 cgroup 中的任务访问设备。
- freezer--这个子系统挂起或者恢复 cgroup 中的任务。
- memory--这个子系统设定 cgroup 中任务使用的内存限制,并自动生成由那些任务使用的内存资源报告。
- net_cls--这个子系统使用等级识别符(classid)标记网络数据包,可允许 Linux 流量控制程序(tc)识别从具体 cgroup 中生成的数据包。
参数介绍
cfs_quota_us 和 cfs_period_us 参数单位是微秒,其中前者是指一个周期内总的可用运行时间,后者是指一个周期的长度,它们可以配合起来限制CPU的运行时间,下面列举几组例子让大家更容易理解。
- cpu.cfs_quota_us=250000、 cpu.cfs_period_us=250000 如果 period为 250ms 且 quota 为 250ms,那么该控制组每 250ms 将获得1个CPU运行时间。
- cpu.cfs_quota_us=1000000、 cpu.cfs_period_us=500000 如果 period为 500ms 且 quota 为 1000ms,那么该控制组每 500ms 将获得2个CPU运行时间。
- cpu.cfs_quota_us=10000、 cpu.cfs_period_us=50000 如果 period 为 50ms 且 quota 为 10ms,那么该控制组每 50ms 将获得1个CPU的20%运行时间。
注意
- 可以通过 top 系统命令并查看 %CPU 和 %MEM 列确认目标进程资源是否真的被限制住了,这个百分比只表示单核,如果机器是多核就乘以核数。
- 如果某个控制组已经应用在一个进程上了,那再次使用该控制组将中断原先的进程,你可以通过创建多个控制组来解决这个问题。
实验
[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 命令行:
- dd if=/dev/sda of=/dev/null 使用 iotop 工具进行查看。
- Simulating high CPU usage : sha1sum /dev/zero &
- 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 进行流控。
参考资源
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
视频笔记:容器是什么?让我们用 Go 写一个吧! - Liz Rice
stress, 生成指定cpu压力和内存占用a tool for maing cpu load and memory usage with specified ratio
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! #
Linux的cgroup功能(二):资源限制cgroup v1和cgroup v2的详细介绍
cgexec - Execute commands in a Linux Control Group (cgroups v2)
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%)
----------------------------------------------
我不记得了,并且不认为在 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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论