Docker数据卷

Docker的镜像是由一系列的只读层组合而来的,当启动一个容器时,Docker加载镜像的所有只读层,并在最上层加入一个读写层。这个设计使得Docker可以提高镜像构建、存储和分发的效率,节省了时间和存储空间,然而也存在如下问题。
❏ 容器中的文件在宿主机上存在形式复杂,不能在宿主机上很方便地对容器中的文件进行访问。
❏ 多个容器之间的数据无法共享。
❏ 当删除容器时,容器产生的数据将丢失。
为了解决这些问题,Docker引入了数据卷(volume)机制。volume是存在于一个或多个容器中的特定文件或文件夹,这个目录以独立于联合文件系统的形式在宿主机中存在,并为数据的共享与持久化提供以下便利。

❏ volume在容器创建时就会初始化,在容器运行时就可以使用其中的文件。
❏ volume能在不同的容器之间共享和重用。
❏ 对volume中数据的操作会马上生效。
❏ 对volume中数据的操作不会影响到镜像本身。
❏ volume的生存周期独立于容器的生存周期,即使删除容器,volume仍然会存在,没有任何容器使用的volume也不会被Docker删除。
Docker提供了volumedriver接口,通过实现该接口,我们可以为Docker容器提供不同的volume存储支持。当前官方默认实现了local这种volumedriver,它使用宿主机的文件系统为Docker容器提供volume。本节接下来的讨论都将默认针对local这种volumedriver。

  1. 数据卷的使用方式
    为容器添加volume,类似于Linux的mount操作,用户将一个文件夹作为volume挂载到容器上,可以很方便地将数据添加到容器中供其中的进程使用。多个容器可以共享同一个volume,为不同容器之间的数据共享提供了便利。
    用户可以使用docker volume create创建一个volume,以下命令创建了一个指定名字的volume。
sudo docker volume  create  --name  vol_simple

说明 Docker当前并未对volume的大小提供配额管理,用户在创建volume时也无法指定volume的大小。在用户使用Docker创建volume时,由于采用的是默认的local volumedriver,所以volume的文件系统默认使用宿主机的文件系统,如果用户需要创建其他文件系统的volume,则需要使用其他的volumedriver。

用户在使用docker run或docker create创建新容器时,也可以使用-v标签为容器添加volume,以下命令创建了一个随机名字的volume,并挂载到容器中的/data目录下。

sudo docker  run  -d  -v  /data    ubuntu  /bin/bash

以下命令创建了一个指定名字的volume,并挂载到容器中的/data目录下。

sudo docker  run  -d  -v  vol_simple:/data    ubuntu  /bin/bash

Docker在创建volume的时候会在宿主机/var/lib/docker/volume/中创建一个以volume ID为名的目录,并将volume中的内容存储在名为_data的目录下。使用docker volume inspect命令可以获得该volume包括其在宿主机中该文件夹的位置等信息。

  1. 挂载volume

用户在使用docker run或docker create创建新容器时,可以使用-v标签为容器添加volume。用户可以将自行创建或者由Docker创建的volume挂载到容器中,也可以将宿主机上的目录或者文件作为volume挂载到容器中。下面分别介绍这两种挂载方式。用户可以使用如下命令创建volume,并将其创建的volume挂载到容器中的/data目录下。

sudo docker  run it  -v  /host/dir:/container/dir    ubuntu  /bin/bash

使用以上命令将宿主机中的/host/dir文件夹作为一个volume挂载到容器中的/container/dir。文件夹必须使用绝对路径,如果宿主机中不存在/host/dir,将创建一个空文件夹。在/host/dir文件夹中的所有文件或文件夹可以在容器的/container/dir文件夹下被访问。如果镜像中原本存在/container/dir文件夹,该文件夹下原有的内容将被隐藏,以保持与宿主机中的文件夹一致。

sudo docker  run it  --name vol_read_only   -v  /host/dir:/container/dir:ro   ubuntu  /bin/bash

将主机上的文件或文件夹作为volume挂载时,可以使用:ro指定该volume为只读。

  1. 使用Dockerfile添加volume

在使用docker build命令生成镜像并且以该镜像启动容器时会挂载一个volume到/data。与上文中vol_simple例子类似,如果镜像中存在/data文件夹,这个文件夹中的内容将全部被复制到宿主机中对应的文件夹中,并且根据容器中的文件设置合适的权限和所有者。

VOLUME /data

与使用docker run -v不同的是,VOLUME指令不能挂载主机中指定的文件夹。这是为了保证Dockerfile的可移植性,因为不能保证所有的宿主机都有对应的文件夹。需要注意的是,在Dockerfile中使用VOLUME指令之后的代码,如果尝试对这个volume进行修改,这些修改都不会生效。在下面的例子中,在创建volume后,尝试在其中添加一些初始化的文件并改变文件所有权。

FROM ubuntu
RUN useradd foo
VOLUME /data
RUN touch /data/file
RUN chown -R  foo:foo /data

通过这个Dockerfile创建镜像并启动容器后,该容器中存在用户foo,并且能看到在/data挂载的volume,但是/data文件夹内并没有文件file,更别说file的所有者并没有被改变为foo。这是由于Dockerfile中除了FROM指令的每一行都是基于上一行生成的临时镜像运行一个容器,执行一条指令并执行类似docker commit的命令得到一个新的镜像,这条类似docker commit的命令不会对挂载的volume进行保存。所以上面的Dockerfile最后两行执行时,都会在一个临时的容器上挂载/data,并对这个临时的volume进行操作,但是这一行指令执行并提交后,这个临时的volume没有被保存,我们通过最后生成的镜像创建的容器所挂载的volume是没有操作过的。