关于Cgroups
上一篇namespace主要是说环境隔离,此篇Cgroups主要是说对隔离环境做出限制,防止隔离出的空间相互抢夺资源。
Cgroups同样也是linux的内核技术,提供了对一组进程及其子进程的资源限制,控制,以及统计的功能。这些资源包括:
cpu、内存、存储、网络等。
Cgroups中的三个组件,cgroup、subsystem、hierarchy.首先cgroup是对进程分组管理的一种机制,一个Cgroup包含一组进程,
并可以对此cgroup增加各种subsystem参数,将一组进程和一组subsystem关联起来。其次subsystem是一组资源管理控制的模块。
(比如其中的blkio可以设置对块设备的IO控制,memory用于控制进程的内存占用...)最后hierarchy,它的功能是把一组Cgroup串
成一个树状的结构,通过这种结构Cgroups可以做到继承。
手动调用kernel接口感受下Cgroups
1创建并挂载一个hierarchy(Cgroup树)
[root@iZ2zebynirz2ac5eron661Z ~]# mkdir cgroup-test #创建一个hierarchy挂载点
[root@iZ2zebynirz2ac5eron661Z ~]# mount -t cgroup -o none,name=cgroup-test cgroup-test ./cgroup-test #挂载一个hierarchy
[root@iZ2zebynirz2ac5eron661Z ~]# ls ./cgroup-test/ #挂载后发现多了些默认配置文件
cgroup.event_control cgroup.procs notify_on_release release_agent tasks
2在根节点中扩展出两个子Cgroup
[root@iZ2zebynirz2ac5eron661Z cgroup-test]# mkdir cgroup-1
[root@iZ2zebynirz2ac5eron661Z cgroup-test]# mkdir cgroup-2
[root@iZ2zebynirz2ac5eron661Z cgroup-test]# tree
.
├── cgroup-1
│ ├── cgroup.event_control
│ ├── cgroup.procs
│ ├── notify_on_release
│ └── tasks
├── cgroup-2
│ ├── cgroup.event_control
│ ├── cgroup.procs
│ ├── notify_on_release
│ └── tasks
├── cgroup.event_control
├── cgroup.procs
├── notify_on_release
├── release_agent
└── tasks
我们可以发现,在一个Cgroup的目录下创建文件时,kernel会把文件标记为这个Cgroup的子Cgroup,他们会继承Cgroup
的属性。
3在Cgroup中添加移动进程
一个进程在一个Cgroups的hierarchy中,只能在一个Cgroup节点上存在,系统的所有进程都会默认在根节点上存在,
将进程ID写到移动到的Cgroup节点的tasks文件中即可将该进程移动到该Cgroup节点。
[root@iZ2zebynirz2ac5eron661Z cgroup-1]# echo $$
18091
[root@iZ2zebynirz2ac5eron661Z cgroup-1]# cat /proc/18091/cgroup
1:name=cgroup-test:/
[root@iZ2zebynirz2ac5eron661Z cgroup-1]# sh -c "echo $$ >>tasks"
[root@iZ2zebynirz2ac5eron661Z cgroup-1]# cat /proc/18091/cgroup
1:name=cgroup-test:/cgroup-1
[root@iZ2zebynirz2ac5eron661Z cgroup-1]#
可以看到,当前的进程移动到了cgroup-1中了
4在通过subsystem限制Cgroup中进程的资源
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
7644 bozhon 20 0 211980 205620 748 R 100.0 2.6 1:55.63 stress
bozhon@bozhon:~$ mkdir test-limit-memory && cd test-limit-memory
bozhon@bozhon:~/test-limit-memory$ cd /sys/fs/cgroup/memory
bozhon@bozhon:/sys/fs/cgroup/memory$ sudo mkdir test-limit-memory && cd test-limit-memory
[sudo] password for bozhon:
bozhon@bozhon:/sys/fs/cgroup/memory/test-limit-memory$ sudo sh -c "echo "100m">memory.limit_in_bytes"
bozhon@bozhon:/sys/fs/cgroup/memory/test-limit-memory$ sudo sh -c "echo $$ >tasks"
bozhon@bozhon:/sys/fs/cgroup/memory/test-limit-memory$ stress --vm-bytes 200m --vm-keep -m 1
stress: info: [7674] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
已杀死
bozhon@bozhon:/sys/fs/cgroup/memory/test-limit-memory$ stress --vm-bytes 200m --vm-keep -m 1
stress: info: [7676] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
已杀死
bozhon@bozhon:/sys/fs/cgroup/memory/test-limit-memory$ stress --vm-bytes 100m --vm-keep -m 1
stress: info: [7678] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
已杀死
bozhon@bozhon:/sys/fs/cgroup/memory/test-limit-memory$ stress --vm-bytes 10m --vm-keep -m 1
stress: info: [7680] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
我们可以看到当使用限制100M后,大于等于100M的进程会杀死
Cgroups在docker中的运用
02613ec46397ee46a6a9bd7e62f78b1bd89f15f07b898f3c8553db4243b5346f
为某个容器ID
bozhon@bozhon:~$ cd /sys/fs/cgroup/memory/docker/02613ec46397ee46a6a9bd7e62f78b1bd89f15f07b898f3c8553db4243b5346f/
bozhon@bozhon:/sys/fs/cgroup/memory/docker/02613ec46397ee46a6a9bd7e62f78b1bd89f15f07b898f3c8553db4243b5346f$ ls
cgroup.clone_children memory.kmem.failcnt memory.kmem.tcp.limit_in_bytes memory.max_usage_in_bytes memory.soft_limit_in_bytes notify_on_release
cgroup.event_control memory.kmem.limit_in_bytes memory.kmem.tcp.max_usage_in_bytes memory.move_charge_at_immigrate memory.stat tasks
cgroup.procs memory.kmem.max_usage_in_bytes memory.kmem.tcp.usage_in_bytes memory.numa_stat memory.swappiness
memory.failcnt memory.kmem.slabinfo memory.kmem.usage_in_bytes memory.oom_control memory.usage_in_bytes
memory.force_empty memory.kmem.tcp.failcnt memory.limit_in_bytes memory.pressure_level memory.use_hierarchy
bozhon@bozhon:/sys/fs/cgroup/memory/docker/02613ec46397ee46a6a9bd7e62f78b1bd89f15f07b898f3c8553db4243b5346f$ cat memory.limit_in_bytes
9223372036854771712
用go语言实现通过Cgroups限制容器资源
package main
import (
"os"
"fmt"
"syscall"
"os/exec"
"path"
"io/ioutil"
"strconv"
)
//挂载了memory subsystem 的hierarchy的根目录的位置
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.Run(); err!=nil{
fmt.Println(err)
os.Exit(1)
}else{
//得到fork处理进程映射在外部命名空间的pid
fmt.Printf("%v",cmd.Process.Pid)
//在系统默认创建挂载了memory subsystem的Hierachy上创建cgroup
os.Mkdir(path.Join(cgroupMemoryHierarchyMount,"testmemorylimit"),0755)
//将容器进程加入到这个cgroup中
ioutil.WriteFile(path.Join(cgroupMemoryHierarchyMount,"testmemorylimit","memory.limit_in_bytes"),
[]byte(strconv.Itoa(cmd.Process.Pid)),0644)
//限制cgroup进程使用
ioutil.WriteFile(path.Join(cgroupMemoryHierarchyMount,"testmemorylimit","memory.limit_in_bytes"),
[]byte("100m"),0644)
}
cmd.Process.Wait()
}
我们可以看到占用了20%内存(1G*20%=100M),说明限制成功
34192 maojian+ 20 0 41820 3764 3120 R 4.3 0.4 0:00.40 top
1 root 20 0 119744 5844 4012 S 0.0 0.6 0:02.73 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:01.80 ksoftirqd/0
5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0H
7 root 20 0 0 0 0 S 0.0 0.0 0:00.70 rcu_sched
8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_bh
9 root rt 0 0 0 0 S 0.0 0.0 0:00.00 migration/0
10 root rt 0 0 0 0 S 0.0 0.0 0:00.03 watchdog/0
11 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kdevtmpfs