精准限制CPU:Cgroups

网友投稿 2557 2022-08-26

精准限制CPU:Cgroups

精准限制CPU:Cgroups

如何看待CPU资源?

由于进程和线程在Linux的CPU调度看来没啥区别,所以本文后续都会用进程这个名词来代表内核的调度对象,一般来讲也包括线程

如果要分配资源,我们必须先搞清楚这个资源是如何存在的,或者说是如何组织的。我想CPU大家都不陌生,我们都在系统中用过各种工具查看过CPU的使用率,比如说以下这个命令和它的输出:

mpstat -P ALL 1 1

根据显示内容我们知道,这个计算机有4个cpu核心,目前的cpu利用率几乎是0,就是说系统整体比较闲。

从这个例子大概可以看出,我们对cpu资源的评估一般有两个观察角度:核心个数 和百分比。

目前的计算机基本都是多核甚至多cpu系统,一个服务器上存在几个到几十个cpu核心的情况都很常见。所以,从这个角度看,cgroup应该提供一种手段,可以给进程们指定它们可以占用的cpu核心,以此来做到cpu计算资源的隔离。百分比这个概念我们需要多解释一下:这个百分比究竟是怎么来的呢?难道每个cpu核心的计算能力就像一个带刻度表的水杯一样?一个进程要占用就会占用到它的一定刻度么?

当然不是!这个cpu的百分比是按时间比率计算的。基本思路是:一个CPU一般就只有两种状态,要么被占用,要么不被占用。当有多个进程要占用cpu的时候,那么操作系统在一个cpu核心上是进行分时处理的。比如说,我们把一秒钟分成1000份,那么每一份就是1毫秒,假设现在有5个进程都要用cpu,那么我们就让它们5个轮着使用,比如一人一毫秒,那么1秒过后,每个进程只占用了这个CPU的200ms,使用率为20%。整体cpu使用比率为100%。同理,如果只有一个进程占用,而且它只用了300ms,那么在这一秒的尺度看来,cpu的占用时间是30%。于是显示出来的状态就是占用30%的CPU时间。

这就是内核是如何看待和分配计算资源的。当然实际情况要比这复杂的多,但是基本思路就是这样。Linux内核是通过CPU调度器CFS--完全公平调度器对CPU的时间进行调度的,由于本文的侧重点是cgroup而不是CFS,对这个题目感兴趣的同学可以到这里进一步学习。CFS是内核可以实现真对CPU资源隔离的核心手段,因此,理解清楚CFS对理解清楚CPU资源隔离会有很大的帮助。

如何隔离CPU资源?

根据CPU资源的组织形式,我们就可以理解cgroup是如何对CPU资源进行隔离的了。

无非也是两个思路,一个是分配核心进行隔离,另一个是分配CPU使用时间进行隔离。

Cgroups介绍

Cgroups是linux的重要组件之一,可以对进程或用户进行隔离和限制

Cgroups全称Control Groups,是Linux内核提供的物理资源隔离机制,通过这种机制,可以实现对Linux进程或者进程组的资源限制、隔离和统计功能。比如可以通过cgroup限制特定进程的资源使用,比如使用特定数目的cpu核数和特定大小的内存,如果资源超限的情况下,会被暂停或者杀掉。

cgroups核心概念

任务(task)在cgroup中,任务就是一个进程。控制组(control group)cgroup的资源控制是以控制组的方式实现,控制组指明了资源的配额限制。进程可以加入到某个控制组,也可以迁移到另一个控制组。层级(hierarchy)控制组有层级关系,类似树的结构,子节点的控制组继承父控制组的属性(资源配额、限制等)。子系统(subsystem)一个子系统其实就是一种资源的控制器,比如memory子系统可以控制进程内存的使用。子系统需要加入到某个层级,然后该层级的所有控制组,均受到这个子系统的控制

cgroups进行CPU限制

cgroup暴露给用户的API为文件系统,所有对cgroup的操作均可以通过对文件的修改完成,cgroup API对应的路径为:/sys/fs/cgroup/,作为使用方,仅需要对文件系统中的内容进行编辑,即可达到配置对应的cgroup的要求。

创建cgroup

