Linux Cgroups介绍


Linux Cgroups介绍

  • Linux Cgroups 提供了对一组进程及将来子进程的资源控制、控制和统计能力,这些资源包括CPU、内存、存储、网络等。通过Cgroups 可以方便的限制某个进程的资源占用,并可以实时监控进程的监控和统计信息。

Cgroups 三个组件

  • Cgroup是对进程分组管理的一种机制,一个cgroup包含一组进程,并可以在这个cgroup上增加Linux subsystem的各种参数配置,将一组进程和一组subsystem的系统参数关联起来。
  • subsystem是一组资源控制模块,一般包含如下几项:
    • blkio 设置对块设备(比如硬盘)输入输出的访问控制
    • cpu 设置 cgroup 中进程的 CPU 被调度的策略。
    • cpuacct 可以统计 cgroup 中进程的 CPU 占用 。
    • cpuset 在多核机器上设置 cgroup 中进程可以使用的 CPU 和内存(此处内存仅使用于NUMA 架构) 。
    • devices 控制 cgroup 中进程对设备的访问 。
    • freezer 用于挂起( suspend )和恢复( resume) cgroup 中的进程 。
    • memory 用于控制 cgroup 中进程的内存占用 。
    • net_els 用于将 cgroup 中进程产生的网络包分类,以便 Linux 的 tc (traffic con 位oller )可以根据分类区分出来自某个 cgroup 的包并做限流或监控 。
    • net_prio 设置 cgroup 中进程产生的网络流量的优先级 。
    • ns 这个 subsystem 比较特殊,它的作用是使 cgroup 中的进程在新的 Namespace 中 fork新进程 (CNEWNS )时,创建出 一 个新的 cgroup ,这个 cgroup 包含新的 Names pace 中的进程
    • 每个 subsystem 会关联到定义了相应限制的 cgroup 上,并对这个 cgroup 中的进程做相应的限制和控制。subsystem是逐步合并到内核中的,通过cgroup命令(apt-get install cgroup-bin)查看kerne支持的subsystem
  • hierarchy的功能是把一 组cgroup串成一个树状的结构,一个这样的树便是一个树,通过这种树状结构,Cgroups 可以做到继承,比如系统对一组定时任务进程通过cgroupl限制cpu的使用率,然后其中有一个dump日志进程需要限制磁盘IO,为了避免限制了磁盘IO之后影响到其他进程,就可以创建cgroup2,使其继承与cgroup1并限制磁盘IO,这样cgroup2便继承了cgroup1中对cpu使用率的限制,并增加了磁盘IO的限制而不影响到cgroup1中其他进程。

三个组件相互的关系

  • 系统在创建了新的hierarchy之后,系统中所有的进程都会加入这个hierarchy的cgroup根节点,这个cgroup根节点是cgroup默认创建的
  • 一个subsystem只能附加一个hierarchy上面
  • 一个hierarchy可以附件多个subsystem
  • 一个进程可以作为多个cgroup的成员,但是这些cgroup必须在不同的hierarchy中,也可以根据需要转移到其他cgroup
  • 一个进程fork出子进程时,子进程是和父进程在同一个cgroup中的,也可以根据需要将其移动到其他cgroup中。

kernel接口

  • Cgroups中的hierarchy是一种树状组织结构,kernel为了使对Cgroups的配置更加直观,是通过一个虚拟树状文件系统配置Cgroups的,通过层级的目录虚拟出cgroup树
    1、创建并挂载一个hierarchy(cgroup树)
mkdir cgroup-test
mount -t cgroup -o none,name=cgroup-test cgroup-test ./cgroup-test  
  • 可以看到系统在这个目录生成一些默认文件了,这些文件就是这个hierarchy中cgroup根节点的配置项:

  • cgroup.clone_children:cpuset的subsystem会读取这个配置文件,如果这个值是1,子cgroup才会继承cgroup的cpuset的配置

  • cgroup.procs:树中当前节点cgroup中进程组ID,现在的位置是在根节点,这个文件中会有现在系统中所有进程组的ID

  • notify_on_release和release_agent会一起使用,notify_on_release标识当这个cgroup最后一个进程退出的时候是否执行了release_agent;release_agent是一个路径,通常用作进程退出后自动清理掉不再使用的cgroup

  • tasks标识该cgroup下面的进程ID,如果把一个进程ID写到tasks文件中,便会将相应的进程加入这个cgroup

  • 2、在刚创建的hierarchy上cgroup根节点中扩建出两个子cgroup

