前言
上节[mydocker]---构造容器02-实现资源限制01中已经加入了对
memory
的限制. 本节将会梳理一下上节的代码使其更清晰, 为了方便扩展加入其它的subsystem
的限制.
代码: 代码下载
效果
最终要达到的效果会如下所示:
--------------------------terminal 01---------------------------------
root@nicktming:/sys/fs/cgroup/memory# pwd
/sys/fs/cgroup/memory
root@nicktming:/sys/fs/cgroup/memory# ls | grep mydocker
--------------------------terminal 02---------------------------------
root@nicktming:~/go/src/github.com/nicktming# pwd
/root/go/src/github.com/nicktming
root@nicktming:~/go/src/github.com/nicktming# git clone https://github.com/nicktming/mydocker.git
root@nicktming:~/go/src/github.com/nicktming# cd mydocker
root@nicktming:~/go/src/github.com/nicktming/mydocker# git checkout code-3.2.2
root@nicktming:~/go/src/github.com/nicktming/mydocker# go build .
root@nicktming:~/go/src/github.com/nicktming/mydocker# ./mydocker run -it -m 12M /bin/sh
2019/04/02 23:53:26 Apply absolutePath:/sys/fs/cgroup/memory/mydocker, taskPath:/sys/fs/cgroup/memory/mydocker/tasks
2019/04/02 23:53:26 err :
#
// 查看是否生成cgroup 并且对此进程加入限制
--------------------------terminal 01---------------------------------
root@nicktming:/sys/fs/cgroup/memory# ls | grep mydocker
mydocker
root@nicktming:/sys/fs/cgroup/memory# cat mydocker/tasks
29478
root@nicktming:/sys/fs/cgroup/memory# cat mydocker/memory.limit_in_bytes
12582912
--------------------------terminal 02---------------------------------
// 运行测试程序后退出
# ./memory
1M memory allocated
2M memory allocated
3M memory allocated
4M memory allocated
5M memory allocated
6M memory allocated
7M memory allocated
8M memory allocated
9M memory allocated
10M memory allocated
11M memory allocated
Killed
# exit
--------------------------terminal 01---------------------------------
// 查看mydocker是否被删除
root@nicktming:/sys/fs/cgroup/memory# ls | grep mydocker
root@nicktming:/sys/fs/cgroup/memory#
实现
在上个版本
code-3.2.1
的基础上进行改动, 会做出下面几点改动.
1. cgroups/cgroup-manager.go
在此处改动如下:
import "github.com/nicktming/mydocker/cgroups/subsystems"
type CroupManger struct {
// 限制的值
Resource *subsystems.ResourceConfig
// 用于在当前容器中标识有哪些subsystem需要做限制
SubsystemsIns []subsystems.Subsystem
// 用于启动多个container的containerId 比如memory则是:/sys/fs/cgroup/memory/mydocker/[containerId]
//path string
}
func (c *CroupManger) Set() {
for _, sub := range c.SubsystemsIns {
sub.Set(c.Resource)
}
}
func (c *CroupManger) Apply(pid string) {
for _, sub := range c.SubsystemsIns {
sub.Apply(pid)
}
}
func (c *CroupManger) Destroy() {
for _, sub := range c.SubsystemsIns {
sub.Remove()
}
}
这里的CroupManger是暴露给外部调用程序, 统一进行管理包括加入或者去除某种
subsystem
类型的限制如Set()
和Destroy()
, 加入pid
(Apply()
方法)等等.
至于需要对某种
subsystem
进行加入限制,则由属性SubsystemsIns
来获得, 该属性是个数组, 包括所有需要加限制的subsystem
. 所以subsystems.Subsystem
是一个接口, 所有的subsystem
包括(memory
,cpuset
等等)都需要实现该接口的方法.
加限制总得需要加多少, 比如需要对内存最大限制多少, 这些变量会保存到属性
Resource
中.
2. 增加cgroups/subsystems/ResourceConfig.go
package subsystems
type ResourceConfig struct {
MemoryLimit string
}
type Subsystem interface {
Name() string
Set(res *ResourceConfig) error
Apply(pid string) error
Remove() error
}
const (
ResourceName = "mydocker"
)
这里只是定义了所有
subsystem
需要实现的接口Subsystem
和资源变量ResourceConfig
.
3. 修改cgroups/subsystems/memory.go
因为需要实现
Subsystem
接口, 对于Set
方法只需要在原来的基础上把参数加以修改即可, 对于Apply, Remove
方法不需要做改变, 增加了MemorySubsystem
定义和Name()
方法.
type MemorySubsystem struct {
}
func (s *MemorySubsystem) Set(res *ResourceConfig) error {
if res.MemoryLimit != "" {
content := res.MemoryLimit
absolutePath := ""
if absolutePath = FindAbsolutePath(s.Name()); absolutePath == "" {
log.Printf("ERROR: absoutePath is empty!\n")
return fmt.Errorf("ERROR: absoutePath is empty!\n")
}
if err := ioutil.WriteFile(path.Join(absolutePath, "memory.limit_in_bytes"), []byte(content),0644); err != nil {
log.Printf("ERROR write content:%s.\n", content)
return fmt.Errorf("ERROR write content:%s.\n", content)
}
}
return nil
}
func (s *MemorySubsystem) Name() string {
return "memory"
}
修改command/command.go
修改
RunCommand
, 因为从命令行中得到限制内存的值memory
, 如果用户设置了该参数, 此时需要把该限制对应的subsystem
加入到CroupManager
中的属性SubsystemsIns
数组中对其限制, 并把memory
值放到ResourceConfig
中的MemoryLimit
属性.
var RunCommand = cli.Command{
Name: "run",
Flags: []cli.Flag {
cli.BoolFlag{
Name: "it",
Usage: "enable tty",
},
cli.StringFlag{
Name: "m",
Usage: "memory usage",
},
},
Action: func(c *cli.Context) error {
tty := c.Bool("it")
memory := c.String("m")
command := c.Args().Get(0)
res := subsystems.ResourceConfig{
MemoryLimit: memory,
}
cg := cgroups.CroupManger {
Resource: &res,
SubsystemsIns: make([]subsystems.Subsystem, 0),
}
if memory != "" {
cg.SubsystemsIns = append(cg.SubsystemsIns, &subsystems.MemorySubsystem{})
}
Run(command, tty, &cg)
return nil
},
}
修改command/run.go
因为
Run
方法中上个版本是直接传的内存限制值memory
,这个版本已经封装了memory
, 因此Run
方法中也需要做一些相应的变化.
func Run(command string, tty bool, cg *cgroups.CroupManger) {
//cmd := exec.Command(command)
cmd := exec.Command("/proc/self/exe", "init", command)
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC,
}
if tty {
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
cmd.Stdin = os.Stdin
}
/**
* Start() will not block, so it needs to use Wait()
* Run() will block
*/
if err := cmd.Start(); err != nil {
log.Printf("Run Start err: %v.\n", err)
log.Fatal(err)
}
//log.Printf("222 before process pid:%d, memory:%s\n", cmd.Process.Pid, memory)
//subsystems.Set(memory)
//subsystems.Apply(strconv.Itoa(cmd.Process.Pid))
//defer subsystems.Remove()
// 变动在此
cg.Set()
defer cg.Destroy()
cg.Apply(strconv.Itoa(cmd.Process.Pid))
cmd.Wait()
}
所有改动已经做完, 运行结果会如上面的效果一致.
参考
1. 自己动手写docker.(基本参考此书,加入一些自己的理解,加深对
docker
的理解)
全部内容
1. [mydocker]---环境说明
2. [mydocker]---urfave cli 理解
3. [mydocker]---Linux Namespace
4. [mydocker]---Linux Cgroup
5. [mydocker]---构造容器01-实现run命令
6. [mydocker]---构造容器02-实现资源限制01
7. [mydocker]---构造容器02-实现资源限制02
8. [mydocker]---构造容器03-实现增加管道
9. [mydocker]---通过例子理解存储驱动AUFS
10. [mydocker]---通过例子理解chroot 和 pivot_root
11. [mydocker]---一步步实现使用busybox创建容器
12. [mydocker]---一步步实现使用AUFS包装busybox
13. [mydocker]---一步步实现volume操作
14. [mydocker]---实现保存镜像
15. [mydocker]---实现容器的后台运行
16. [mydocker]---实现查看运行中容器
17. [mydocker]---实现查看容器日志
18. [mydocker]---实现进入容器Namespace
19. [mydocker]---实现停止容器
20. [mydocker]---实现删除容器
21. [mydocker]---实现容器层隔离
22. [mydocker]---实现通过容器制作镜像
23. [mydocker]---实现cp操作
24. [mydocker]---实现容器指定环境变量
25. [mydocker]---网际协议IP
26. [mydocker]---网络虚拟设备veth bridge iptables
27. [mydocker]---docker的四种网络模型与原理实现(1)
28. [mydocker]---docker的四种网络模型与原理实现(2)
29. [mydocker]---容器地址分配
30. [mydocker]---网络net/netlink api 使用解析
31. [mydocker]---网络实现
32. [mydocker]---网络实现测试