[mydocker]---构造容器02-实现资源限制02

前言

上节[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的理解)

全部内容

[mydocker]---构造容器02-实现资源限制02_第1张图片
mydocker.png

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]---网络实现测试

你可能感兴趣的:([mydocker]---构造容器02-实现资源限制02)