1  概述

容器的数据存放在最上层的读写层。

Docker镜像由多个只读层叠加而成,启动容器时,docker会加载只读镜像层并在镜像栈顶部添加一个读写层

如果运行的容器修改了现有的一个已经存在的文件,那该文件就会从读写层下面的只读层复制到读写层,该文件的只读版本依然存在,只是已经被读写层中该文件的副本所隐藏,为“写时复制”机制

联合挂载解决了镜像分发问题,但是导致性能损耗,

关闭并重启容器,其数据不受影响;但删除Docker容器,则其更改将会全部丢失 

存在以下三个问题 

.存储于联合文件系统中,不易于宿主机访问; 

.容器间数据共享不便

 .删除容器其数据会丢失 

解决方案:使用“卷(volume)” ,卷是容器上的一个或者多个“目录”,此类目录可绕过联合文件系统,与宿主机的某个目录“绑定”,使得数据脱离容器而存在,容器删除后,数据依然存在。

每一个挂载点称为卷,这里的挂载是用来挂载目录的(bind,目录挂目录)。

一个卷用来绑定一个目录,一个容器可以定义多个卷

卷关联宿主机上的某一路径,卷和联合挂载不一样,优于联合挂载

卷实现了数据的持久化

卷为docker提供了独立于容器的数据管理机制 

.可以把 “镜像 ”想像成静态文件,例如 “程序 ”,把卷类比为动态内容,例如 “数据 ”;于是,镜像可以重用,而卷可以共享;

.卷实现了 “程序 (镜像 )”和“数据 (卷)”分离,以及 “程序 (镜像 )”和“制作镜像的主机 ”分离,用户制作镜像时无须再考虑镜像运行的容器所在的主机的环境;

2  卷的类型

卷有两种类型:

Docker有两种类型的卷,每种类型都在容器中存在一个挂载点,但其在宿主机上的位置有所不同;

1:绑定挂载卷:由用户指定宿主机的路径,需要人工创建好宿主机的目录。

2docker管理的卷:docker路径不需要创建,docker进程会自动生成该路径。

-f后是json格式的字典,查看分段格式的标志

docker run命令使用-v选项即可使用Volume指定-v选项指定卷路径,docker run命令使用-v选项即可使用Volume

2.1  docker管理的卷

例子如下:

 docker run -it  --name bbox1 –v /data busybox

查看 bbox1容器的卷、卷标识符及挂载的主机目录

 docker inspect -f {{.Config.Volumes}}{{.Mounts}} bbox1

可以用docker volume ls查看卷的名称,在用docker volum inspect 卷名称  来查看具体卷的信息

docker volume inspect  737432382e1292b2aa828f6e66f04721bf251dbeaa082b295f5bc08bf5ac6c60

找到的路径为本地的宿主机的路径

这个方式,可以直接在宿主机部署站点,直接把站点放到容器对应的目录本地。

2.2  绑定挂载卷例子

格式

docker run -it -v HOSTDIR:VOLUMEDIR --name sunny02 busybox

以下HOSTDIR:VOLUMEDIR表示宿主机路径:卷路径,因此宿主机要先创建对应要挂载的目录

如这里创建宿主机目录如下

 mkdir -pv /docker/data/volumes/sunny

启动容器

docker run -it --name sunny02 -v /docker/data/volumes/sunny:/data/htdocs --rm busybox:1.27.2
docker inspect -f {{.Mounts}} sunny02

结果如下

[{bind  /docker/data/volumes/sunny /data/htdocs   true rprivate}]

bind表示绑定挂载卷

此时,路径就是宿主机的/docker/data/volumes/sunny

如果路径不需要管理就使用docker生成的卷,如果需要管理,就用绑定挂载卷

这里,如果启动的镜像本来就存在/data/htdocs,即绑定挂载点本身有数据,新容器的挂载点启动后,挂载目录也是/data/htdocs,这个时候如果再次挂载到这个目录,则原来镜像里的/data/htdocs数据不可见,已经被隐藏

例子如下

首先先制作一个镜像

先启动容器,并创建/data/htdocs/data/htdocs目录下生成hello文件

