数据卷是一个或多个容器中专门指定的目录,它能够绕过联合文件系统
卷被设计用作数据持久化、并且是独立于容器的生命周期的
docker是一个分层文件系统,性能比较差,生命周期和容器是相同的,容器删除,里面的内容就没有了
数据卷是mount到宿主机上的,这就绕开了分层的文件系统,和主机的磁盘性能是相同的,而且容器删除后,文件会依然保留,但这个仅限于本地磁盘,不能随容器去迁移
当我们在使用docker容器的时候,会产生一系列的数据文件,这些数据文件在我们关闭docker容器时是会消失的
docker官方提供两种卷:
bind mount,直接在宿主机的文件系统中但是容器可以访问;
docker managed volume磁盘上Docker管理的数据卷,因为没有名字想要找到不容易,Docker来管理这些文件
数据卷是在(如果没有网络文件系统等情况下)宿主机文件系统里面的,
第一种是在宿主机内的特定目录下,
第二种在docker管理的目录下/var/lib/docker/volumes/
(1)数据卷可以在容器之间共享或重用数据
(2)数据卷中的更改可以直接生效
(3)数据卷中的更改不会包含在镜像的更新中
(4)数据卷的生命周期一直持续到没有容器使用它为止
bind mount:将主机上的目录或文件直接mount到容器里
(1)挂载文件到容器
docker run -d --name web1 -p 80:80 -v /opt/website: /usr/share/nginx/html nginx ##容器里/usr/share/nginx/html是nginx 的默认发布目录
curl localhost ##当前无资源,出现403报错
docker exec web1 mount ##在容器里执行mount命令,不进入容器中
##可以看到/dev/mapper/rhel-root(根目录),挂接到容器的/usr/share/nginx/html上
vim /opt/website/index.html ##编辑nginx 的默认发布主页
curl localhost ##可以访问到资源内容
docker container attach web1 ##应用容器不能这样进,会卡住,这是因为ubuntu会给你一个交互式shell,nginx只是打开了nginx,并没有给你交互式的shell
docker history ubuntu:latest
docker history nginx:latest
docker start web1 ##开启web1容器
docker exec -it web1 bash ##以交互方式给它执行一个bash命令,bash表示在容器内执行操作,打开一个bash ,在容器中修改
curl localhost ##数据已经更改,达到预期效果
注意:
在容器内更改的数据事实上是宿主机上的数据
这种方式,不存在的目录会自动建立(无论容器还是宿主机),但是会以宿主机为准,绑定挂接到容器后,容器中的对应目录如果有数据,就会被覆盖
(3)指定挂载时是只读挂载
bind mount方式挂载时默认权限是rw,现在指定在挂载时只读ro
docker run -it --name vm1 -v /opt/data1:/data1 -v /opt/data2:/data2:ro ubuntu ##这些目录在宿主机和容器内都是没有的 ,data1读写挂载,data2只读挂载
cd data1/
touch file1 ##可以建立文件
cd ../data2
touch file2 ##不可以建立
ctrl+p+q退出容器,在宿主机上的/opt/data2目录下建立file2
cd /opt/data2/
touch file2 ##可以建立文件
docker attach vm1 ##可以看到file2
docker run -it --name vm2 -v /etc/yum.repos.d/dvd.repo:/etc/yum.repos.d/dvd.repo rhel7 bash
##挂载yum源,注意要给一个bash,不给bash看不到交互式界面
docker run -it --name vm2 -v /etc/yum.repos.d/dvd.repo:/etc/yum.repos.d/dvd.repo:ro rhel7 bash
##也可以加上只读选项,让yum源不可更改
bind mount方式必须指定宿主机文件系统路径,可移植性比较差
如果容器在A主机上crash掉,B主机上没有对应的路径,就无法进行移植,此时你还需要先去创建这个路径,移植起来不方便,但docker 管理卷不需要指定,docker会自动给这个容器创建
docker managed volume不需要指定宿主机文件系统路径,docker自动为容器创建,默认数据卷目录都在 /var/lib/docker/volumes
如果挂载时指向容器内已有的目录,原数据会被复制到volume中
(1)docker managed volume挂载文件
docker run -d --name web1 -p 80:80 -v /usr/share/nginx/html nginx ##只指定容器的文件路径,没有指定宿主机上面的文件路径
curl localhost ##可以访问,因为宿主机上会自动创建一个目录与之对应
docker inspect web1 ##查看web1容器信息,Source:数据卷目录
进入到数据卷目录,修改数据卷的内容后依然可以访问
(2)自己指定数据卷目录
docker run -d --name web2 -p 80:80 -v website2:/usr/share/nginx/html nginx ##指定数据卷目录/var/lib/docker/volumes/website2
curl localhost
docker inspect web2
docker rm -f web2 ##删除容器
docker volume ls ##查看卷,发现容器删除了,数据还在
docker volume prune ##清理没有被使用的卷
docker volume create webdata
docker run -d --name web2 -p 80:80 -v website2:/usr/share/nginx/html nginx
docker rm -f web2 ##删除容器
docker volume ls ##查看卷,容器删除了,数据还在
docker volume create webdata ##创建一个数据卷
cp /var/lib/docker/volumes/website2/_data/* /var/lib/docker/volumes/webdata/_data/ ##拷贝数据
docker run -d --name web2 -p 80:80 -v webdata:/usr/share/nginx/html nginx ##创建web2依然可以使用web1的数据
curl localhost ##可以访问
echo www.westos.com > /var/lib/docker/volumes/webdata/_data/index.html ##更改默认发布页面后也可以访问
docker run -d --name web3 -p 80:80 -v webdata:/usr/share/nginx/html nginx ##再新建一个web3容器,去掉端口映射,指定到web1容器删除前的卷目录里
docker inspect web3
curl localhost ##可以访问
docker run -d --name web4 -p 80:80 -v webdata:/usr/share/nginx/html nginx ##再新建一个web4容器,去掉端口映射,指定到web1容器删除前的卷目录里
curl localhost ##可以访问
如果想实现跨主机的容器之间的数据共享就要使用卷插件
使用了卷插件才可以使用网络文件系统实现数据共享
docker卷默认使用的是local类型的驱动,只能存在于宿主机,跨主机的volume就需要第三方驱动。可以查看一链接
https://docs.docker.com/engine/extend/legacy_plugins/#volume-plugins
docker官方 link只提供了插件api,开发者可以根据实际需求定制插件驱动。
docker plugin是以web服务的方式运行在docker主机上,是独立的
Docker Plugin 是以Web Service的服务运行在每一台Docker Host上的,通过HTTP协议传输RPC风格的JSON数据完成通信
工作流程:
docker engine(daemon) -->volume plugin–>storage platform;
引擎调用插件,插件操作存储
Plugin的启动和停止,并不归Docker管理,Docker Daemon依靠在缺省路径下查找Unix Socket文件,自动发现可用的插件
当客户端与Daemon交互,使用插件创建数据卷时,Daemon会在后端找到插件对应的 socket 文件
建立连接并发起相应的API请求,最终结合Daemon自身的处理完成客户端的请求
convoy卷插件底层存储支持三种模式:
devicemapper、NFS、EBS(亚马逊的弹性化存储)
在所有docker节点提前挂载NFS存储
实验环境:准备两台虚拟机并安装好docker
server1:172.25.254.1
server2:172.25.254.2
实验:
(1)在server1和server2上部署配置nfs文件系统
yum install -y nfs-utils.x86_64 ##安装nfs
systemctl start rpcbind ##启动rpcbind服务,不启动此服务的话是看不到共享资源的,RPC服务主要是在nfs共享时候负责通知客户端,服务器的nfs端口号的
vim /etc/exports ##编辑共享配置文件
/mnt/nfs *(rw,no_root_squash) ##root在操作时是不切换身份,默认情况下会切换到nfs用户或nobody用户
mkdir /mnt/nfs ##建立共享目录
systemctl start nfs ##启动nfs
exportfs -rv ##导出一下,输出共享目录,使NFC配置文件生效
mkdir /mnt/nfs ##建立共享目录
showmount -e 172.25.254.1 ##客户端访问服务端,可以列出表示服务正常
mount 172.25.254.1:/mnt/nfs /mnt/nfs ##客户端挂载共享目录
df ##查看挂载
[root@server1 ~]# cd /mnt/nfs/
[root@server1 nfs]# touch file
[root@server1 nfs]# ls
file
[root@server2 ~]# cd /mnt/nfs/
[root@server2 nfs]# ls
file
可以看到新建的file1,说明nfs配置没有问题,nfs实现了数据共享
(2)配置convoy插件
先获取convoy软件包
docker官方只提供了卷插件的api,开发者可以根据实际需求定制卷插件驱动
server1和server2
tar zxf convoy.tar.gz ##解压convoy包
cd convoy ##进入convoy目录
cp convoy convoy-pdata_tools /usr/local/bin ##将二进制文件加入到PATH路径,方便操作
convoy daemon --drivers vfs --driver-opts vfs.path=/mnt/nfs &
##第一次运行上面的convoy daemon命令的时候,会在/mnt/nfs目录下生成一个config目录,这个目录不要删除,不然客户端的convoy命令就会用不了,config目录是客户端的一些配置
mkdir /etc/docker/plugins ##建立插件目录,docker程序默认会在这个目录下查找convoy套接字
cd /etc/docker/plugins
echo "unix:///var/run/convoy/convoy.sock" > convoy.spec
##将convoy守护进程开启生成的.sock文件放入/etc/docker/plugins目录下的convoy.spec文件中,docker就可以识别。其中convoy.spec文件之前是不存在的,convoy.spec,必须以spec结尾,convoy是插件名
convoy create vol1 ##创建卷
convoy list ##列出所有卷
docker run -it --name vm1 -v vol1:/data ubuntu ##使用卷,运行容器,挂载到容器内的data目录
ls /mnt/nfs/vol1/ ##退出容器可以看到创建的文件在本地
在server2 上也可以看到
注意:
由上可知server1和server2上看到的内容一致,即不同主机间实现了共享存储
这时即使容器crash掉了,数据已经保存在了本地,可以随时迁移容器
(4)恢复删除的容器中的数据
模拟:假设vm1容器挂掉了,在server2上去恢复这个容器
server2上恢复,不用新建convoy是因为我们是通过nfs同步的,一个节点的信息其它节点都可以看到
docker rm -f vm1 ##在server1中挂掉vm1
docker run -it --name vm1 -v vol1:/data ubuntu ##在server2上恢复
convoy delete vol1 ##先删除所有占用存储的容器
convoy list ##server2上也自动清掉