-
为什么需要容器数据卷
角度:遇到问题,尝试以朴素的道理解决问题.问题复杂化,解决的方式也变得复杂
问题的提出:docker将应用和环境打包成一个镜像,但是对于容器内的数据,如果不进行外部的保存,那么当容器删除后,数据也会丢失
解决:通过docker的容器数据卷技术,实现容器数据的持久化和同步操作!同时容器间也是可以数据共享的!
实现:目录的挂载,将我们容器内的目录,挂载到Linux上面,使两者之间相互对应
-
使用数据卷
建议使用两个终端进行调试
-
docker run -v 主机目录地址:容器目录地址
:将主机目录地址与对应的容器目录地址进行双向绑定v:类似vue的v-model双向绑定(注:v-bind和v-model的区别在于v-bind多用于单向绑定,比如字段的显示;v-model则多用于表单,比如input中输入的返回等)
测试:
#运行一个容器,同时制定行管的目录 [root@iZwz908j8pbqd86doyrez5Z test]# docker run -d -it -v /test:/root/test -p 8081:8080 tomcat:9.0 /bin/bash a5c3db57e2b230db84a9d273e2379d55b47b47bc07f5630d16a35d1e5fc0c3b0 [root@iZwz908j8pbqd86doyrez5Z test]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a5c3db57e2b2 tomcat:9.0 "/bin/bash" 14 seconds ago Up 14 seconds 0.0.0.0:8081->8080/tcp sad_tu [root@iZwz908j8pbqd86doyrez5Z test]# docker attach a5c3db57e2b2 #从元数据中查看绑定情况 "Mounts": [ { "Type": "bind", "Source": "/root/test", //源文件位置(主机) "Destination": "/root/test", //目标文件位置(容器) "Mode": "", "RW": true, "Propagation": "rprivate" } ] #尝试从容器写入主机 #容器 [root@6eec281a5e9f test]# mkdir test1 [root@6eec281a5e9f test]# ls test.java test1 #主机 [root@iZwz908j8pbqd86doyrez5Z test]# ls test1 test.java #尝试从主机写入容器 #主机 [root@iZwz908j8pbqd86doyrez5Z test]# touch a.tets [root@iZwz908j8pbqd86doyrez5Z test]# ls a.tets test1 test.java #容器 [root@6eec281a5e9f test]# ls a.tets test.java test1
注:
- -it:以交互的形式打开终端,-d:在后台运行
- 只有在运行时用-it...bash,才能在之后用attach打开一个命令行
- 从主次关系来说,其实还是主机的目录覆盖容器的目录
优点:修改只需在本地修改即可,容器内会自动同步
-
-
安装mysql完成挂载
mysql所有数据都在data目录下
#下载镜像 docker pull mysql:5.7 #在使用挂载对mysql的相关目录覆盖之前,我们需要保证目录中有所需要的东西 #新建一下目录:/usr/arno/mysql/conf,/usr/arno/mysql/data #打开一个不需要挂载的mysql容器,将容器内对应的数据复制到新建的目录中 [root@iZwz908j8pbqd86doyrez5Z conf]# docker run -d -p 3311:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 mysql:5.7 [root@iZwz908j8pbqd86doyrez5Z ~]# docker cp ad7db127211a:/etc/mysql/conf.d /usr/arno/mysql/conf [root@iZwz908j8pbqd86doyrez5Z ~]# docker cp ad7db127211a:/var/lib/mysql /usr/arno/mysql/data #运行镜像(同时对mysql的conf和data进行了挂载) docker run -d -p 3310:3306 -v /usr/arno/mysql/conf:/etc/mysql/conf.d -v /usr/arno/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7 #尝试通过navicat访问(需要配置阿里云安全组) #(1).连接成功(欧耶!) #(2).新建一个数据库(test) #查看主机中的data [root@iZwz908j8pbqd86doyrez5Z data]# ls auto.cnf client-cert.pem ibdata1 ibtmp1 private_key.pem server-key.pem ca-key.pem client-key.pem ib_logfile0 mysql public_key.pem sys ca.pem ib_buffer_pool ib_logfile1 performance_schema server-cert.pem test #查看容器中的数据库 root@78c6defe3428:/var/lib/mysql# ls auto.cnf client-cert.pem ib_logfile0 ibtmp1 private_key.pem server-key.pem ca-key.pem client-key.pem ib_logfile1 mysql public_key.pem sys ca.pem ib_buffer_pool ibdata1 performance_schema server-cert.pem test #完美!
注:
- 通过hub.docker搜查mysql,以便完成mysql密码的配置
$ docker run --name some-mysql -v /my/custom:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
- 注意:挂载的开始,主机的目录会覆盖容器的目录,所以你的目录必须一开始就要有对应的文件.解决方法就是运行一个不挂载的容器,然后将所有东西都用
docker cp 容器id:目录 要主机目录
命令拷贝到主机目录下(但更好的方法是使用下面的具名挂载)
- 通过hub.docker搜查mysql,以便完成mysql密码的配置
-
具名挂载与匿名挂载
#1.匿名挂载 -v 容器内路径 docker run -d -P --name nginx01 -v /etc/nginx nginx #查看所有挂载的卷 [root@iZwz908j8pbqd86doyrez5Z ~]# docker volume ls DRIVER VOLUME NAME local 2e364979e68adc93ea6ac8075250d0f41488c62b0345db8a917f8571df312992 local 811aa7cb9f25b11fffb8a6e3490726f6d85b28f73147a7bceb8096288aa70163 local c1abdabde73dcf238913e7f51aa4bcd726f170a474d3282f704f19bc86f40be0 local f0e7e07b67bf298bdf8c3d420f93c5851c0cfa313669cfd1887f4a3210e73c62 #匿名卷挂载形式(在-v时只写了容器内的路径,没有写容器外的路径) #通过原数据查看匿名挂载的卷 "Mounts": [ { "Type": "volume", "Name": "811aa7cb9f25b11fffb8a6e3490726f6d85b28f73147a7bceb8096288aa70163", #对应的匿名 "Source": "/var/lib/docker/volumes/811aa7cb9f25b11fffb8a6e3490726f6d85b28f73147a7bceb8096288aa70163/_data", #匿名挂载对应的主机路径 "Destination": "-etc/nginx", #匿名挂载对应的容器路径 "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } #2.具名挂载 [root@iZwz908j8pbqd86doyrez5Z ~]# docker run -d -P --name nginx02 -v volume:/etc/nginx nginx d4a2fed1744e7c8ed2e3febe35612842a01eb8379d159b437de752e0b6c25ca1 [root@iZwz908j8pbqd86doyrez5Z ~]# docker volume ls DRIVER VOLUME NAME local 2e364979e68adc93ea6ac8075250d0f41488c62b0345db8a917f8571df312992 local 811aa7cb9f25b11fffb8a6e3490726f6d85b28f73147a7bceb8096288aa70163 local c1abdabde73dcf238913e7f51aa4bcd726f170a474d3282f704f19bc86f40be0 local f0e7e07b67bf298bdf8c3d420f93c5851c0cfa313669cfd1887f4a3210e73c62 local volume #查看具名挂载的目录 [root@iZwz908j8pbqd86doyrez5Z ~]# docker inspect volume [ { "CreatedAt": "2020-09-12T09:03:07+08:00", "Driver": "local", "Labels": null, "Mountpoint": "/var/lib/docker/volumes/volume/_data", "Name": "volume", "Options": null, "Scope": "local" } ] #查看元数据中的挂载 [root@iZwz908j8pbqd86doyrez5Z ~]# docker inspect nginx02 "Mounts": [ { "Type": "volume", "Name": "volume", "Source": "/var/lib/docker/volumes/volume/_data", #主机目录 "Destination": "/etc/nginx", #容器目录 "Driver": "local", "Mode": "z", "RW": true, "Propagation": "" } ]
注:
-
-P(大p):随机映射端口
-
所有的docker容器内的卷,没有指定目录的情况下都是docker 在
/var/lib/docker/volume/xxxx/_data
下 -
挂载形式 指定挂载路径 有名字 匿名挂载 false false 指定路径挂载 true false 具名挂载 true true
如何区分具名挂载,匿名挂载,还是具名指定路径挂载?
-v 容器内路径 #匿名挂载
-v 卷名:容器内路径 #具名挂载
-v /宿主机路径:/容器内路径 #指定路径挂载
拓展:
# 通过-v容器内路径,ro/rw改变读写权限 docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx #只读 docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx #只写
注:只读的挂载卷只能通过宿主机来操作
-
-
初始Dockerfile
Dockerfile就是用来构建docker镜像的构建文件和命令脚本.由于镜像是分层的,每个命令也对应一层
#创建一个名为docker file1的文件,同时进入编辑 vim dockerfile1 #写命令(指令大写) FROM centos #以centos为基础 VOLUME ["volumen01","volume02"] #在生成时挂载卷,匿名挂载,volume01/volume02是内部的文件 CMD echo "----end----" CMD /bin/bash #以指令台的形式打开 #保存退出:esc -> :wq #查看编写的文件脚本 car dockerfile1 #生成dockerfile [root@iZwz908j8pbqd86doyrez5Z test]# docker build -f dockerfile1 -t arno/centos:1.0 . Sending build context to Docker daemon 3.584kB Step 1/4 : FROM centos ---> 0d120b6ccaa8 Step 2/4 : VOLUME ["volumen01","volume02"] ---> Running in e9fd99177a57 Removing intermediate container e9fd99177a57 ---> 4b51c5ece9de Step 3/4 : CMD echo "---end---" ---> Running in 17fc5ba5da77 Removing intermediate container 17fc5ba5da77 ---> ba8b29cc064e Step 4/4 : CMD /bin/bash ---> Running in 047d5ced7a4b Removing intermediate container 047d5ced7a4b ---> bbbedbad86c6 Successfully built bbbedbad86c6 Successfully tagged arno/centos:1.0 #-f 生成的文件 #-t 生成的文件名:tag 生成的文件位置(当时当前目录) #运行容器 [root@iZwz908j8pbqd86doyrez5Z test]# docker run -it arno/centos:1.0 /bin/bash [root@652cd9f64f74 /]# ls bin etc lib lost+found mnt proc run srv tmp var volumen01 dev home lib64 media opt root sbin sys usr volume02#可以看到有volume01和volume02两个文件 #查看挂载的数据卷 [root@iZwz908j8pbqd86doyrez5Z ~]# docker inspect 652cd9f64f74 "Mounts": [ { "Type": "volume", "Name": "7fe986067800aa48085b302336faa270d7df53ba65d4eb3e0964eaee6f50796a", #匿名 "Source": "/var/lib/docker/volumes/7fe986067800aa48085b302336faa270d7df53ba65d4eb3e0964eaee6f50796a/_data", "Destination": "volume02", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" }, { "Type": "volume", "Name": "a5e71c3f49495c1b02fa559b92fb65ecc6dee0a1ce7fd3d2fa77b2dc1341e739", #匿名 "Source": "/var/lib/docker/volumes/a5e71c3f49495c1b02fa559b92fb65ecc6dee0a1ce7fd3d2fa77b2dc1341e739/_data", "Destination": "volumen01", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ], #测试 #在容器中生成一个/volumen01/test.txt文件 [root@652cd9f64f74 /]# cd volumen01 [root@652cd9f64f74 volumen01]# touch test.txt [root@652cd9f64f74 volumen01]# ls test.txt #在主机中查看(直接复制元数据中mounts的source路径即可) [root@iZwz908j8pbqd86doyrez5Z _data]# cd /var/lib/docker/volumes/a5e71c3f49495c1b02fa559b92fb65ecc6dee0a1ce7fd3d2fa77b2dc1341e739/_data [root@iZwz908j8pbqd86doyrez5Z _data]# ls test.txt #验证完毕!
-
数据卷容器
多个容器间的数据共享
注意容器数据卷和数据卷容器的区别:
- 容器的数据卷
- 数据卷的容器:子容器挂载父容器(数据卷的容器)的数据,实现数据的同步
#使用 [root@iZwz908j8pbqd86doyrez5Z _data]# docker run -d -it --name docker01 arno/centos:1.0 [root@iZwz908j8pbqd86doyrez5Z _data]# docker run -it --name docker02 --volumes-from docker01 arno/centos:1.0 #docker01的数据挂载到docker02上 #验证 [root@iZwz908j8pbqd86doyrez5Z _data]# docker attach docker01 [root@5bd780fa7cad /]# ls bin etc lib lost+found mnt proc run srv tmp var volumen01 dev home lib64 media opt root sbin sys usr volume02 [root@5bd780fa7cad /]# cd volumen01 [root@5bd780fa7cad volumen01]# touch test.txt [root@5bd780fa7cad volumen01]# ls test.txt [root@iZwz908j8pbqd86doyrez5Z _data]# docker attach docker02 [root@56e0c6ac0d64 /]# ls bin etc lib lost+found mnt proc run srv tmp var volumen01 dev home lib64 media opt root sbin sys usr volume02 [root@56e0c6ac0d64 /]# cd volumen01 [root@56e0c6ac0d64 volumen01]# ls test.txt #查看元数据 [root@iZwz908j8pbqd86doyrez5Z _data]# docker inspect docker01 "Mounts": [ { "Type": "volume", "Name": "9c60002316aa06044c718bd6728a9c4e71865b3127773461f65a82354e26d70c", "Source": "/var/lib/docker/volumes/9c60002316aa06044c718bd6728a9c4e71865b3127773461f65a82354e26d70c/_data", "Destination": "volume02", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" }, { "Type": "volume", "Name": "e0584a1a806735c297b08629bb9ab4b1d419bbdc9a61fb5f526618a6096802b0", "Source": "/var/lib/docker/volumes/e0584a1a806735c297b08629bb9ab4b1d419bbdc9a61fb5f526618a6096802b0/_data", "Destination": "volumen01", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ], [root@iZwz908j8pbqd86doyrez5Z _data]# docker inspect docker02 "Mounts": [ { "Type": "volume", "Name": "9c60002316aa06044c718bd6728a9c4e71865b3127773461f65a82354e26d70c", "Source": "/var/lib/docker/volumes/9c60002316aa06044c718bd6728a9c4e71865b3127773461f65a82354e26d70c/_data", "Destination": "volume02", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" }, { "Type": "volume", "Name": "e0584a1a806735c297b08629bb9ab4b1d419bbdc9a61fb5f526618a6096802b0", "Source": "/var/lib/docker/volumes/e0584a1a806735c297b08629bb9ab4b1d419bbdc9a61fb5f526618a6096802b0/_data", "Destination": "volumen01", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ], #可以看到匿名挂载下,挂载的是同一个路径
注:
- 虽然挂载的时候有主次的关系,但是数据是互通的
- 删除父容器,子容器内的数据还是能访问到,因为各个容器的数据卷真实主机地址的文件位置没有删
- 主机->父容器->子容器,主机挂载数据卷到父容器,父容器作为数据卷容器对子容器挂载
- 挂载的时候,本地一定会有对应的卷