cd cgroup-test  
mkdir cgroup-1 cgroup-2
  • 在一个cgroup的目录下创建文件夹时,kernel会把这个文件夹标记为cgroup的子cgroup,会继承父cgroup的属性
  • 3、一个进程在一个Cgroup的hierarchy中,只能在一个cgroup节点上存在,系统的所有进程都会默认在根节点上存在,可以将进程移动到其他cgroup节点,只需将进程id移动到cgroup节点的tasks文件中即可。
cd cgroup-1
echo $$
sh -c "echo $$" >>tasks  //将终端进程移动到cgroup-1中
cat /proc//cgroup

Linux Cgroups介绍

  • 4、通过subsystem限制cgroup中进程资源
  • 在上面创建hierarchy的时候,这个hierarchy并没有关联到任何的subsystem,所以没办法通过那个hierarchy中的cgroup节点限制进程资源占用比,其实系统已经默认未每个subsystem创建一个默认的hierarchy,比如memory的hierarchy。
mount|grep memory

Linux Cgroups介绍

  • 可以看到/sys/fs/cgroup/memory目录挂在了memory subsystem的hierarchy上,通过在这个hierarchy中创建cgroup,限制如下进程占用的你内存。
  • 启用一个占用内存的stress进程
stress --vm-bytes 200m --vm-keep -m 1
  • 创建cgroup
cd /sys/fs/cgroup/memory
mkdir test-limit-memory && cd test-limit-memory
  • 设置最大cgroup的最大内存占用为100MB
sh -c "echo "100m" > memory.limit_in_bytes"
  • 将当前进程移动到这个cgroup
sh -c "echo $$ > tasks"

  • 再次运行占用内存200m的stress进程
stress --vm-bytes 200m --vm-keep -m 1

Linux Cgroups介绍
Linux Cgroups介绍

docker 是如何配置Cgroups的

docker run -itd -m 128m ubuntu
cd /sys/fs/cgroup/memory/docker/e9bc042a50c13f117a2bd8868d69234524432ecc7504bf803708a413dee3a5f3/
cat memory.limit_in_bytes

Linux Cgroups介绍

  • 可以看到,docker通过为每个容器创建cgroup,并通过cgroup去配置资源限制和资源监控。

Go实现通过cgroup限制容器的资源

package main

import(


"os/exec"
"path"
"os"
"fmt"
"io/ioutil"
"syscall"
"strconv"

)
const cgroupMemoryHierarchyMount="/sys/fs/cgroup/memory"

func main(){

if os.Args[0]=="/proc/self/exe"{

fmt.Printf("current pid %d",syscall.Getpid())
fmt.Println()
cmd:=exec.Command("sh","-c",`stress --vm-bytes 200m --vm-keep -m 1`)
cmd.SysProcAttr=&syscall.SysProcAttr{}
cmd.Stdin=os.Stdin
cmd.Stdout=os.Stdout
cmd.Stderr=os.Stderr
if err:=cmd.Run();err!=nil{
fmt.Println(err)
os.Exit(1)


}
}
cmd:=exec.Command("/proc/self/exe")
cmd.SysProcAttr=&syscall.SysProcAttr{
	Cloneflags:syscall.CLONE_NEWUTS|syscall.CLONE_NEWPID|syscall.CLONE_NEWNS,}
cmd.Stdin=os.Stdin
cmd.Stdout=os.Stdout
cmd.Stderr=os.Stderr
if err:=cmd.Start();err!=nil{

fmt.Println("ERROR",err)
os.Exit(1)

}else{

fmt.Printf("%v",cmd.Process.Pid)
os.Mkdir(path.Join(cgroupMemoryHierarchyMount,"testmemorylimit"),0755)
ioutil.WriteFile(path.Join(cgroupMemoryHierarchyMount,"testmemorylimit","tasks"),[]byte(strconv.Itoa(cmd.Process.Pid)),0644)
ioutil.WriteFile(path.Join(cgroupMemoryHierarchyMount,"testmemorylimit","memory.limit_in_bytes"),[]byte("100m"),0644)




}
cmd.Process.Wait()
}



原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/279545.html

(0)
上一篇 2022年8月8日
下一篇 2022年8月8日

相关推荐

发表回复

登录后才能评论