数据卷

  • 1、考虑问题
  • 2、volume机制
  • 2.1 声明方式
  • 2.2 挂载机制
  • 3、额外知识(copyData)


1、考虑问题

  • 1 宿主机文件怎么让容器访问到
  • 2 容器内文件怎么让宿主机访问到

2、volume机制

  • 它允许容器将宿主机指定的目录或文件挂载到容器内进行读取和修改

2.1 声明方式

#在第一种情况下,由于你并没有显示声明宿主机目录,
#那么 Docker 就会默认在宿主机上创建一个临时目录 /var/lib/docker/volumes/[VOLUME_ID]/_data,
#然后把它挂载到容器的 /test 目录上。
#而在第二种情况下,Docker 就直接把宿主机的 /home 目录挂载到容器的 /test 目录上。
$ docker run -v /test ...
$ docker run -v /home:/test ...

2.2 挂载机制

  • 镜像的各个层,保存在 /var/lib/docker/aufs/diff 目录下,在容器进程启动后,它们会被联合挂载在 /var/lib/docker/aufs/mnt/ 目录中,这样容器所需的 rootfs 就准备好了。
  • 所以,我们只需要在 rootfs 准备好之后,在执行 chroot 之前,把 Volume 指定的宿主机目录(比如 /home 目录),挂载到指定的容器目录(比如 /test 目录)在宿主机上对应的目录(即 /var/lib/docker/aufs/mnt/[可读写层 ID]/test)上,这个 Volume 的挂载工作就完成了。
  • 更重要的是,由于执行这个挂载操作时,“容器进程”已经创建了,也就意味着此时 Mount Namespace 已经开启了。所以,这个挂载事件只在这个容器里可见。你在宿主机上,是看不见容器内部的这个挂载点的。这就保证了容器的隔离性不会被 Volume 打破。
  • 这里提到的"容器进程",是 Docker 创建的一个容器初始化进程 (dockerinit),而不是应用进程 (ENTRYPOINT + CMD)。dockerinit 会负责完成根目录的准备、挂载设备和目录、配置 hostname 等一系列需要在容器内进行的初始化操作。最后,它通过 execv() 系统调用,让应用进程取代自己,成为容器里的 PID=1 的进程。
  • 上面的使用的是一种绑定挂载(bind mount)的技术,它的主要作用就是,允许你将一个目录或者文件,而不是整个设备,挂载到一个指定的目录上。并且,这时你在该挂载点上进行的任何操作,只是发生在被挂载的目录或者文件上,而原挂载点的内容则会被隐藏起来且不受影响。核心实现就是通过修改inode的目录项(也可以称为指针)(destry),也可以说成是inode替换的过程,如下图。

    这样就可以实现将宿主机的目录挂载到容器里面。

3、额外知识(copyData)

  • 如果你执行 docker run -v /home:/test 的时候,容器镜像里的 /test 目录下本来就有内容的话,你会发现,在宿主机的 /home 目录下,也会出现这些内容。这是怎么回事?为什么它们没有被绑定挂载隐藏起来呢?(提示:Docker 的“copyData”功能)
  • copyData(docker的copyData功能)可以使得挂载点和源宿主机的目录下的文件同时存在。