>> 什么是数据卷
Data Volume 数据卷:是可以存放在一个或多个容器内的特定的目录,提供独立于容器之外的持久化存储;是经过特殊设计的目录,可以绕过联合文件系统(UFS),为一个或多个容器提供访问;
不使用volume的时候,对容器进行的改动是不会被保存的,使用volume可以实现持久化存储;比如运行一个数据库的操作,数据库的一个容器,数据库的数据应该被持久化存储的,volume就可以实现这个,并且volume可以提供容器与容器之间的共享的数据;
Docker的理念之一:就是将其应用与其运行的环境打包,因此,通常Docker容器的生存周期,都是与在容器中运行的程序相一致的,而我们对数据的要求通常是持久化的;另一方面,docker容器之间也需要有一个共享数据的渠道,而这些需求就催生出了docker数据卷的产生;
数据卷的设计的目的:在于数据的永久化,它完全独立于容器的生存周期,因此,Docker不会在容器删除时删除其挂载的数据卷,也不会存在类似垃圾收集机制,对容器引用的数据卷进行处理了;
数据卷特点:
- 1、Docker数据卷是独立于Docker的存在,它存在于Docker host(宿主机)中,因此,它与容器的生存周期是分离的;
- 2、Docker数据卷本质上是存在于Docker宿主机的本地文件系统中;
- 3、Docker数据卷可以是目录也可以是文件;
- 4、Docker容器可以利用数据卷的技术与宿主机进行数据共享;
- 5、同一个目录或者文件,可以支持多个容器进行访问,这样其实实现了容器间的数据的共享和交换;
- 6、数据卷是在容器启动时进行初始化的,那么如果容器使用的镜像包含了的数据也会在容器启动时拷贝到容器的数据卷中;
- 7、
数据卷可以在容器之间共享和重用
; - 8、
对数据卷的修改会立马生效
;容器可以对数据卷里的内容直接进行修改;容器对数据卷进行的修改是及时进行的,所有的修改都会直接体现在数据卷中; - 9、
对数据卷的更新不会影响镜像
;因为文件不会写到镜像中去,数据卷是独立于联合文件系统的,而镜像本身基于联合文件系统,so镜像与数据卷之间不会有相互影响的情况; - 10、
数据卷会一直存在,即使挂载数据卷的容器已经被删除
;因为数据卷本质上是宿主机上的一个目录,同时为了提供数据的永久化,它的生存周期与容器是完全隔离的;
Docker容器中的数据操作是经过了UFS的,UFS会再在宿主机中写一次文件,这个文件在宿主机上是临时的,这时候就出现了重复写的情况,会影响系统的性能;此外,删除容器的时候,就没有人能够通过UFS再访问到宿主机中的文件了;
容器卷可以绕过UFS直接操作宿主机上的文件,当容器删除的时候,宿主机上的文件还在,就在指定的目录下,再重新创建容器的时候,可以指定容器继续去读取宿主机上的文件;
>> 使用Docker run命令为容器添加数据卷
docker run -v ~/datavolume:data[:ro] -i -t imagesName
: 为容器创建数据卷;
-
-v:指定数据卷本机文件系统中的目录,和在容器中映射的目录名,ro设置文件权限为只读;这个命令执行完时候会自动进入容器路径下;
-
datavolume本机文件系统中的一个目录,若不存在,则执行完run命令之后会自动在本机文件系统中创建该目录;
-
data指定在容器中访问的目录名字;
-
ro设置容器中的文件权限为只读;在指定目录映射后,再指定文件权限;数据卷设置为只读之后,不能再在数据卷目录中创建文件了,数据卷中已经存在的文件可以读取,不能改;
-
ls -l
:查看容器文件系统中的目录结构; -
touch /data/c1
:使用touch命令在容器的文件系统中的data目录下创建名叫c1的文件; -
echo "i am in container" > /data/c1
:使用echo命令输出一个字符串; -
exit
:退出容器,回到本机文件系统 -
ls -l
:在本机上查看本机文件系统,可以看到datavolume文件夹; -
ls -l datacolumn
:查看文件夹的内容,可以看到刚才在容器中创建的c1这个文件已经存在; -
vi datavolume/c1
:使用编辑器打开这个文件,可以看到刚才输出的字符也已经存在了; -
docker ps -l 、docker inspect imagesId
:使用inspect命令查看刚才创建的镜像,在输出的位置可以看到刚才创建的数据卷的信息;也就是说可以通过inspect命令来查看一个容器是否挂载了数据卷、以及容器中数据卷的读写权限;
>> 使用Dockerfile构建一个包含数据卷的镜像
与在docker run命令中创建的数据卷不同:在dockerfile中创建的数据卷是不能映射到已经存在的本地文件的目录中的;在镜像构建时指定的数据卷,会在容器启动时创建指定名字的数据卷,并且运行同样镜像的不同容器所创建的数据卷也是不一样的;
-
vi Dockerfile
:使用vi文本编辑器进入Dockerfile文件中添加volume指令; -
volume["/data","/data2", ...]
:使用Dockerfile构建一个包含数据卷的镜像,并通过使用这个镜像来创建包含数据卷的容器; -
docker build -t imagesName .
:构建镜像; -
docker run --name containerName1 -it imagesName
:运行一个容器containerName1 ;在容器中添加了数据卷的指令之后,容器启动时就可以不再使用-v得参数了; -
ls
:run命令后直接进入到容器目录下;查看容器的文件系统,可以看到,新创建的容器中已经挂载了在镜像中指定的两个目录; -
exit、docker inspect containerName
:查看刚刚创建的容器,可以看到在volumes里包含了两个数据卷,而这两个数据卷指定的本地的文件路径也是Docker自动创建的; -
docker run --name containerName2 -it imagesName
:若这时以同样的镜像再创建一个容器containerName2,再使用inspect命令查看新容器的数据卷地址,可以看到容器containerName2的数据卷地址与containerName1 的两个数据卷的地址是不一样的; -
也就是说,在容器启动时,我们在镜像中指定的数据卷都会进行一次完整的初始化,那么,根据镜像指定数据卷 来创建的容器所使用的数据卷就无法共享,那么,当我们不能访问到本地目录时,怎么样在容器之间共享数据呢?通过数据卷容器;
>> 数据卷容器
如果用户需要在容器之间共享一些持续更新的数据,最简单的方式是使用数据卷容器,数据卷容器其实是一个普通的容器,专门用来提供数据卷供其它容器挂载;
- 数据卷容器,与容器的数据卷的不同:
- 数据卷容器挂载了一个本地文件系统的目录,其它容器通过挂载这个数据卷容器来实现容器间的数据的共享;
>> 挂载数据卷容器的方法
-
创建一个数据卷容器dbdata,并在其中创建一个数据卷挂载到/dbdata:
docker run -it -v /home/zxj/Documents/test/datavolumn:/dbdata --name dbdata ubuntu:18.04
-
ls
:可以看到这个容器dbdata 的文件系统中已经包含了在镜像中指定的数据卷dbdata; -
创建容器,使用
--volumes-from
命令来挂载dbdata容器中的数据卷,--volumes-from
选项的值就是已经挂载了数据卷的容器的容器名字:
docker run -it --volumes-from dbdata --name db1 ubuntu:18.04
docker run -it --volumes-from dbdata --name db2 ubuntu:18.04
此时容器db1和db2都挂载同一个数据卷到相同的/dbdata目录,三个容器任何一方在该目录下的写入,其他容器都可以看到; -
touch /dbdata/c1
:在数据卷容器中,使用touch命令往数据卷中写入一个文件c1; -
ls /dbdata
:查看这个文件c1已经创建成功; -
exit
,在db1容器中ls查看,可以看到在dbdata容器中创建的文件c1; -
使用数据卷容器,就能很容易的在不同容器之间共享数据;同时,并不需要使用者确切的连接到已知的Docker宿主机的文件目录;这一点在多租户的环境下很重要,因为在这种情况下,并不想暴露服务器的实际目录;
-
docker inspect --format="{ {.volumes}}" db1
:使用inspect命令来查看新建容器的信息;使用inspect的format选项简化inspect返回的信息:指定只查看volumes的数据;
可以看到 :使用数据卷容器来挂载数据卷时,在inspect信息中,并不会直接反应数据卷容器的信息,而是直接返回了数据卷容器所挂载的数据卷的目录;db1和db2中数据卷信息是一样的;因此可以实现容器间的数据的共享; -
docker rm [-v] dbdata
:删除用来提供数据卷的数据卷容器;若加上-v参数,就是告诉容器在删除时,也删除其挂载的数据卷;但是在挂载了数据卷容器的db1、db2容器中,依然可以访问dbdata容器中挂载的数据卷;这是因为在Docker中,若一个数据卷还在被容器使用,那么它就会一直存在,同时也验证了一句话:通过数据卷容器来挂载数据卷,容器在这之间的作用,仅仅是将数据卷的挂载配置传递到挂载了数据卷容器的新容器中; -
docker restart db2
:重启容器2; -
docker attach db2
:使用attach命令来附加到容器上; -
ls
:看到db2中,数据卷的目录名还存在; -
touch dbdata/c3
:在db2中的dbdata中创建一个新文件c3; -
ls dbdata
:可以看到c1、c2、c3都存在;即使已经删除了数据卷容器,挂载了这个数据卷容器的容器仍然可以访问数据卷容器挂载的目录; 也就是说:通过数据卷容器来挂载数据卷,容器在这之间的作用,仅仅是一个数据卷配置信息的传递;
>> Docker 数据卷的备份和还原
docker run --name newContainerName --volumes-from [containerName] -v $(pwd):/backup:wr ubantu tar cvf /backup/backup.tar [container data volume]
-
--volumes-from [containerName]
:这个命令来指定需要备份的容器的名字;(数据卷容器的名字) -
-v $(pwd):/backup:权限
:使用-v命令来指定希望备份文件存放的位置;本地存放目录:容器存放目录:读写权限;(默认权限是读写) -
tar cvf /backup/backup.tar [container data volume]
:tar表示执行备份的操作是:压缩文件的命令; /backup/backup.tar是文件存放的地址, [container data volume]指定需要备份的目录; -
tar cvf 压缩;tar xvf解压缩;
在这个命令中包含了两挂载方式:
- 使用
--volumes-from
指令挂载了我们需要备份数据的容器名,–volumes-from实际上是将当前创建的容器指向参数中容器的挂载目录; -v
参数指定了我们要保存数据的路径,也可以是当前本机的一个路径;tar
:在容器运行过程中执行的是一个压缩文件tar命令,它将要备份的容器中的目录压缩到指定的目录下;
上面的命令就是Docker容器中“执行备份命令容器”所执行的操作:它将需要备份的数据容器与备份数据存放目录同时挂载在“执行备份命令的容器”上,而需要备份数据的“数据容器”它的数据实际上也是存放在挂载在本机的目录上的数据卷;数据卷容器实际上就是将本地的一个数据卷挂载到引用到数据卷容器的容器中;
实际关系是这样的:虚线的部分是实际上是通过前面命令中的–volumes-from指令实现的真实的挂载情况;
最后的结果是:“执行备份命令容器”既挂载了一个需要备份的数据卷,同时也挂载了一个备份数据存放的数据卷,那么在这个容器中,无论是执行copy操作,还是压缩操作,都能实现数据的转移;
-
docker restart db2
:重启container2容器; -
docker attach db2
:附加到container2容器上; -
ls
:查看db2的目录; -
ls dbdata
:数据卷中包含之前创建的c123三个文件;
现在要备份这个目录:
-
exit
:退出容器db2; -
docker run --name db3 --volumes-from db2 -v ~/backup:/backup:wr tar cvf /backup/db3.tar /dbdata
:启动一个新的容器,用来执行备份命令; 取个名字是db3; 使用–vloumes-from这个命令来指定需要备份的容器的名字db2; 使用-v命令来指定希望备份文件存放的位置,这里存放在本地的backup目录下,在容器中指定目录也是backup,文件的权限是读写; 执行备份的操作是:压缩文件的命令,/backup/db3.tar是文件存放的地址, /dbdata指定需要备份的目录; -
ls backup
:访问本地的backup目录可以访问到压缩文件:db2的压缩包;
到这里就已经将包含数据卷的容器中的数据,通过一个容器,执行一个压缩命令tar cvf
,从而经数据备份出来;
使用同样的技术,通过解压缩命令tar xvf
来实现备份数据的还原;