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
- 4、通过subsystem限制cgroup中进程资源
- 在上面创建hierarchy的时候,这个hierarchy并没有关联到任何的subsystem,所以没办法通过那个hierarchy中的cgroup节点限制进程资源占用比,其实系统已经默认未每个subsystem创建一个默认的hierarchy,比如memory的hierarchy。
mount|grep memory
- 可以看到/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
docker 是如何配置Cgroups的
docker run -itd -m 128m ubuntu
cd /sys/fs/cgroup/memory/docker/e9bc042a50c13f117a2bd8868d69234524432ecc7504bf803708a413dee3a5f3/
cat memory.limit_in_bytes
- 可以看到,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/279507.html