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