1. docker exec 如何是怎么做到进入一个容器的?

容器是一个特殊的进程。Namespace是起到对进程的隔离作用,这种隔离作用虽然对进程来说是无法感知的,但是确实以文件的形式存在宿主机中。

首先通过docker命令查看目标容器的进程ID。

  1. docker inspect --format '{{ .State.Pid }}' <容器ID>

然后通过得到的进程号,去宿主机的proc文件中找到对应进程的Namespace文件。

  1. ls -l /proc/<进程ID>/ns

所以想要进入一个容器,其实只需要使得一个进程加入到目标进程的Namespace就可以实现进入到目标容器的目的,这就是docker exec的实现原理。

在Linux系统中有一个setns()的系统调用可以实现这个目的,即:设置当前进程的Namespace。
在Docker中则有其他的参数可以做到设置容器的Namespace,例如 --net可以设置一个容器的Network Namespace

  1. ## 新建容器,并加入到ID=4ddf4638572d的容器Network Namespace中。
  2. docker run -it --net container:4ddf4638572d busybox ifconfig

2. Volume(数据卷)的作用是什么?

容器技术使用了 rootfs 机制和 Mount Namespace,使得容器可以在一个完全隔离的文件系统环境中运行。但是如果容器又想要访问到宿主机的文件要怎么做呢?这就是Volume存在的原因。

Docker Volume可以允许你将宿主机上指定的目录或者文件,挂载到容器里面进行读取和修改操作。

3. Docker Volume有几种声明方式?

  1. docker run -v /test ...
  2. docker run -v /home:/test ...

两种方式都是把宿主机的目录挂载到容器的 /test目录下。
区别是:

  • 第一种方式会先在宿主机创建临时目录 /var/lib/docker/volumes/[VOLUME_ID]/_data,再把临时目录挂载到容器的 /test目录上。
  • 第二种方式会把宿主机中的 /home目录挂载到容器的 /test目录上。

    4. Volume(数据卷)的原理是什么?

    容器的启动过程是需要经过 rootfs 准备和chroot 执行,只有在chroot执行后,容器所看的到文件环境才是完全隔离的文件系统环境。所以Volume,只需要选择在 rootfs准备好之后,并且在chroot执行之前的时机去完成挂载即可。
    并且由于Mount Namespace存在,宿主机也无法感知到这个挂载点的存在。

    5. 挂载的数据卷能否被docker commit 提交?

    不会被提交。

原因:
挂载的目录虽然在可读写层(/var/lib/docker/aufs/mnt/[可读写层 ID]/)中,但是实际操作的目录是在宿主机中,可读写层中的挂载目录实际是一个只有目录名的空目录。
只有进入到容器中,得知这个Volume的挂载信息,才能找到对应的挂载目录。
docker commit是在容器进程外执行,由于 Mount Namespace 的隔离作用,宿主机不能得知这个Volume的存在,所以即使挂载的数据卷在可读写层,也不会被提交,即:提交的是一个空的目录。

那为什么会有一个空的目录呢?因为容器需要先创建一个目录才能完成数据卷的挂载,而这个目录是在可读写层真实存在的。