动手自己写Docker之实现容器文件系统与镜像的隔离.

mac2022-06-30  268

本节所要实现的功能是改变init程序的执行路径,并且实现容器文件系统与镜像的隔离.

pivotRoot

这是一个系统调用,主要功能是改变当前的root文件系统,是吧整个系统切换到一个新的root中,移除对之前root的依赖具体原理是把当前进程root文件系统移动到old文件夹中,使new_root成为新的root文件系统. func pivotRoot(root string) error { /** 为了使当前root的老 root 和新 root 不在同一个文件系统下,我们把root重新mount了一次 bind mount是把相同的内容换了一个挂载点的挂载方法 */ 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) } // 创建 rootfs/.pivot_root 存储 old_root pivotDir := filepath.Join(root, ".pivot_root") if err := os.Mkdir(pivotDir, 0777); err != nil { return err } // pivot_root 到新的rootfs, 现在老的 old_root 是挂载在rootfs/.pivot_root // 挂载点现在依然可以在mount命令中看到 // 把当前文件系统移动到pivotDir,然后呢当前进程的root就作为新的root文件系统 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") // umount rootfs/.pivot_root // 卸载原来的root if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil { return fmt.Errorf("unmount pivot_root dir %v", err) } // 删除临时文件夹 return os.Remove(pivotDir) }

挂载proc之前,先调用pivotRoot,把当前文件系统切换为pwd

func setUpMount() { pwd, err := os.Getwd() if err != nil { log.Errorf("Get current location error %v", err) return } log.Infof("Current location is %s", pwd) pivotRoot(pwd) syscall.Mount("", "/", "", syscall.MS_PRIVATE | syscall.MS_REC, "") //mount proc defaultMountFlags := syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV syscall.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), "") syscall.Mount("tmpfs", "/dev", "tmpfs", syscall.MS_NOSUID|syscall.MS_STRICTATIME, "mode=755") }

记得在NewParentProcess函数的cmd那里加一句

cmd.Dir = "/root/busybox"

检验

./mydocker run -it /bin/sh pwd 看现在文件系统到底是不是/root/busybox

用AUFS包装busybox

本节的目的是进一步进行容器与镜像的隔离.使得在容器内对文件系统的操作不会影响到镜像

上一节我们通过pivot_root实现了把init初始进程的文件系统换为宿主机的/root/busybox目录,但问题是,在容器内对文件系统进行修改的时候,宿主机下的目录也会发生改变,达不到隔离效果.

alex@alex:~/writeDocker$ sudo ./writeDocker run -ti sh [sudo] password for alex: {"level":"info","msg":"init come on","time":"2019-10-01T14:26:00Z"} {"level":"info","msg":"command all is sh","time":"2019-10-01T14:26:00Z"} {"level":"info","msg":"Current location is /root/busybox","time":"2019-10-01T14:26:00Z"} {"level":"info","msg":"Find path /bin/sh","time":"2019-10-01T14:26:00Z"} # ls bin dev etc home proc root sys tmp usr var # mkdir alex # ls alex bin dev etc home proc root sys tmp usr var # exit sh: 7: Cannot set tty process group (No such process) alex@alex:~/writeDocker$ sudo su root@alex:/home/alex/writeDocker# cd /root/busybox root@alex:~/busybox# ls alex bin dev etc home proc root sys tmp usr var

其实root/busybox就是容器的镜像层,如果多个容器的共享一个镜像层,会造成容器之间互相看到对方的文件.我们使用AUFS来解决这个问题.

AUFS

AUFS是一种联合挂载文件系统. 只可读的busybox作为镜像层,然后创建一个可读写的writeLayer作为容器层,所有容器的操作都会发生在容器层. 然后创建mnt文件夹作为挂载点,

检验

在NewParentProcess中添加这几句 mntURL := "/aufs/mnt" rootURL := "/aufs/" NewWorkSpace(rootURL, mntURL) cmd.Dir = mntURL

设当前根目录为/aufs 然后在容器内创建一个test.txt,再看看writeLayer和busybox文件夹有什么变化

# touch test.txt # ls bin dev etc home proc root sys test.txt tmp usr var root@alex:/aufs# ls busybox busybox.tar mnt writeLayer root@alex:/aufs# cd busybox root@alex:/aufs/busybox# ls bin dev etc home proc root sys tmp usr var root@alex:/aufs/busybox# cd ../writeLayer root@alex:/aufs/writeLayer# ls test.txt root@alex:/aufs/writeLayer# cd ../mnt root@alex:/aufs/mnt# ls bin dev etc home proc root sys test.txt tmp usr var

可以看到busybox作为只读层,(镜像层),其实是不会变化的,而在读写层的writeLayer,就会保存着修改/新建的文件. 那么实际上容器的文件系统是挂载在/mnt这个文件夹里面的,这个文件夹使用AUFS联合挂载了/writeLayer与/busybox.

然后在退出容器的时候,会删除mnt与writeLayer这两个中间文件夹.而作为镜像层的busybox不会发生改变

Docker run详细流程

ref

https://www.jianshu.com/p/ecbdcc98db76


最新回复(0)