网站首页 > 技术教程 正文
在容器世界里,有两个非常重要的核心技术:Namespace和Cgroups。其中Namespace用于隔离,而Cgroups用于资源限制。今天我们通过理论到实战案例来了解怎么通过cgroups技术限制CPU的使用。
什么是cgroup?
Linux Cgroups 的全称是 Linux Control Group。它最主要的作用,就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。
在 Linux 中,Cgroups 给用户暴露出来的操作接口是文件系统,即它以文件和目录的方式组织在操作系统的/sys/fs/cgroup路径下。我们以CentOS 7为例,看一下cgroups有哪些,其他系统可能挂载的数量和参数会有部分不同,但不妨碍本文的阅读。
[root@centos7 ~]# mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
可以看到,在/sys/fs/cgroup下面有很多诸如cpuset、cpu、memory这样的子目录,也叫子系统。这些都是我这台机器当前可以被 Cgroups 进行限制的资源种类。而在子系统对应的资源种类下,你就可以看到该类资源具体可以被限制的方法。
Red Hat Enterprise Linux 7 中可用的资源控制器包括
- blkio —— 对输入 ∕ 输出访问存取块设备设定权限;
- cpu —— 使用 CPU 调度程序让 cgroup 的任务可以存取 CPU。它与 cpuacct 管控器一起挂载在同一 mount 上;(本文的案例)
- cpuacct —— 自动生成 cgroup 中任务占用 CPU 资源的报告。它与 cpu 管控器一起挂载在同一 mount 上;
- cpuset —— 给 cgroup 中的任务分配独立 CPU(在多芯系统中)和内存节点;
- devices —— 允许或禁止 cgroup 中的任务存取设备;
- freezer —— 暂停或恢复 cgroup 中的任务;
- memory —— 对 cgroup 中的任务可用内存做出限制,并且自动生成任务占用内存资源报告;
- net_cls —— 使用等级识别符(classid)标记网络数据包,这让 Linux 流量控制器(tc 指令)可以识别来自特定 cgroup 任务的数据包;
- perf_event —— 允许使用 perf 工具来监控 cgroup;
- hugetlb —— 允许使用大篇幅的虚拟内存页,并且给这些内存页强制设定可用资源量。
比如,对于今天介绍的 CPU 子系统来说,我们就可以看到如下几个参数文件:
[root@centos7 ~]# ls /sys/fs/cgroup/cpu/
cgroup.clone_children cgroup.sane_behavior cpuacct.usage cpu.cfs_period_us cpu.rt_period_us cpu.shares notify_on_release tasks
cgroup.procs cpuacct.stat cpuacct.usage_percpu cpu.cfs_quota_us cpu.rt_runtime_us cpu.stat release_agent
CPU cgroup的调度算法有哪些?
cpu 子系统可以调度 cgroup 对 CPU 的资源使用量。可用以下两个调度程序来管理对 CPU 资源的获取:
- 完全公平调度算法CFS(Completely Fair Scheduler) — 一个比例分配调度程序,可根据任务优先级 ∕ 权重或 cgroup 分得的份额,在任务群组(cgroups)间按比例分配 CPU 时间(CPU 带宽)。
- 实时调度算法RT(Real-Time) — 一个任务调度程序,可对实时任务使用 CPU 的时间进行限定。
我们在日常使用的程序大部分都不是实时调度的进程,因此本文着重介绍CFS调度算法,关于CFS,我们先来学习一下CFS相关的配置和参数。
- cpu.cfs_period_us它是 CFS 算法的一个调度周期,单位为微秒,默认值是 100000,换算成以 microseconds 为单位,也就 100ms,我们通常不会直接调整这个周期值,而是调整下面将要介绍的这个参数。
- cpu.cfs_quota_us它表示 CFS 算法中,在一个调度周期里这个控制组被允许的运行时间,单位为微秒,比如这个值为 50000 时,就是 50ms。如果将这个值设置为-1,这表示 cgroup 不需要遵循任何 CPU 时间限制,这也是cgroup的默认值。如果用这个值去除以调度周期(也就是cpu.cfs_period_us),50ms/100ms = 0.5,这样这个控制组被允许使用的 CPU 最大配额就是 0.5 个 CPU。从这里能够看出,cpu.cfs_quota_us 是一个绝对值。如果这个值是 200000,也就是 200ms,那么它除以 period,也就是 200ms/100ms=2。你看,结果超过了 1 个 CPU,这就意味着这时控制组需要 2 个 CPU 的资源配额。
另外今天的实战还会用到一个cgroup的参数cgroup.procs,这个参数是表示受到cgroup限制的进程号PID,默认值为空,也就是默认没有任何进程受到这个cgroup组的限制。
好了,我们现在已经有一点概念了,那么现在开始用一个实战来加深理解吧。
实战案例
先决条件
- Linux(CentOS/Ubuntu均可)
- Docker(可选)
首先我们在CPU的cgroup目录下(/sys/fs/cgroup/cpu/)新建一个用于测试的目录,命名为demo,这个目录就称为一个“控制组”。你会发现,操作系统会在你新创建的 demo目录下,自动生成该子系统对应的资源限制文件。
[root@centos7 ~]# cd /sys/fs/cgroup/cpu
[root@centos7 cpu]# mkdir demo
[root@centos7 cpu]# ls demo/
cgroup.clone_children cpuacct.usage cpu.cfs_quota_us cpu.shares tasks
cgroup.procs cpuacct.usage_percpu cpu.rt_period_us cpu.stat
cpuacct.stat cpu.cfs_period_us cpu.rt_runtime_us notify_on_release
我们先来看一下系统帮我们新建的cpu.cfs_period_us,cpu.cfs_quota_us和cgroup.procs 3个文件的默认值:
[root@centos7 cpu]# cd demo/
[root@centos7 demo]# cat cpu.cfs_period_us cpu.cfs_quota_us cgroup.procs
100000
-1
我们明明cat了三个文件,为什么只有两个值呢?正如刚才说的,cgroup.procs的默认值为空,因此我们得到的输出只有cpu.cfs_period_us,cpu.cfs_quota_us这两个文件的默认值。
在/sys/fs/cgroup目录中,默认是不允许新建文件的,因此我们需要在其他目录下新建一个用于测试cgroup限制的程序demo.sh,这里以/tmp目录为例。
[root@centos7 demo]# cat > /tmp/demo.sh << EOF
> #!/bin/sh
> while :;
> do :;
> done
> EOF
[root@centos7 demo]# chmod +x /tmp/demo.sh
显然,这个脚本的意思是执行一个死循环,可以把计算机当前的CPU 吃到 100%,现在我们运行一下程序,并且使用top命令观察一下CPU使用率。根据它的输出,我们可以看到这个脚本在主机后台运行的进程号(PID)是 11990。
[root@centos7 demo]# /tmp/demo.sh &
[1] 11990
[root@centos7 demo]# top -p 11990
...
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
11990 root 20 0 113192 2596 2444 R 99.9 0.1 0:15.39 demo.sh
接下来,我们通过修改cpu.cfs_quota_us文件限制这个程序的CPU使用率。
例如,我们要限制此进程只能使用50%(0.5个)CPU,那么根据CFS算法,在cpu.cfs_period_us的单位时间100ms里,cpu.cfs_quota_us的值应该设置为50ms,计算过程是 50ms/100ms=0.5。
[root@centos7 demo]# echo 50000 > /sys/fs/cgroup/cpu/demo/cpu.cfs_quota_us
仅仅只有cpu.cfs_period_us,cpu.cfs_quota_us这两个文件,系统是不知道对哪个进程进行限制的,因此别忘了,我们还有一个参数还没用到,那就是cgroup.procs,现在我们需要把进程号PID写入cgroup.procs文件里。
[root@centos7 demo]# echo 11990 > /sys/fs/cgroup/cpu/demo/cgroup.procs
现在我们再使用top命令看看刚才进程的CPU使用率
[root@centos7 demo]# top -p 11990
...
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
11990 root 20 0 113192 2596 2444 R 50.3 0.1 24:25.85 demo.sh
可以看到,cgroup系统已经对我们的进程做了CPU使用率的限制。大家可以通过调整cpu.cfs_period_us,cpu.cfs_quota_us不同的值来测试不同的使用率限制。
测试完之后,记得把测试的程序停掉,避免持续消耗CPU资源,再把刚才用于测试的demo目录删掉。
[root@centos7 demo]# kill -15 11990
[1]+ Terminated /tmp/demo.sh
[root@centos7 demo]# rmdir /sys/fs/cgroup/cpu/demo/
加餐:
既然我们已经对CPU cgroup和CFS有点了解了,那么docker又是怎么限制容器的CPU使用率的呢?
实际上docker做了一个很巧妙的设计,即在每一个cgroup的子系统下为每个容器新建一个资源控制组(也就是一个目录,相当于我们刚才的demo目录),控制组的名称以容器名来命名,然后在容器启动的时候,把主机对应的进程PID写到相应的cgroup.procs里面就可以了。
至于控制组的资源限制数值是从哪里来呢?我们可以从docker run命令里面看到有两个参数--cpu-period和--cpu-quota,也就是说,资源控制数值是通过用户在启动容器的时候指定的。
现在我们启动一个容器来验证一下,与前面的案例一样,我们希望把CPU使用率限制在50%,即--cpu-period=100000和--cpu-quota=50000:
[root@centos7 demo]# docker run -it --cpu-period=100000 --cpu-quota=50000 busybox /bin/sh
/ #
现在我们同样写入一个用于测试的sh脚本:
/ # cat > /tmp/demo.sh << EOF
> #!/bin/sh
> while :;
> do :;
> done
> EOF
/ # chmod +x /tmp/demo.sh
/ #
接着在后台执行这个sh脚本,并且通过top命令观察容器里面的CPU使用率,可以看到,demo.sh所在的进程已经被限制只能使用50%的CPU了:
/ # /tmp/demo.sh &
/ # top
...
PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
12 1 root R 1304 0.0 0 50.0 {demo.sh} /bin/sh /tmp/demo.sh
1 0 root S 1324 0.0 0 0.0 /bin/sh
13 1 root R 1312 0.0 0 0.0 top
那么运行docker的主机是怎样做到这个限制的呢?
首先我们先找到容器的container_id,接着根据container_id进入对应的CPU cgroup的目录(基于docker的容器会统一放置在/sys/fs/cgroup/cpu/docker/$container_id目录下)
[root@centos7 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
720772929dca busybox "/bin/sh" 31 minutes ago Up 31 minutes unruffled_hofstadter
[root@centos7 ~]# cd /sys/fs/cgroup/cpu/docker/720772929dcaeff7d5ff90bdb37c87ac63fae782162878dbf26ecee5bfe4ddc6/
[root@centos7 720772929dcaeff7d5ff90bdb37c87ac63fae782162878dbf26ecee5bfe4ddc6]# ls .
cgroup.clone_children cpuacct.usage cpu.cfs_quota_us cpu.shares tasks
cgroup.procs cpuacct.usage_percpu cpu.rt_period_us cpu.stat
cpuacct.stat cpu.cfs_period_us cpu.rt_runtime_us notify_on_release
我们先来看看用于资源控制的两个文件cpu.cfs_period_us和cpu.cfs_quota_us的数值,确认了这个容器的CPU资源限制跟我们期待的值是一致的:
[root@centos7 720772929dcaeff7d5ff90bdb37c87ac63fae782162878dbf26ecee5bfe4ddc6]# cat cpu.cfs_period_us cpu.cfs_quota_us
100000
50000
接着再看看cgroup.procs文件,里面有两个PID,分别是12683和12717。
[root@centos7 720772929dcaeff7d5ff90bdb37c87ac63fae782162878dbf26ecee5bfe4ddc6]# cat cgroup.procs
12683
12717
先从PID=12683的进程来分析,在主机执行以下命令看看PID=12683实际运行了什么命令:
[root@centos7 720772929dcaeff7d5ff90bdb37c87ac63fae782162878dbf26ecee5bfe4ddc6]# ps -ef|grep 12683|grep -v grep
root 12683 12663 0 14:51 pts/0 00:00:00 /bin/sh
root 12717 12683 49 14:55 pts/0 00:21:43 /bin/sh /tmp/demo.sh
通过屏幕输出可以看到PID=12683的进程就是容器启动时的命令(/bin/sh),而PID=12717是正在运行的程序demo.sh的PID,它的父进程是PID=12683。
其实啊,docker是通过把容器的1号进程(在演示的容器里面,PID=1的进程是/bin/sh)映射到主机的12683号进程,然后把主机的进程PID=12683以及这个PID下的其他后续创建的所有进程都写入cgroup.procs里面,这样就完成对一个容器的CPU资源限制,所以cgroup.procs里面可以有不止一个PID,并且这些PID均受到同一个CPU cgroup的资源限制,是不是很巧妙?
总结
通过这篇文章,首先给大家介绍了Linux Cgroup的概念以及cgroup的子系统有哪些,紧接着介绍了CPU cgroup的CFS调度算法和实战案例,最后通过加餐学习了docker是怎么通过cgroup限制不同容器的CPU使用率限制。希望大家在通读这篇文章后对Cgroup的机制有所了解并且在实际使用中可以举一反三。
参考资料
[1] Red Hat Enterprise Linux 7 资源管理指南
https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/7/html/resource_management_guide/index
猜你喜欢
- 2024-10-11 Java面试题总结 java面试题整理
- 2024-10-11 全志V3S开发板驱动示例(linux demo驱动开发)
- 2024-10-11 使用Visual Studio Code编写调试C语言
- 2024-10-11 C++编程知识:教你手写C++内存池 c++内存池实现
- 2024-10-11 大华报警主机的常开、常闭接线方式与协议配置
- 2024-10-11 60秒一口Python:147个demo,助你从零基础步步进阶编程高手
- 2024-10-11 八周年了!小岛秀夫发推纪念经典恐怖佳作《P.T.》
- 2024-10-11 互动电影三部曲登陆Steam,《底特律变人》Demo免费试玩中
- 2024-10-11 亲爱的:KK战队如果是一个男团,你觉得谁是C位,网友们都说是他
- 2024-10-11 Istio 在阿里云容器服务的部署及流量治理实践
你 发表评论:
欢迎- 05-14喜报!双色球5注824万头奖花落辽宁等地,开奖情况一览
- 05-14双色球新一期前瞻:红球蓝球走势深度剖析,精选号码提前看
- 05-1449倍、33倍、30倍、15倍!双色球第25053期开奖:多张倍投票集结
- 05-14双色球25054期:红球:04、05、15、18、29、33 蓝球:05、08
- 05-14厉害了!495倍独蓝票、万元独蓝票双双报喜!双色球第25053期开奖
- 05-14双色球25054期!龙头02凤尾31,独蓝14稳中,连号20-21围剿奖池!
- 05-14双色球25054期参考:蓝球侧重选2路蓝,红球依然三金胆、重号先
- 05-14双色球25054期:独蓝04,头01尾30,连号15-16,6+1精选
- 最近发表
-
- 喜报!双色球5注824万头奖花落辽宁等地,开奖情况一览
- 双色球新一期前瞻:红球蓝球走势深度剖析,精选号码提前看
- 49倍、33倍、30倍、15倍!双色球第25053期开奖:多张倍投票集结
- 双色球25054期:红球:04、05、15、18、29、33 蓝球:05、08
- 厉害了!495倍独蓝票、万元独蓝票双双报喜!双色球第25053期开奖
- 双色球25054期!龙头02凤尾31,独蓝14稳中,连号20-21围剿奖池!
- 双色球25054期参考:蓝球侧重选2路蓝,红球依然三金胆、重号先
- 双色球25054期:独蓝04,头01尾30,连号15-16,6+1精选
- 一号之差!井喷1416注,5注一等奖,100注二等,双色球25053开奖
- 双色球25054期:1、5尾,头单,尾双,斜连三码,胆11、12、27
- 标签列表
-
- sd分区 (65)
- raid5数据恢复 (81)
- 地址转换 (73)
- 手机存储卡根目录 (55)
- tcp端口 (74)
- project server (59)
- 双击ctrl (55)
- 鼠标 单击变双击 (67)
- debugview (59)
- 字符动画 (65)
- flushdns (57)
- ps复制快捷键 (57)
- 清除系统垃圾代码 (58)
- web服务器的架设 (67)
- 16进制转换 (69)
- xclient (55)
- ps源文件 (67)
- filezilla server (59)
- 句柄无效 (56)
- word页眉页脚设置 (59)
- ansys实例 (56)
- 6 1 3固件 (59)
- sqlserver2000挂起 (59)
- vm虚拟主机 (55)
- config (61)
本文暂时没有评论,来添加一个吧(●'◡'●)