美文网首页
[mydocker]---一步步实现使用busybox创建容器

[mydocker]---一步步实现使用busybox创建容器

作者: nicktming | 来源:发表于2019-05-11 09:56 被阅读0次

    1. 准备工作

    1.1 准备环境

    Tingzhangs-MacBook-Pro:mydocker tingzhangming$ git clone https://github.com/nicktming/mydocker.git
    Tingzhangs-MacBook-Pro:mydocker tingzhangming$ git checkout code-3.3
    Tingzhangs-MacBook-Pro:mydocker tingzhangming$ git checkout -b dev-4.1
    

    1.2 准备busybox

    执行以下两个命令获得busybox, 并放入每个目录下(本文busybox地址:/root/busybox)

    docker export `docker run -itd busybox:latest` > busybox.tar
    mkdir busybox && tar -xvf busybox.tar -C busybox
    

    1.3 本文最终效果

    root@nicktming:~/go/src/github.com/nicktming/mydocker# git clone https://github.com/nicktming/mydocker.git
    root@nicktming:~/go/src/github.com/nicktming/mydocker# git checkout code-4.1
    root@nicktming:~/go/src/github.com/nicktming/mydocker# ./mydocker run -it /bin/sh
    2019/04/06 15:49:23 rootPath:
    2019/04/06 15:49:23 set cmd.Dir by default: /root/busybox
    2019/04/06 15:49:23 current path: /root/busybox.
    / # ls
    bin   dev   etc   home  proc  root  sys   tmp   usr   var
    / # ps -l
    PID   USER     TIME  COMMAND
        1 root      0:00 /bin/sh
        5 root      0:00 ps -l
    / # mount
    rootfs on / type rootfs (rw)
    /dev/disk/by-uuid/23922b2b-365b-4a75-999c-61c297921652 on / type ext4 (rw,noatime,data=ordered)
    proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
    / # exit
    root@nicktming:~/go/src/github.com/nicktming/mydocker# 
    

    2. 实现改变init程序执行路径

    cmd加入一些参数, cmd.Dir = "/root", 在执行用户程序的时候可以设置该程序在哪个目录下执行.

    2.1 修改command/run.go

    ...
        cmd := exec.Command("/proc/self/exe", "init")
    
        cmd.SysProcAttr = &syscall.SysProcAttr{
            Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC,
        }
        log.Printf("cmd.Dir:%s\n", "/root")
        cmd.Dir = "/root"
        cmd.ExtraFiles = []*os.File{reader}
        sendInitCommand(command, writer)
    ...
    

    2.2 修改command/init.go

    加入提示信息

    ...
        pwd, err := os.Getwd()
        if err != nil {
            log.Printf("ERROR: get pwd error!\n")
            return
        }
        log.Printf("current path: %s.\n", pwd)
    
    
        if err := syscall.Exec(command, []string{command}, os.Environ()); err != nil {
            log.Printf("syscall.Exec err: %v\n", err)
            log.Fatal(err)
        }
    

    执行如下:

    root@nicktming:~/go/src/github.com/nicktming/mydocker# pwd
    /root/go/src/github.com/nicktming/mydocker
    root@nicktming:~/go/src/github.com/nicktming/mydocker# go build .
    root@nicktming:~/go/src/github.com/nicktming/mydocker# ./mydocker run -it /bin/ls
    2019/04/06 13:44:28 cmd.Dir:/root
    2019/04/06 13:44:28 read from commandline:
    2019/04/06 13:44:28 read from pipe:/bin/ls
    2019/04/06 13:44:28 current path: /root.
    aufs  busybox  busybox.tar  cgroup  go  go1.7.3.linux-amd64.tar.gz  memory  memory.c  mnt
    

    执行结果如上所示, ./mydocker run -it /bin/ls是在/root/go/src/github.com/nicktming/mydocker执行的. 但是用户程序/bin/ls是在/root目录下执行的, 所以显示结果也是对的.

    root@nicktming:~/go/src/github.com/nicktming/mydocker# ./mydocker run -it /bin/sh
    2019/04/06 13:44:55 cmd.Dir:/root
    2019/04/06 13:44:55 read from commandline:
    2019/04/06 13:44:55 read from pipe:/bin/sh
    2019/04/06 13:44:55 current path: /root.
    # ls
    aufs  busybox  busybox.tar  cgroup  go  go1.7.3.linux-amd64.tar.gz  memory  memory.c  mnt
    # exit
    

    2.3 给执行目录参数化

    修改command/command.go如下:

        Action: func(c *cli.Context) error {
            tty     := c.Bool("it")
            memory  := c.String("m")
            rootPath  := c.String("r")
            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, rootPath)
            return nil
        },
    

    对应的需要修改command/run.go如下:

    func Run(command string, tty bool, cg *cgroups.CroupManger, rootPath string)  {
    ...
        cmd := exec.Command("/proc/self/exe", "init")
    
        cmd.SysProcAttr = &syscall.SysProcAttr{
            Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC,
        }
        log.Printf("rootPath:%s\n", rootPath)
        cmd.Dir = rootPath
        if rootPath == "" {
            log.Printf("set cmd.Dir by default: /root/busybox\n")
            cmd.Dir = "/root/busybox"
        }
    
        cmd.ExtraFiles = []*os.File{reader}
        sendInitCommand(command, writer)
    ...
    }
    

    执行如下所示:

    root@nicktming:~/go/src/github.com/nicktming/mydocker# go build .
    root@nicktming:~/go/src/github.com/nicktming/mydocker# ./mydocker run -it /bin/sh
    2019/04/06 14:46:35 rootPath:
    2019/04/06 14:46:35 set cmd.Dir by default: /root/busybox
    2019/04/06 14:46:35 current path: /root/busybox.
    / # ls
    bin   dev   etc   home  proc  root  sys   tmp   usr   var
    / # exit
    root@nicktming:~/go/src/github.com/nicktming/mydocker# ./mydocker run -it -r /root/busybox /bin/sh
    2019/04/06 14:47:05 rootPath:/root/busybox
    2019/04/06 14:47:05 current path: /root/busybox.
    / # ls
    bin   dev   etc   home  proc  root  sys   tmp   usr   var
    / # exit
    

    3. 使用pivot_root

    关于pivot_root 可以参考 [mydocker]---通过例子理解chroot 和 pivot_root.

    pivotRoot(root string) 可以把路径root变为根节点.

    func pivotRoot(root string) error {
        if err := syscall.Mount(root, root, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
            return fmt.Errorf("Mount rootfs to itself error: %v", err)
        }
        pivotDir := filepath.Join(root, ".pivot_root")
        if err := os.Mkdir(pivotDir, 0777); err != nil {
            return err
        }
        if err := syscall.PivotRoot(root, pivotDir); err != nil {
            return fmt.Errorf("pivot_root %v", err)
        }
        if err := syscall.Chdir("/"); err != nil {
            return fmt.Errorf("chdir / %v", err)
        }
    
        pivotDir = filepath.Join("/", ".pivot_root")
        if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil {
            return fmt.Errorf("unmount pivot_root dir %v", err)
        }
        return os.Remove(pivotDir)
    }
    

    4. 实现容器根目录

    由于上面已经实现了2. 实现改变init程序执行路径3. 使用pivot_root, 因此可以使用pivot_root将init程序执行路径变为根目录. 只需要在command/init.go中的Init方法中调用一下pivot_root方法即可. 改动如下:

    func Init(command string)  {
        command = readFromPipe()
    
        pwd, err := os.Getwd()
        if err != nil {
            log.Printf("ERROR: get pwd error!\n")
            return
        }
        log.Printf("current path: %s.\n", pwd)
        pivotRoot(pwd)
    
        defaultMountFlags := syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
        syscall.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), "")
    
        if err := syscall.Exec(command, []string{command}, os.Environ()); err != nil {
            log.Printf("syscall.Exec err: %v\n", err)
            log.Fatal(err)
        }
    }
    

    执行结果如下: 指定目录为/root/busybox.

    root@nicktming:~/go/src/github.com/nicktming/mydocker# go build .
    root@nicktming:~/go/src/github.com/nicktming/mydocker# ./mydocker run -it -r /root/busybox /bin/sh
    2019/04/06 15:13:57 rootPath:/root/busybox
    2019/04/06 15:13:57 current path: /root/busybox.
    / # ps -l
    PID   USER     TIME  COMMAND
        1 root      0:00 /bin/sh
        4 root      0:00 ps -l
    / # pwd
    /
    / # ls
    bin   dev   etc   home  proc  root  sys   tmp   usr   var
    / # exit
    root@nicktming:~/go/src/github.com/nicktming/mydocker# 
    

    如果指定的目录中不包括有busybox会报错, 因为执行的用户程序/bin/sh会从当前根目录下找, 找不到会报错. 该知识点可以参考[mydocker]---通过例子理解chroot 和 pivot_root.

    root@nicktming:~/go/src/github.com/nicktming/mydocker# ./mydocker run -it -r /root /bin/sh
    2019/04/06 15:16:41 rootPath:/root
    2019/04/06 15:16:41 current path: /root.
    2019/04/06 15:16:41 syscall.Exec err: no such file or directory
    2019/04/06 15:16:41 no such file or directory
    

    5. 时序图

    set_root.png

    6. 参考

    1. 自己动手写docker.(基本参考此书,加入一些自己的理解,加深对docker的理解)

    7. 全部内容

    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]---一步步实现使用busybox创建容器

          本文链接:https://www.haomeiwen.com/subject/qsxriqtx.html