cd /sys/fs/cgroup/cpu mkdir test

其中,需要关注的文件有3个,分别为:

cgroup的限制逻辑如下:

1 限制所有pid在tasks中的进程,2 在 cpu.cfs_period_us 周期内,只能使用最多 cpu.cfs_quota_us 的cpu资源。3 默认情况下,cpu.cfs_period_us的单位为微秒,默认值为100ms。cpu.cfs_quota_us的值为-1,暨不做限制。4 例如: 限制在100ms中,只能使用30ms的cpu资源,暨限制cpu占用率为30%echo 30000 > cpu.cfs_quota_us5 启动测试程序,并添加pid到tasks文件中后,再观察CPU情况,可以清晰的看到被限制在了30%echo pid(loglistener的进程号) > /sys/fs/cgroup/cpu/rocket/test

使用cgroups的go客户端

这是一个使用Golang封装的用来操作cgroups的工具包,支持创建、管理、检查和销毁cgroups。使用go提供的客户端,可以在服务器上提供一个守护进程,由守护进程接收请求后,进行cgroup管理。相关的核心代码如下:

package main import ( "flag" "github.com/containerd/cgroups" "github.com/opencontainers/runtime-spec/specs-go" "log" "os/exec" "strings" "time" ) var kb = 1024 var mb = 1024 * kb // main // call some process and add this process into cgroup func main() { var cgPath = flag.String("cgroup_path", "", "cg-path is cgroup path name") var period = flag.Uint64("cpu_period", 100000, "cpu limit value, default is 100% ") var quota = flag.Int64("cpu_quota", -1, "cpu limit value, default is 100% ") var memLimit = flag.Int("mem_limit", 100, "mem limit value, default is 100mb ") var cmd = flag.String("cmd", "", "your application cmd") var args = flag.String("args", "", "cmd args") flag.Parse() cpuLimit := float32(*quota) / float32(*period) * 100 limit := int64(*memLimit * mb) log.Printf("cgroup_path: %s, cpu_quota: %v, cpu_period: %v,max (%v%%), mem_limit: %vm (%d), cmd: %s, args: %v \n", *cgPath, *quota, *period, cpuLimit, *memLimit, limit, *cmd, *args) control, err := cgroups.New(cgroups.V1, cgroups.StaticPath(*cgPath), &specs.LinuxResources{ CPU: &specs.LinuxCPU{ Quota: quota, Period: period, }, Memory: &specs.LinuxMemory{ Limit: &limit, }, }) if err != nil { log.Fatal(err) return } defer control.Delete() pid := run(*cmd, strings.Split(*args, " ")...) log.Printf("run process done, pid: %v, add to cgroup task\n", pid) if err = control.AddTask(cgroups.Process{Pid: pid}); err != nil { log.Fatal(err) return } tasks, err := control.Tasks(cgroups.Freezer, false) if err != nil { log.Fatal(err) } log.Printf("Current tasks: %v", tasks) time.Sleep(10 * time.Second) } // run cmd in background, and return pid func run(cmd string, args ...string) int { log.Printf("[run], cmd:%s, args: %v", cmd, args) command := exec.Command(cmd, args...) err := command.Start() if err != nil { log.Fatalf("Start error, %v", err) return 0 } for { if command.Process != nil { return command.Process.Pid } time.Sleep(1000 * time.Microsecond) } }

go run main.go -cgroup_path test -cmd /root/pi/main -cpu_quota 300000 -cpu_period 1000000 2022/08/08 12:21:23 cgroup_path: test, cpu_quota: 300000, cpu_period: 1000000,max (30.000002%), mem_limit: 100m (104857600), cmd: /root/pi/main, args: 2022/08/08 12:21:23 [run], cmd:/root/pi/main, args: [] 2022/08/08 12:21:23 run process done, pid: 11855, add to cgroup task 2022/08/08 12:21:23 Current tasks: [{freezer 11855 /sys/fs/cgroup/freezer/test/}]

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:「BPM架构」Zeebe 的常见问题和答案
下一篇:「时序数据库」使用cassandra进行时间序列数据扫描
相关文章

 发表评论

暂时没有评论,来抢沙发吧~