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

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本节所要实现的功能是改变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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值