文章目录
一、Volume(数据卷)
1.1 问题引出:
Docker容器中产生的数据在容器重启即丢失,对于存储系统或者有状态的应用,我们希望数据能够持久化下来,最典型的是数据库。不仅如此,我们还希望不同容器之间能够共享数据。
解决方案就是:数据卷,本质就是个挂载。
1.2 命令
docker run -v 宿主机目录:容器目录
比如:
docker run --name tomcat0 -v /home/test:/home -p 8080:8080 -d tomcat:9.0
下面再举个栗子:
启动一个centos 容器,将容器内的 /home 目录和 宿主机的一个测试目录 /home/centosTest 映射起来。
[root@localhost test]# docker run --name mycentos -v /home/test:/home -d centos
实际上, 容器的 /home 和 宿主机的 /home/test 是同一个目录。在 容器的 /home 中修改 会映射到 宿主机的/home/test ; 宿主机 /home/test 的修改也会映射到 容器的 /home 。
即使是 删除掉这个容器,宿主机中的内容依然存在。
再来看一下容器的 mount
信息 ,执行docker inspect ContainerID
,截取其中 Mounts 如下。
- type: bind 可以理解为 双向绑定
- source: 宿主机目录
- destination: 容器目录
1.3 实战(MySQL)
需求:实现MySQL数据持久化功能
1、搜索并pull 镜像
2、启动
docker hub 中的启动命令:
$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
这个命令无法让容器中的数据持久化,我们使用下面这个语句启动:
docker run --name mysql01 -p 3306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
着重看下下面两个卷映射:
-v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql
这是把 宿主机的 /home/mysql/conf
和 容器的/etc/mysql/conf.d
“双向绑定”起来了;同时把宿主机/home/mysql/data
和 容器的 /var/lib/mysql
绑定起来。
分别进入到容器内部和宿主机的内部看数据:
3、测试连通OK
通过GUI工具连过去看看
4、测试创建新Schema OK
5、删除容器看数据是否存在于宿主机
二、具名挂载和匿名挂载
具名挂载: 给挂载取了个名字
匿名挂载:挂载没名字(docker内部会生成一个字符串作为名字)
1、匿名挂载举个栗子:
-
启动匿名挂载的Nginx (随机指定端口了)
docker run --name nginx01 -d -P -v /etc/nginx nginx
这里的-v /etc/nginx
是容器内 的 路径,由于是匿名挂载,因此并不再指定 宿主机内的目录 -
docker volume ls
查看挂载的卷[root@localhost ~]# docker volume ls DRIVER VOLUME NAME ... local c1baa4089155de6a7245595288f2c3ebaa63722563af898a4091394769f7937c local portainer_data
这里有 多个 挂载的卷,Nginx 挂载的卷是其中一个。
2、具名挂载再举个栗子 -
docker run --name nginx02 -d -P -v juming-nginx-volume:/etc/nginx nginx
得到结果:
[root@localhost ~]# docker run --name nginx02 -d -P -v juming-nginx-volume:/etc/nginx nginx f015fce75db3b5023fc98227d26f303e0ed7ef14eeafa90d5b0c9045969293f9 [root@localhost ~]# docker volume ls DRIVER VOLUME NAME ... local c1baa4089155de6a7245595288f2c3ebaa63722563af898a4091394769f7937c local juming-nginx-volume local portainer_data
看下
juming-nginx-volume
这个卷具体信息(挂到了哪里):
docker volume inspect juming-nginx-volume
截图中的 MountPoint 就是 卷的挂载点。/var/lib/docker/volumes/
不仅是这个例子,而是docker 在默认情况下挂载卷的存放位置。
3、如何区分 是匿名还是具名挂载,还是指定路径挂载?
通过-v
参数判断:- -v 卷名:容器内目录路径 --> 这是具名挂载
- -v 容器内目录路径 --> 这是匿名挂载
- -v /宿主机路径:容器内目录路径 -->这是 指定路径挂载
4、volume 的 rw ro
通过指定 -v
容器内路径,ro rw
可改变读写权限
无论是匿名、具名,还是指定路径挂载,都可以指定rw ro
权限。
docker run --name nginx02 -d -P -v juming-nginx-volume:/etc/nginx:ro nginx
docker run --name nginx02 -d -P -v juming-nginx-volume:/etc/nginx:rw nginx
指定了ro
的挂载:只能从宿主机操作,容器没有写卷的权限
指定了rw
的挂载:宿主机、容器均可以读写
三、docker file初体验
Dockerfile : 就是构建Image的描述文件(命令脚本)。通过这个脚本,可以生成镜像,镜像是一层层的(UFS),脚本 命令是一个个的,每个脚本命令就会产生一层镜像。
举个栗子:创建一个DockerFile,制作出 一个自己的镜像
第一步:创建一个dockfile ,内容如下
[root@localhost xiaohe-test]# cat dockerfile1
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "..end.."
CMD /bin/bash
第二步: 执行 docker build 【注意最后有个 . 表示 当前的 build context】
[root@localhost xiaohe-test]# docker build -f dockerfile1 -t xiaohe/centos:01 .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM centos
---> 300e315adb2f
Step 2/4 : VOLUME ["volume01","volume02"]
---> Running in 15d62a5445bd
Removing intermediate container 15d62a5445bd
---> fd5b5d9fede3
Step 3/4 : CMD echo "..end.."
---> Running in 17bec299f276
Removing intermediate container 17bec299f276
---> 724156c85227
Step 4/4 : CMD /bin/bash
---> Running in 978558bfe4e3
Removing intermediate container 978558bfe4e3
---> 2a4be065fc65
Successfully built 2a4be065fc65
Successfully tagged xiaohe/centos:01
看这里分了四步,对应到dockerfile中的四个命令。每步都执行成功后,打了个 xiaohe/centos:01
[root@localhost xiaohe-test]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
xiaohe/centos 01 2a4be065fc65 8 minutes ago 209MB
mytomcat 02 2f6f33afc42c 27 hours ago 654MB
最后看确实生成了自己打的镜像。
接着这个栗子,从 自己的打的xiaohe/centos
拉起一个容器。有意思的是,我们在DockerFile VOLUME 出来的 volume01
和volume02
也存在。
既然是容器内目录,那它映射到宿主机的什么目录呢?
docker inspect ContainerID
,发现到了Mount
信息:原来是匿名挂载了 /var/lib/docker/volumes/ContainerID/_data这个目录!!!
看到这里,我们总结下:两种挂载卷的方式
- 直接在
docker run
的时候使用-v
参数挂载 - 在生成镜像的时候指定命令
VOLUME
四、 数据卷 容器
啥是数据卷容器?说白了:利用某个容器给别的容器共享数据
4.1 举第一个栗子
利用前面一节自己制造的镜像xiaohe/centos
拉起三个容器docker01、docker02、docker03;其中docker01 是数据卷容器:docker02会“自动同步” docker01 数据卷中的内容。
1、启动 父容器docker01
【注:ctrl P Q 退出容器console,但保持容器进程】
2、启动 子容器docker02
成功启动docker02后,看volume01 中已经有了在前一步中创建的 a.txt 测试文件。
3、在子容器docker 02 中创建新测试文件b.txt,看能不能同步到父容器中
由此可见: 默认情况下 --volumes-from
实现的是“双向绑定”
4、目前只是docker02 --volumes-from
docker01 ;下面测试 docker03 --volumes-from
docker01 ,即一个父容器下多个子容器。
可以看到,docker01 中创建的 a.txt ,docker02 中创建的b.txt ,都被“同步”到docker03
5、做个测试:假如docker02被 rm 了,那docker01 docker03 中还能找到的之前创建的 a.txt b.txt 测试文件吗? 答案是 Yes!
6、再做个测试:假如 docker01 (父容器)被删了,那 仅剩的 docker03 还能找到的之前创建的 a.txt b.txt 测试文件吗? 答案也是 Yes!
7、看起来: docker01 docker02 docker03 共享卷不仅实现了数据的“共享”,还实现了数据的“备份”
4.2 举第二个栗子(多个MySQL同步数据)
第一个msyql 实例:
docker run --name mysql01 -p 3306:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
第二个MySQL:
docker run --name mysql02 -p 3307:3306 --volumes-from mysql01 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
我们再去mysql02 /home/mysql/data 下加一个测试文件 data_test.txt。发现数据确实存在于 mysql01 /home/mysql/data 下面。
看MySQL01 的挂载:
再看MySQL 02 的挂载:
从截图发现:
两个MySQL 容器,实际上是 mount到了同一个 宿主机目录!这个点很关键: 2个MySQL进程共同操作同一份数据目录,往往会有冲突产生。我们观察到 mysql 02 进程在运行一段时间后就会报异常并退出。【假如两个容器在不同的宿主机上,那么应该不会有这个问题】
对于MySQL这种有状态(存储系统)的应用,利用docker部署应格外注意。
(?可能有完备的方案了,笔者目前还没研究)
4.3 小结
1、
--volumes-from
实现了多个容器之间的数据备份(不仅仅是共享)
2、有两种方式实现挂载:
docker run -v
:- 匿名
- 具名
- 指定目录挂载
- dockerfile中用 VOLUME 命令指定数据卷,打出镜像,拉起容器,即实现挂载
3、docker run --volumes-from
实现数据备份和同步
使用 --volumes-from
关联的容器,只要至少一个不删除,数据就能保留一份。如果父容器 是挂载了 宿主机目录,即使所有容器删除了,数据依然存在。
4、不同宿主机上的容器也可以利用 --volumes-from
实现数据备份和同步,但需要容器之间连通(还需要注意带宽?)