0%

容器实现-cgroup

前言

cgroup是实现容器的除了namespace以外的另一种技术,namespace的用途是隔离环境,而cgroup则用来限制进程使用的资源。

对于cgroup的概述之前已写过一篇文章,这是链接:linux资源管理器–cgroups理解

简单来说,使用cgroup限制进程的资源有如下几步:

  1. 规划需要限制的资源,比如说限制内存资源为100M。
  2. 创建需限制的进程。
  3. /sys/fs/cgroup/memory/目录下创建文件夹,并进入该文件夹。
  4. 100M写入memory.max_usage_in_bytes文件,将进程pid追加写入tasks文件。

这样,该进程如果使用超过100M的内存,则将无法申请更多的内存,或直接被kill掉。

实践

那么,尝试写一个程序测试一下吧。

示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package main

import (
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"strconv"
"syscall"
)

const cgroupMemoryHierarchyMount = "/sys/fs/cgroup/memory"
const memoryLimit = "memory.limit_in_bytes"

func main() {
cmd := exec.Command("sh")
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.Stdin = os.Stdin
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout

// 运行子进程,且主进程不阻塞等待
var p string
if err := cmd.Start(); err != nil {
log.Fatal(err)
} else {
// 打印子进程pid
fmt.Printf("subprocess pid: %v\n", cmd.Process.Pid)
// 创建子文件夹
p = path.Join(cgroupMemoryHierarchyMount, strconv.Itoa(cmd.Process.Pid))
os.Mkdir(p, 0755)
fmt.Printf("mkdir: %v\n", p)

// 限制内存使用
ioutil.WriteFile(path.Join(p, memoryLimit), []byte("10m"), 0644)
fmt.Printf("writefile: %v content: %v\n", path.Join(p, memoryLimit), "10")

// 将子进程加入至该cgroup中
ioutil.WriteFile(path.Join(p, "tasks"), []byte(strconv.Itoa(cmd.Process.Pid)), 0644)
fmt.Printf("writefile: %v content: %v\n", path.Join(p, "tasks"), cmd.Process.Pid)

}

// 等待子进程运行结束
cmd.Process.Wait()

fmt.Println("subprocess ended.")
os.Remove(p)
fmt.Printf("rmdir: %v\n", p)
}

运行该程序:

1
2
3
4
5
6
7
8
[root@staight chmdocker]# go build
[root@staight chmdocker]# ./chmdocker
sh-4.2# subprocess pid: 30560
mkdir: /sys/fs/cgroup/memory/30560
writefile: /sys/fs/cgroup/memory/30560/memory.limit_in_bytes content: 10
writefile: /sys/fs/cgroup/memory/30560/tasks content: 30560

sh-4.2#

尝试在新的shell中运行stress程序:

1
2
3
4
5
sh-4.2# stress --vm 1 --vm-bytes 100M --vm-keep
stress: info: [30842] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: FAIL: [30842] (415) <-- worker 30843 got signal 9
stress: WARN: [30842] (417) now reaping child worker processes
stress: FAIL: [30842] (451) failed run completed in 0s

如上,stress进程想要申请100M的内存,因超过cgroup的内存限制而被9信号强制kill,试验成功。

最后,关闭新的shell:

1
2
3
4
sh-4.2# exit
exit
subprocess ended.
rmdir: /sys/fs/cgroup/memory/30560

后记

在实验的过程中,有个问题需要说一下:

首先,默认情况下,将一个进程添加至cgroup中,那么它以后创建的所有子进程也都将自动添加至该cgroup。可是在如上程序中,将sh进程添加至cgroup的时间是已经在该进程运行一段时间之后的,如果sh进程在被添加至cgroup之前已经创建了子进程,那么它的子进程将不在cgroup中,也就是说sh进程的子进程将不被cgroup管控。这种情况该如何解决呢?

待解决。。。