本节所要实现的功能是改变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
本文详细介绍了如何通过pivotRoot系统调用和AUFS联合文件系统实现Docker容器的文件系统与镜像隔离。通过挂载proc前调用pivotRoot改变根文件系统,并使用AUFS将只读镜像层与可读写容器层结合,确保容器内部操作不影响镜像。同时,阐述了Docker run的执行流程。

被折叠的 条评论
为什么被折叠?



