1. Volume原理

想要了解Docker Volume,首先我们需要知道Docker的文件系统是如何工作的。Docker镜像是由多个文件系统(只读层)叠加而成。当我们启动一个容器的时候,Docker会加载只读镜像层并在其上(译者注:镜像栈顶部)添加一个读写层。如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏。当删除Docker容器,并通过该镜像重新启动时,之前的更改将会丢失。在Docker中,只读层及在顶部的读写层的组合被称为Union File System(联合文件系统)。

 

为了能够保存(持久化)数据以及共享容器间的数据,Docker提出了Volume的概念。简单来说,Volume就是目录或者文件,它可以绕过默认的联合文件系统,而以正常的文件或者目录的形式存在于宿主机上。

 

Docker管理数据的方式有两种:

 

数据卷

 

数据卷是一个特殊的文件或者目录,它将宿主机文件或者目录直接映射进容器中,可供一个或多个容器使用。容器数据卷设计的目的就是为了 数据的持久化,它完全独立与容器的生命周期。因此,容器删除时,不会删除其挂载的数据卷,也不会存在类似的垃圾机制对容器存在的数据卷进行处理。通过docker run -v命令可以将数据卷挂载到对应的容器目录空间,进行文件读取,容器卷特性如下:

 

    * 数据卷可以在容器之间共享和重用,容器间传递数据将变得高效方便

 

    * 对数据卷内数据的修改会立马生效,无论是容器内操作还是本地操作

 

    * 对数据卷的更新不会影响镜像,解耦了应用和数据

 

    * 卷会一直存在,直到没有容器使用,可以安全地卸载它


数据卷容器

 

命名的容器已挂载数据卷,其他的容器通过挂载这个容器(父容器)实现数据共享,挂载数据卷的容器,称为数据卷容器。通过数据卷容器可以实现容器间的数据共享。

 

例如:已经存在一个挂载了数据卷的容器,如果存在其他容器通过docker run --volumes-from [容器别名]命令挂载到该容器上,则该容器可以被称之为数据卷容器,其主要功能是提供数据卷供其他容器挂载。

 

当数据卷容器宕机后,并不会造成数据卷的回收卸载,数据卷会继续挂载在其他容器中。当全部挂载该数据卷的容器全部宕机后,该数据卷才会卸载。

 

2. 容器数据卷方式

2.1 docker命令方式

2.1.1 匿名目录挂载

匿名目录挂载只需要写容器内目录或者文件即可,而宿主机对应的目录会在/var/lib/docker/volumes路径下生成:

 

//以交互模式运行容器,并使用-v 匿名挂载容器数据卷

docker run -it -v 容器内目录/文件的绝对路径[:rw/ro] -p 主机端口:容器端口 --name=容器名称 镜像ID/镜像名称[:版本号]

 

//以后台方式运行容器,并使用-v 匿名挂载容器数据卷 (推荐)

docker run -d -v 容器内目录/文件的绝对路径[:rw/ro] -p 主机端口:容器端口 --name=容器名称 镜像ID/镜像名称[:版本号]

 

//注意:如果出现Docker挂载宿主机目录显示cannot open directory .:Permission denied

解决办法:在挂载目录后面 多加一个--privileged=true参数即可


案例:

 

使用centos容器内的 根目录 (/) 下的centosVolume目录,匿名挂载到宿主机中

 

创建并运行centos容器,同时匿名挂载数据卷

 

// 这里没有使用--name=容器名称 去指定容器名称,则docker随机一个容器名称

docker run -it -v /centosVolume centos


查看数据卷是否挂载成功

 

docker inspect 81b687fb8e88 (容器ID)

在这里插入图片描述

 

可以看到,centos容器内的/centosVolume数据卷挂载到宿主机/var/lib/docker/volumes路径下的/0a4ea838c43ccc9af377af6d2e641b2c9fd4977c4a56408d76d31c8719b9dd8f/_data目录了。

 

2.1.2 具名目录挂载

具名目录挂载相对于匿名目录挂载,就是在宿主机生成对应的目录时可以指定该目录的名称,同样目录也会在/var/lib/docker/volumes路径下生成。例如:匿名目录挂载生成的对应宿主机目录为0a4ea838c43ccc9af377af6d2e641b2c9fd4977c4a56408d76d31c8719b9dd8f一串随机的数字,而具名目录挂载就是可以将这一串随机的数字改成指定的目录名称。

 

//以交互模式运行容器,并使用-v 具名挂载容器数据卷

docker run -it -v 目录名称:容器内目录/文件的绝对路径[:rw/ro] -p 主机端口:容器端口 --name=容器名称 镜像ID/镜像名称[:版本号]

 

//以后台方式运行容器,并使用-v 具名挂载容器数据卷 (推荐)

