02 cpu cgroup使用

一 CPU CGROUP介绍

cpu cgroup是什么?为何要用?

之前我们提过Cgroup是用于限制进程对资源使用的一种机制,而Cpu Cgroup是Cgroup机制的一种,即cpu cgroup是cgroups的一个子系统,具体用来限制进程对cpu资源的使用的

k8s里pod的request与limit底层其实就是在配置cpu cgroup

二 储备知识cpu usage

既然cpu cgroup是限制进程对cpu的使用,那么在研究具体如何配置相关限制之前,我们必须搞清楚进程对cpu的使用都有哪些

因为cpu usage有一些对cpu的使用时不会计入进程对cpu占用的,

如下:CPU Usage

# 请仔细阅读:https://egonlin.com/?p=210
第2.2小节

# 可以通过top命令查看到如下关于cpu usage
us User,用户态进程占用cpu时间的百分比,不包括低优先级进程的用户态时间(nice值1-19)
sys System,内核态进程占用cpu时间的百分比
ni Nice,nice值1-19的进程用户态占cpu时间的百分比,注意ni与us一样,都是在用户态
id Idle,系统空闲cpu的百分比
wa Iowait,系统等待I/O的cpu时间占比,该时间不计入进程的CPU时间
hi Hardware irq,处理硬件中断所占用CPU的时间,该时间同样不计入进程的CPU时间
si Softtirq,处理软件中断的时间,该时间不计入进程的CPU时间
st Steal,表示同一宿主机上的其他虚拟机抢走的CPU时间

---------------------------------------------------
强调:
1、无论是hi还是si,占用的cpu时间,都不会计入进程的cpu时间,
因为本来中断程序就是单独的程序,它们在处理时本就不属于任何一个进程,即不属于用户态也不属于内核态,因此cgroup不会限制它们
2、不可中断睡眠D代表的是,进程正在进行的io,如果你把取快递这件事比喻一个进程,你是cpu,快递员是磁盘,你问快递员把快递给我吧,快递员说我有点忙,你等我去车上取过来给你,你别跑啊,你千万别跑啊,我这就给你去取,我真的是很忙,你要说跑了,我取过来你没有收到,那么我可就不管你了啊,于是你就进入了一个等待的状态,此时无论什么事都不能影响你,更不可能直接被kill干掉,因为一旦发生这件事,快递员取来东西你可就收不到了,这种等待就是不可中断睡眠

3、可终端店额睡眠S代表的是,进程就是在原地等待,还是取快递的例子,你是cpu,快递员此时变成了网卡或者是终端的输入操作,你的程序运行到一行代码,等待用户输入才能继续运行,至于用户什么时候输入,不知道,那进程就进入了S状态,你是可以被kill掉的。
---------------------------------------------------

了解:
硬中断hi与软中断si(中断是一种设备数据处理机制,linux通过中断打断一个进程的运行让其处理设备数据,就好比你正在吃饭,送快递的打了一个电话过来让你取快递去,这个电话就是一个中断信号,快递员就好比是网卡这种谁硬件设备
只有硬中断可能会因为需要处理数据的时间过程而明显耽误当前进程的执行所以有了软中断
软中断是配合硬中断运行的,
你正在吃饭,硬中断就好比是有人拿大铁棒朝着你脑门就是一棒子,你必须要愣一下,然后你先不着急报仇,先在本子上记下来某年某月某日有人拿棒子打了我以后是要报仇的,然后你可以继续吃饭,过了一会你爸爸也就是操作系统看了一眼你的记仇本,然后对你吼了一嗓子说吃吃吃,吃屁啊吃,别他妈的吃了赶紧报仇去,这个就是软中断,于是你停止吃饭,开始复仇)
cpu用户态进程使用比例包括us与ni
cpu内核态进程使用比例只有sys,不包括hi,si,wait这些都不计入进程信息

总结:进程对cpu的使用包括两部分

  • linux cpu 的使用总共分为两类:一类是用户态,包含us与ni,另外一类是内核态也就是sy
  • 至于wa、hi、si,这些I/O或者中断相关的cpu使用,cpu cgroup不会去做限制

三 CPU Cgroup的使用

每个Cgroups的子系统都是通过一个虚拟文件系统挂载点的方式,挂到一个缺省的目录下。在linux发行版里,cpu cgroup一般是挂载到/sys/fs/cgroup/cpu目录下

在该目录下,每个控制组(Control Group)都是一个子目录,各个控制组之间的关系是一个树状的层级关系(hiearchy)

例如,我们在子系统的最顶层目录开始创建两个控制组,其实就是创建两个目录group1与group2,然后再在group2下面创建两个控制组group3与group4,如此,我们便建立了一个树状的控制组层级,如下图所示

创建控制组后,会在目录下自动生成一系列文件

