docker容器提供volume数据卷的方式,可以将容器某个目录或者文件挂载到宿主机的某个目录或者文件。这种方式的好处就是可以将容器内的数据挂载到宿主机,比如mysql的数据,即使容器意外宕机或者被删除的话,只要数据还在,我们就可以启动一个新的mysql容器。

创建一个数据卷并挂载宿主机

这里先初步举个例子,用来理解什么是数据卷,什么是挂载,容器和宿主机又是如何进行关联:

启动redis容器,将名称改为redis,并为容器添加一个数据卷,这个数据卷在容器里的目录是/opt/data
[root@localhost ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
redis               latest              0a153379a539        5 days ago          83.4MB

[root@localhost ~]# docker run --name redis -v /opt/data -tid 0a153379a539 /bin/bash
145fe5a93a6deaa76fd6c408d2b88ef2d685af48b03db3d0f64dd4a768b709a8
[root@localhost ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
145fe5a93a6d        0a153379a539        "docker-entrypoint.s…"   14 seconds ago      Up 13 seconds       6379/tcp            redis

进入容器,在数据卷目录下创建文件
[root@localhost ~]# docker exec -it redis /bin/bash
root@145fe5a93a6d:/data# cd /opt/data/
root@145fe5a93a6d:/opt/data# ls
root@145fe5a93a6d:/opt/data# echo 123 > 123
root@145fe5a93a6d:/opt/data# echo 123123 > 123123
root@145fe5a93a6d:/opt/data# ls
123  123123

退出容器,在宿主机上查看对应上面的那个数据卷的目录路径,并修改其中一个文件
root@145fe5a93a6d:/opt/data# exit
exit
[root@localhost ~]# docker inspect redis|grep /var/lib/docker/volumes
                "Source": "/var/lib/docker/volumes/442f7aaf3f84b6b41d4cffc4e3e71a84ab1b89fe9dbe70b76e99f851ff1bf66e/_data",
                "Source": "/var/lib/docker/volumes/b11ec41d372b348ca953e6ba5eccfa88cd732d84f08524af8a3c40e1eb466362/_data",
[root@localhost ~]# ls /var/lib/docker/volumes/442f7aaf3f84b6b41d4cffc4e3e71a84ab1b89fe9dbe70b76e99f851ff1bf66e/_data
123  123123
[root@localhost ~]# echo abcdef >> /var/lib/docker/volumes/442f7aaf3f84b6b41d4cffc4e3e71a84ab1b89fe9dbe70b76e99f851ff1bf66e/_data/123

进入容器,查看修改是否同步
[root@localhost ~]# docker exec -it redis /bin/bash
root@145fe5a93a6d:/data# ls /opt/data/
123  123123
root@145fe5a93a6d:/data# cat /opt/data/123
123
abcdef

挂载宿主机文件或目录到容器数据卷

可以直接挂载宿主机文件或目录到容器里,可以理解为目录映射,这样就可以让所有的容器共享宿主机数据,从而只需要改变宿主机的数据源就能够影响到所有的容器数据,或者当容器意外删除的时候,宿主机的数据仍然有一份,便于容灾。
挂载命令:

-v hostFile:containerFile
  • hostFile表示宿主机的目录或文件,需要提前存在的
  • containerFile表示容器的目录或文件,运行容器时会自动创建
  • 容器数据卷的权限默认是可读可写权限,如果需要设置权限,则使用命令 -v hostFile:containerFile:ro
  • 如果没有指定hostFile,即命令如果为 -v containerFile 则宿主机的默认目录为 /var/lib/docker/volumes/ 下,具体的映射关系可以使用命令查看 docker inspect [容器名称]|grep /var/lib/docker/volumes
  • 注意:目录只能挂载目录,文件只能挂载文件!

挂载宿主机文件到容器文件上

[root@localhost test]# pwd
/usr/local/test
[root@localhost test]# cat web.list 
192.168.0.1
192.168.0.2
192.168.0.3

运行容器,ro表示该文件在容器内是只读的
[root@localhost test]# docker run -tid --name redis -v /usr/local/test/web.list:/usr/local/test/web.list:ro redis /bin/bash
038603af3e21e509a827f1f18ab749ffd9e3a31982ce1f58936a710aa0bcbe6e
[root@localhost test]# docker exec -it redis /bin/bash
root@038603af3e21:/data# cat /usr/local/test/web.list 
192.168.0.1
192.168.0.2
192.168.0.3

容器内修改文件,发现会提示该文件是只读的
root@038603af3e21:/data# echo '192.168.0.4' >> /usr/local/test/web.list
bash: /usr/local/test/web.list: Read-only file system

挂载宿主机目录到容器目录上

[root@localhost test]# mkdir -p /usr/local/dalomao
[root@localhost test]# echo 'test' > /usr/local/dalomao/test
[root@localhost test]# echo 'test1' > /usr/local/dalomao/test1
[root@localhost test]# docker run -tid --name redis -v /usr/local/dalomao:/usr/local/dalomao redis /bin/bash
ee4ea05150476c6da22512597028ed18b17a1737f7644ebdad896e4ade389e97

容器上查看并修改
[root@localhost test]# docker exec -it redis /bin/bash
root@ee4ea0515047:/data# cd /usr/local/dalomao/
root@ee4ea0515047:/usr/local/dalomao# ls
test  test1
root@ee4ea0515047:/usr/local/dalomao# cat test
test
root@ee4ea0515047:/usr/local/dalomao# cat test1
test1
root@ee4ea0515047:/usr/local/dalomao# echo '1234' >> test
root@ee4ea0515047:/usr/local/dalomao# echo '4311' >> test1

宿主机上查看
root@ee4ea0515047:/usr/local/dalomao# exit
exit
[root@localhost test]# cat /usr/local/dalomao/test
test
1234
[root@localhost test]# cat /usr/local/dalomao/test1
test1
4311

挂载多个目录

[root@localhost test]# mkdir -p /usr/local/dalomao/data1
[root@localhost test]# mkdir -p /usr/local/dalomao/data2
[root@localhost test]# echo '123456' > /usr/local/dalomao/data1/test1
[root@localhost test]# echo 'abcdef' > /usr/local/dalomao/data2/test2
[root@localhost test]# docker run -tid --name redis -v /usr/local/dalomao/data1:/usr/local/dalomao/data1 -v /usr/local/dalomao/data2:/usr/local/dalomao/data2 redis /bin/bash
ef4d028958386f09352275daf9964f1fad259264e10a5662de46114cef50a0ca

容器内查看并修改
[root@localhost test]# docker exec -ti redis /bin/bash
root@ef4d02895838:/data# ls /usr/local/dalomao/data1
test1
root@ef4d02895838:/data# ls /usr/local/dalomao/data2
test2
root@ef4d02895838:/data# cat /usr/local/dalomao/data1
cat: /usr/local/dalomao/data1: Is a directory
root@ef4d02895838:/data# cat /usr/local/dalomao/data1/test1 
123456
root@ef4d02895838:/data# cat /usr/local/dalomao/data2/test2 
abcdef
root@ef4d02895838:/data# echo 'data1' >> /usr/local/dalomao/data1/test1
root@ef4d02895838:/data# echo 'data2' >> /usr/local/dalomao/data2/test2

容器间挂载

启动一个名为container1容器,此容器包含两个数据卷/var/volume1和/var/volume2,这两个目录是在容器里的,运行容器时会自动创建

[root@localhost test]# docker run -tid --name container1 -v /var/volume1 -v/var/volume2 redis /bin/bash
61a8b7761e96c01798a0e77371bf5e74546e469a747b38f457d68011dbdf6486
[root@localhost test]# docker inspect container1|grep /var/lib/docker/volumes
                "Source": "/var/lib/docker/volumes/5bde8e911fe864a982a67800eca3313a92d116cfec7d5667cc09d5d44ff31196/_data",
                "Source": "/var/lib/docker/volumes/4323c7faa7eb1e2a29e52f9c65983e0016054b0dc23dd15faa41c30c7f0ad846/_data",
                "Source": "/var/lib/docker/volumes/fd74c2f2e44d7fba2e5c1257e99eec14009af96e0a1f1c09fb46f642c2b3ab83/_data",

创建container2容器,挂载container1容器中的数据卷

[root@localhost test]# docker run -tid --rm --volumes-from container1 --name container2 redis /bin/bash
4bb09861542a6bc2eb6a9e98049a5b54953a76c57f1ecc637468ca10b48dc0dd

登陆容器2查看并修改内容
[root@localhost test]# docker exec -ti container2 /bin/bash
root@4bb09861542a:/data# ls /var/volume1
root@4bb09861542a:/data# ls /var/volume2
root@4bb09861542a:/data# echo 'this is volume1' > /var/volume1/test1
root@4bb09861542a:/data# echo 'this is volume2' > /var/volume2/test2

登陆容器1查看修改后的内容
root@4bb09861542a:/data# exit
exit
[root@localhost test]# docker exec -ti container1 /bin/bash
root@61a8b7761e96:/data# cat /var/volume1/test1 
this is volume1
root@61a8b7761e96:/data# cat /var/volume2/test2 
this is volume2

再创建一个container3,挂载container2中从container1挂载的数据卷,当然也可以直接挂载初识的container1容器的数据卷

[root@localhost test]# docker run -tid --rm --volumes-from container2 --name container3 redis /bin/bash
63c57cd4bc183f7d41e18e86e3359e6a431eae3015b6c4056f0035e7e6ff3783
[root@localhost test]# docker exec -ti container3 /bin/bash
root@63c57cd4bc18:/data# cat /var/volume1/test1 
this is volume1
root@63c57cd4bc18:/data# cat /var/volume2/test2 
this is volume2
  • 即使删除了初始的数据卷容器container1,或者是删除了其他容器,但只要是有容器在使用该数据卷,那么它里面的数据就不会丢失
  • 命令中的rm表示当容器退出即停止的时候,会自动删除该容器

备份数据卷

创建一个容器container1,包含两个数据卷/var/volume1和/var/volume2(这两个目录是在容器里的数据卷路径)

[root@localhost test]# docker run -tid -v /var/volume1 -v /var/volume2 --name container1 redis /bin/bash
2a76939f65eac00110dfff3bc3e0dd99a33804e2175397133184449afb2af731
[root@localhost test]# docker exec -ti redis /bin/bash
[root@localhost test]# docker exec -ti container1 /bin/bash
root@2a76939f65ea:/data# echo 'test1' > /var/volume1/test1
root@2a76939f65ea:/data# echo 'test11' > /var/volume1/test11
root@2a76939f65ea:/data# echo 'test2' > /var/volume2/test2
root@2a76939f65ea:/data# echo 'test22' > /var/volume2/test22
root@2a76939f65ea:/data# ls /var/volume1
test1  test11
root@2a76939f65ea:/data# ls /var/volume2
test2  test22

接下来进行数据卷的备份操作

使用–volumes-from来创建一个加载container1容器卷的容器,并从宿主机挂载当前所在目录到容器的/backup目录,容器内会tar压缩/var/colume1目录下的文件到/backup/backup1.tar,因为宿主机当前目录已经映射到/backup目录了,因此会在宿主机当前目录也存在该压缩包。备份完毕后–rm自动删除该创建的容器。

备份container1容器中的/var/volume1数据卷数据
-tid这个参数加不加都可以
--rm加上,备份后就会自动删除这个容器,如果不加这个--rm参数,那么备份后的容器就会保留,docker ps -a就会查看到)
$(pwd)表示当前宿主机目录
[root@localhost test]# pwd
/usr/local/test
[root@localhost test]# docker run -tid --rm --volumes-from container1 -v $(pwd):/backup redis tar cvf /backup/backup1.tar /var/volume1
67b1eda88b832c29db7d5e0850fe31955b83ad0c244b657ac30e2e3f18e416a3

本地目录就会生成backup1.tar文件
解压后的目录结构是:
--var
	--volume1
		--test1
		--test11
[root@localhost test]# ls
backup1.tar
备份container1容器中的/var/volume2数据卷数据
[root@localhost test]# pwd
/usr/local/test
[root@localhost test]# docker run -tid --rm --volumes-from container1 -v $(pwd):/backup redis tar cvf /backup/backup2.tar /var/volume2
896adf78f9b6a06d6543b72e4977d6d16cb623157ee09f22a7fc6a8367e72478
[root@localhost test]# ll
total 24
-rw-r--r--. 1 root root 10240 Oct 15 01:20 backup1.tar
-rw-r--r--. 1 root root 10240 Oct 15 01:36 backup2.tar
备份container1容器中的/var/volume1和/var/volume2数据卷数据
[root@localhost test]# pwd
/usr/local/test
[root@localhost test]# docker run -tid --rm --volumes-from container1 -v $(pwd):/backup redis tar cvf /backup/backup.tar /var/volume1 /var/volume2
60c7b06e27708e98a1f8ddf48a8e287af152699e88026fa75b6133a4b2977be9
[root@localhost test]# ll
total 36
-rw-r--r--. 1 root root 10240 Oct 15 01:20 backup1.tar
-rw-r--r--. 1 root root 10240 Oct 15 01:36 backup2.tar
-rw-r--r--. 1 root root 10240 Oct 15 01:38 backup.tar

恢复和迁移数据卷

使用备份数据卷一章中的容器container1和备份数据卷backup.tar进行恢复操作

恢复数据给同一个容器

之前的数据卷是从container1中备份的,现在模拟container1数据卷丢失,然后直接用之前备份的backup.tar进行恢复

为了测试恢复,先删除容器里原先的数据(注意:数据卷目录不能删除,只能删除其中的数据)
root@2a76939f65ea:/data# ls /var/volume2
test2  test22
root@2a76939f65ea:/data# rm -rf /var/volume1 /var/volume2
rm: cannot remove '/var/volume1': Device or resource busy
rm: cannot remove '/var/volume2': Device or resource busy
root@2a76939f65ea:/data# ls /var/volume1
root@2a76939f65ea:/data# ls /var/volume2

进行数据卷恢复,恢复数据卷中的所有数据
root@2a76939f65ea:/data# exit
exit
[root@localhost test]# pwd
/usr/local/test
[root@localhost test]# ll
total 36
-rw-r--r--. 1 root root 10240 Oct 15 01:20 backup1.tar
-rw-r--r--. 1 root root 10240 Oct 15 01:36 backup2.tar
-rw-r--r--. 1 root root 10240 Oct 15 01:38 backup.tar

注意-C后面的路径,表示将数据恢复到容器里的路径直接使用压缩包中文件的各个路径。比如压缩包中的结果如下:
--var
	--volume1
		--test1
		--test11
	--volume2
		--test22
		--test22	
则直接将文件解压到 /var/volume1和/var/volume2目录		
[root@localhost test]# docker run --rm --volumes-from container1 -v $(pwd):/backup redis tar xvf /backup/backup.tar -C /
var/volume1/
var/volume1/test1
var/volume1/test11
var/volume2/
var/volume2/test2
var/volume2/test22

登陆容器,查看数据卷已经正确恢复
[root@localhost test]# docker exec -ti container1 /bin/bash
root@2a76939f65ea:/data# ls /var/volume1
test1  test11
root@2a76939f65ea:/data# ls /var/volume2
test2  test22

恢复数据给新的容器

新建一个容器container2
[root@localhost test]# docker run -tid -v /var/volume1 -v /var/volume2 --name container2 redis /bin/bash
75b28049c3823843fb2e0fd8bae87671f0cf4ea27b495a4f725821c4edb2883a
[root@localhost test]# docker exec -ti container2 /bin/bash
root@75b28049c382:/data# ls /var/volume1
root@75b28049c382:/data# ls /var/volume2

开始恢复
root@75b28049c382:/data# exit
exit
[root@localhost test]# pwd
/usr/local/test
[root@localhost test]# ll
total 36
-rw-r--r--. 1 root root 10240 Oct 15 01:20 backup1.tar
-rw-r--r--. 1 root root 10240 Oct 15 01:36 backup2.tar
-rw-r--r--. 1 root root 10240 Oct 15 01:38 backup.tar
[root@localhost test]# docker run --rm --volumes-from container2 -v $(pwd):/backup redis tar xvf /backup/backup.tar -C /
var/volume1/
var/volume1/test1
var/volume1/test11
var/volume2/
var/volume2/test2
var/volume2/test22

查看确实已经恢复了
[root@localhost test]# docker exec -ti container2 /bin/bash
root@75b28049c382:/data# ls /var/volume1
test1  test11
root@75b28049c382:/data# ls /var/volume2
test2  test22

注意:

  • 新容器创建时挂载的数据卷路径最好和之前备份的数据卷路径一致
  • 新容器创建时,如果挂载的数据卷只是备份数据卷的一部分,那么恢复的时候也只是恢复一部分数据。比如新建容器挂载数据卷为-v /var/voluem1,那么使用backup.tar恢复时,只会恢复/var/volume1的数据,/var/volume2的数据是不会恢复的
  • 如果新容器创建时挂载的数据卷目录和备份的数据卷目录不一致,那么数据恢复不了,除非修改 -C 后面的路径,比如新建容器时指定数据卷目录为 /var/volume,恢复时也是用 -C /var/volume,则是可以成功恢复的

删除数据卷

Docker不会在容器被删除后自动删除数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的数据卷。如果需要在删除容器的同时移除数据卷,可以在删除容器的时候使用 docker rm -v 这个命令。无主的数据卷可能会占据很多空间,要清理会很麻烦

  • docker volume ls     列出所有的数据卷
  • docker volume ls --filter dangling=true     过滤不在使用的数据卷
  • docker volume rm [volume name]     删除一个数据卷,容器正在使用的数据卷不能删除,绑定挂载的数据卷无法删除
Logo

更多推荐