docker run -d -v 目录名称:容器内目录/文件的绝对路径[:rw/ro] -p 主机端口:容器端口 --name=容器名称 镜像ID/镜像名称[:版本号]

 

//注意:如果出现Docker挂载宿主机目录显示cannot open directory .:Permission denied

解决办法:在挂载目录后面 多加一个--privileged=true参数即可


案例:

 

使用centos容器内的 根目录 (/) 下的centosVolume目录具名挂载到宿主机中

 

创建并运行centos容器,同时具名挂载数据卷

 

//这里没有使用--name=容器名称 去指定容器名称,则docker随机一个容器名称

docker run -it -v hostVolume:/myVolume centos


查看数据卷是否挂载成功

 

docker inspect 8961c0a39ce8 (容器ID)


在这里插入图片描述

 

可以看到,centos容器内的/myVolume数据卷挂载到宿主机的/var/lib/docker/volumes/hostVolume/_data目录了,这时的宿主机目录名称不在是一串随机的数字了,而是我们指定的目录名称hostVolume。

 

2.1.3 指定目录挂载

指定目录挂载就是我们可以将容器内部的数据卷指定挂载到宿主机的某一文件或者目录下

 

//以交互模式运行容器,并使用-v 挂载容器数据卷

docker run -it -v 宿主机目录/文件的绝对路径:容器内目录/文件的绝对路径[:rw/ro] -p 主机端口:容器端口 --name=容器名称 镜像ID/镜像名称[:版本号]

 

//以后台方式运行容器,并使用-v 挂载容器数据卷 (推荐)

docker run -d -v 宿主机目录/文件的绝对路径:容器内目录/文件的绝对路径[:rw/ro] -p 主机端口:容器端口 --name=容器名称 镜像ID/镜像名称[:版本号]

 

//注意:如果出现Docker挂载宿主机目录显示cannot open directory .:Permission denied

解决办法:在挂载目录后面 多加一个--privileged=true参数即可


案例:

 

将宿主机的 /root/hostVolume目录挂载到 centos容器内的根目录 (/) 下的containerVolume目录

 

创建并运行centos容器,同时挂载数据卷

 

//这里没有使用--name=容器名称 去指定容器名称,则docker随机一个容器名称

docker run -it -v /root/hostVolume:/containerVolume centos

1

2

1

2

查看数据卷是否挂载成功

在这里插入图片描述

 

可以看到centos容器的根目录下多了一个containerVolume目录,宿主机的 /root目录下多了一个hostVolume目录,同时还可以在宿主机中使用指令docker inspect 容器ID/容器名称来查看是否挂载成功。

 

在这里插入图片描述

 

容器与宿主机之间进行通信

在这里插入图片描述

 

关闭容器后,更改宿主机hostVolume目录中的 host.txt文件,容器再次启动后数据仍然同步

在这里插入图片描述

 

2.2 dockerFile方式

案例:

 

在宿主机的 /root目录下创建一个DockerFile文件 (名称随意),并通过docker build指令生成镜像来添加数据卷

 

在宿主机的 /root目录下创建一个DockerFile文件,并添加如下内容到文件中

 

#基于centos镜像进行构建

FROM centos

 

#数据卷只能指定容器数据卷,不能指定宿主机数据卷,英文并不能够保证在所有的宿主机上都存在这样的特定目录。

VOLUME ["/dataVolumeContainer1","/dataVolumeContainer2"]

 

#以 /bin/bash方式启动

CMD /bin/bash


使用如下指令,把编写的DockerFile文件执行生成镜像,注意:命令最后面是空格 + .

 

docker build -f 宿主机中DockerFile文件的绝对路径 -t 新镜像名称[:版本号] .


在这里插入图片描述

 

运行我们生成的new-centos镜像,就能够查看到在容器内中生成的数据卷

 

在这里插入图片描述

 

那么容器内的数据卷文件/目录地址已经知道,对应的宿主机文件/目录的地址怎么查看?通过如下指令

 

docker inspect 容器ID/容器名称


在这里插入图片描述

 

3. 数据卷容器方式

添加数据卷容器

 

docker run -it/-d -p 主机端口:容器端口 --name=容器名称 ----volumes-from 数据卷容器ID/数据卷容器名称 生成数据卷容器的镜像ID/镜像名称[:版本号]


案例:

 

创建父容器,并在父容器的数据卷中添加数据,以挂载父容器生成子容器,实现数据共享

 

启动父容器,并在父容器的 dataVolumeContainer1目录下新增内容

 

在这里插入图片描述

 

基于父容器生成子容器son-centos,注意是在宿主机中使用如下指令

 

docker run -it --name=son-centos --volumes-from father-centos new-centos


在这里插入图片描述

 

子容器添加数据,父容器查看数据

 

在这里插入图片描述

 

删除父容器,子容器数据依然保留

 

在这里插入图片描述

 

4. 参考

https://zhuanlan.zhihu.com/p/35493900