[root@test04 cgroup]# df -h |grep sys
tmpfs           981M     0  981M    0% /sys/fs/cgroup
[root@test04 cgroup]# cd /sys/fs/cgroup/
[root@test04 cgroup]# 
[root@test04 cgroup]# 
[root@test04 cgroup]# cd cpu
[root@test04 cpu]# 
[root@test04 cpu]# mkdir group1
[root@test04 cpu]# mkdir group2
[root@test04 cpu]# cd group2
[root@test04 group2]# mkdir group3 group4
[root@test04 group2]# 
[root@test04 group2]# 
[root@test04 group2]# 
[root@test04 group2]# ls cpu.*
cpu.cfs_period_us  cpu.cfs_quota_us  cpu.rt_period_us  cpu.rt_runtime_us  cpu.shares  cpu.stat

删除控制组

rm -rf 控制组目录,会报错不允许删除

# 解决方法
借助libcgroup工具删除目录

# 安装LIBCGROUP工具:使用 libcgroup 工具前,请先安装 libcgroup 和 libcgroup-tools 数据包
# REDHAT系统安装
yum install libcgroup libcgroup-tools -y

# UBUNTU系统安装:
apt-get install cgroup-bin

# 验证是否安装成功
cgdelete -h

cgdelete cpu:/group1
cgdelete cpu:/group2

在云平台里,大部分程序都不是实时调度的进程,而是普通调度(SCHED_HORMAL)类型进程,对于普通调度用到的算法,在linux系统中目前是CFS(Completely Fair Scheduler,即完全公平调度器)。在CPU Cgroup中和CFS相关的参数,共有三个

  • cpu.cfs_quota_us:它是CFS算法的一个调度周期,以microseconds为单位,如果值为100000microseconds,则代表100ms

  • cpu.cfs_period_us:它表示CFS算法中,在一个调度周期里,此控制组被允许的运行时间,比如该值为50000时,就是50ms

    ​ cpu.cfs_period_us / cpu.cfs_quota_us就是此控制组被允许使用cpu的最大配额,例如50ms / 100ms = 0.5 ,

    ​ 代表该控制组被允许使用的cpu最大配置为0.5个cpu

  • cpu.shares:该值用于控制在一个控制组目录树下,同一级控制组关于cpu的分配比例,例如上例中的group3与group4,如果group3下该值为1024,group4下该值为4096,则group3:group4比值为1:4,代表在一个5颗cpu的机器上,当group3与group4都需要5个cpu时,它们实际分配的cpu是:group3是1个,group4时候4个

[root@test04 cpu]# cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us 
-1
[root@test04 cpu]# cat /sys/fs/cgroup/cpu/cpu.cfs_period_us 
100000  
[root@test04 cpu]# cat /sys/fs/cgroup/cpu/cpu.shares 
1024
[root@test04 cpu]# 

k8s会为每个容器创建一个控制组,然后把容器进程pid写入控制组
yaml里的limit控制的就是cpu.cfs_quota_us/cpu.cfs_period_us的比值,得到是资源使用上限
而request控制的是cpu.cfs_shares,当物理机cpu打满时,cpu.cfs_shares与同一控制组目录树下的其他控制组的cpu.cfs_sharez的比值决定了谁可以抢到对应的比例的资源,没错cpu.cfs_shares只在cpu被打满的情况下才有效

先来开发一个能够占用cpu的程序,输入cpu个数,运行程序便占用n个cpu,此处我们用python编写,为了规避GIL对我们的影响,我们采用ctypes调用c语言库的方式,详见:https://egonlin.com/?p=7204

(1)编写一个libdead_loop.c

void DeadLoop() {
     while (1) {
         ; 
     }
} 

(2)编译为.so库,把so文件放到/opt目录下吧,这样方面后面查找

gcc libdead_loop.c -fPIC -shared -o /opt/libdead_loop.so

* -shared 为链接库  让编译器知道是要编译一个共享库
* -fPIC(Position Independent Code)   编译生成代码与位置无关
* 如果想能够调试可加上-g -Wall等参数

(3)编写py脚本test.py

#coding:utf-8
from ctypes import cdll
from threading import Thread
import sys

if __name__ == "__main__":
    if not (len(sys.argv) == 2 and sys.argv[1].isdigit()):
        print("Usage: python xxx.py cpu个数")
        exit()

    num = int(sys.argv[1])
    lib = cdll.LoadLibrary("/opt/libdead_loop.so")
    for i in range(num):
        Thread(target=lib.DeadLoop).start()

补充:如果不会编写脚本也可以直接使用负载模拟工具

yum install stress -y
stress -c 1  -t 60 # 对1个cpu,测试60秒

联系管理员微信tutu19192010,注册账号

上一篇
下一篇
Copyright © 2022 Egon的知识星球 egonlin.com 版权所有 帮助IT小伙伴学到真正的技术