### Docker exec的原理 - 一个进程的Namespace信息在宿主机上是确确实实存在的,并且是以一个文件的方式存在 - 一个进程,可以选择加入到某个进程已有的Namespace中,从而达到"进入这个进程所在容器的目的" ### Docker commit原理 - 实际上就是在容器运行起来后,把最上层的"可读写层",加上原先容器镜像的只读层 - 打包成了一个新的镜像.并且只读层在宿主机是共享的,不会占用额外的空间 - 由于使用了联合文件系统,你在容器里镜像rootfs所做的任何修改, - 都会被操作系统复制到这个读写层,然后再修改,这就是所谓的Copy-on-Write - Init层的存在,就是为了避免执行docker commit时, - 把Docker自己对/etc/hosts等文件的修改,也一起提交掉 ### 容器的volume docker run -v /test ... - Docker默认会在宿主机上创建一个临时目录/var/lib/docker/volumes/[VOLUME_ID]/_data - 然后把它挂载到容器的/test目录上 docker run -v /home:/test ... - Docker直接把宿主机的/home目录挂载到容器的/test目录上 Docker是如何将宿主机目录挂载到容器的 - 只需要在容器的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的进程 ### 挂载机制 - Docker中的挂载,使用的是Linux的绑定挂载(Bind Mount)机制 - 主要作用是运行你将一个目录或者文件,而不是整个设备,挂载到一个指定的目录上 - 原挂载点的内容则会被隐藏起来且不受影响 - Bind Mount实际上是一个inode替换的过程 ### Bind Mount的本质  - mount --bind /home /test 会将/home 挂载到/test上 - 实际上相当于/test的dentry,重定向到了/home的inode - 当我们修改/test目录时,实际上就是修改的是/home目录的inode - 这就是为何一旦执行umount命令,/test目录原先的内容就会恢复 ### /test目录挂载在容器的可读写层,会不会被docker commit提交掉呢 - docker commit是发生在宿主机空间的 - Mount Namespace的隔离作用,宿主机并不知道这个绑定挂载的存在 - 在宿主机看来,容器中可读写层的/test目录(/var/lib/docker/aufs/mnt/[可读写层ID]/test)始终是空的 - 由于Docker一开始还是要创建这个/test目录作为挂载点,执行完docker commit之后 - 新镜像中,会多出来一个空的/test目录,因为新建目录操作不是挂载操作,Mount Namespace不能起到"障眼法"的作用 Last modification:August 5th, 2019 at 05:09 pm © 允许规范转载 Support 如果觉得我的文章对你有用 ×Close Appreciate the author Sweeping payments