[root@docker ~]# docker run -it --name sunny02 --rm busybox:1.27.2
/ # ls
bin   dev   etc   home  proc  root  sys   tmp   usr   var
/ # mkdir /data/htdocs
mkdir: can't create directory '/data/htdocs': No such file or directory
/ # mkdir -p /data/htdocs
/ # cd /data/htdocs/
/data/htdocs # ls
/data/htdocs # echo "hello sunny" > hello
/data/htdocs # ls
hello
/data/htdocs #

环境准备好了,制作成镜像

-a指明作者,-m备注,-p暂停容器时制作镜像,即镜像为当前的容器状态,testbox:sunnyv1为当前标签,-c改变容器默认的启动命令

docker commit -a "sunny <sunny@ghbsunny.cn>" -m "sunny test" -p sunny02 testbox:sunnyv1

基于镜像testbox:sunnyv1来启动容器

先指定挂载点,此时容器有/data/htdocs这个目录,且文件hello存在

[root@docker ~]# docker run -it --name sunny03 --rm testbox:sunnyv1
/ # ls
bin   data  dev   etc   home  proc  root  sys   tmp   usr   var
/ # cd /data/
/data # ls
htdocs
/data # cd htdocs/
/data/htdocs # ls
hello
/data/htdocs #

然后,指定挂载点,做对比区别

先创建宿主机目录

mkdir -pv /docker/data/volumes/sunny02

指定挂载点后启动容器

[root@docker ~]# docker run -it --name sunny02 -v /docker/data/volumes/sunny02:/data/htdocs --rm testbox:sunnyv1
/ # ls
bin   data  dev   etc   home  proc  root  sys   tmp   usr   var
/ # cd /data/
/data # ls
htdocs
/data # cd htdocs/
/data/htdocs # ls
/data/htdocs #

如果宿主机的挂载目录/docker/data/volumes/sunny02有文件,则挂载后的容器依然是可以看到/docker/data/volumes/sunny02的数据,但是原来镜像 testbox:sunnyv1的/data/htdocs下的文件已经不可见,被隐藏了,和路径挂载一样。因此挂载时,要注意镜像的路径,不要隐藏掉镜像需要使用的目录数据

3  共享卷

有两种方式可以在容器间共享卷:

1..多个容器的卷使用同一个主机目录,例如,以下两个容器都使用宿主机的目录 /docker/volumes/v1作为数据盘

 docker run –it --name c1 -v /docker/volumes/v1:/data busybox
 docker run –it --name c2 -v /docker/volumes/v1:/data busybox

 .复制使用其它容器的卷,为 docker run命令使用 --volumes-from选项

如下例子,先启动了容器 bbox1,挂载本地 /docker/volumes/v1作为卷目录,再启动容器bbox2,直接复制bbox1的卷作为其容器的卷

docker run -it --name bbox1 -v /docker/volumes/v1:/data busybox
docker run -it --name bbox2  --volumes-from bbox1 busybox

复制卷,有一个好处是实现共享,有个场景为,假设要挂载的卷很多,新生成的容器要一一挂载相同的宿主机目录,很麻烦,而且挂载路径可能出错,因此可以启动一个容器,不跑程序,仅用于挂载卷,相当于模板,后期要使用这个卷的数据的容器,都可以通过  --volumes-from来直接挂载对应的卷,保证卷的数据和目录都是一致的。这个容器

为基础架构容器,不做其他操作,仅提供卷的复制

一个容器可以使用多个卷,只需要用多个-v来指定即可

如下例子,同时指定两个卷/docker/data/volumes/sunny02 /docker/data/volumes/sunny

[root@docker volumes]# docker run -it --name c6 -v /docker/data/volumes/sunny:/data/htdocs -v /docker/data/volumes/sunny02:/data/html busybox
/ # ls
bin   data  dev   etc   home  proc  root  sys   tmp   usr   var
/ # cd data
/data # ls
htdocs  html

这个也是为什么,复制卷的时候,要用参数 --volumes-from,因为可能存在多个卷挂载的情况

注意,删除容器默认不会删除相关的卷 ,生产环境,建议不要删除卷,因为一旦把卷删除,相关数据也会丢失。