玩转Docker-------第五部-----------数据卷管理&convoy卷插件
1.什么是Docker数据卷
-
数据卷是一个或多个容器中专门指定的目录,它能够绕过联合文件系统。
卷被设计用作数据持久化、并且是独立于容器的生命周期的。 -
因此,Docker不会在删除容器时自动删除数据卷卷,也不会主动“垃圾回收”掉容器不再使用的卷。
-
数据卷的存在就是想让的容器的数据持久化存在,而且可以实现容器之间的数据共享。
-
通俗地来说,docker容器数据卷可以看成使我们生活中常用的u盘,它存在于一个或多个的容器中,由docker挂载到容器,但不属于联合文件系统,Docker不会在容器删除时删除其挂载的数据卷。
2.为什么要用数据卷
docker分层文件系统:
- 性能差
- 生命周期与容器相同
docker数据卷:
- mount到主机中,绕开分层文件系统
- 和主机磁盘性能相同,容器删除后依然保留
- 仅限本地磁盘,不能随容器迁移
3.docker数据卷提供两种卷
bind mount
是将主机上的目录或文件mount到容器里
- 使用直观高效,易于理解。
- 使用 -v 选项指定路径,格式 :
- bind mount 默认权限是读写rw,可以在挂载时指定只读ro。
- -v选项指定的路径,如果不存在,挂载时会自动创建。
docker managed volume
bind mount必须指定host文件系统路径,限制了移植性。
docker managed volume 不需要指定mount源
bind mount 和docker managed volume对比
相同点:两者都是host文件系统中的某个路径
不同点:
bind mount | docker managed volume | |
---|---|---|
volnme位置 | 可任意指定 | /var/lib/docker/volumes/… |
对已有mount point 影响 | 隐藏并替换为volume | 原有数据复制到volume |
是否支持单个文件 | 支持 | 不支持,只能是目录 |
权限控制 | 可设置为只读 | 无控制,均为读写模式 |
移植性 | 移植性弱,与host path 绑定 | 移植性强,无需指定host 目录 |
4.bind mount应用
bind mount 卷直接将将主机上的目录或文件直接 mount 到容器里,使用直接,高效
[root@server1 ~]# docker run -d --name web1 -p 80:80 -v /opt/website:/usr/share/nginx/html nginx
## 使用-V 参数将server1上的/opt/website 挂载到容器中的 nginx默认发布目录,就是/usr/share/nginx/html
109d379bec099b62853adbe1e01b790ce8c47bd9ba31f4dea17fe9e64b50a1f3
[root@server1 ~]# curl localhost #访问时被拒绝的,因为我们里面还没有资源
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.13.9</center>
</body>
</html>
[root@server1 ~]# docker exec web1 mount #这条命令是在容器中执行mount命令,但是不进入容器
...
/dev/mapper/rhel-root on /usr/share/nginx/html type xfs (rw,relatime,attr2,inode64,noquota)
...
# 可以看到dev/mapper/rhel-root,也就是我们的根目录,挂载到容器的/usr/share/nginx/html
[root@server1 ~]# vim /opt/website/index.html 现在在server1上写一个资源
[root@server1 ~]# curl localhost
www.westos.org
[root@server1 ~]# echo www.westos.org >> /opt/website/index.html #追加
[root@server1 ~]# curl localhost
www.westos.org
www.westos.org
还可以在容器中进行更改:
[root@server1 website]# docker container attach web1
## 这样的方式是登陆不进去的,因为nginx是一个应用容器,但是之前的 ubuntu 为什么可以?
## 查看构建历史
[root@server1 ~]# docker history nginx:latest
IMAGE CREATED CREATED BY SIZE COMMENT
e548f1a579cf 2 years ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
<missing> 2 years ago /bin/sh -c #(nop) STOPSIGNAL [SIGTERM] 0B
<missing> 2 years ago /bin/sh -c #(nop) EXPOSE 80/tcp 0B
<missing> 2 years ago /bin/sh -c ln -sf /dev/stdout /var/log/nginx… 22B
<missing> 2 years ago /bin/sh -c set -x && apt-get update && apt… 53.4MB
<missing> 2 years ago /bin/sh -c #(nop) ENV NJS_VERSION=1.13.9.0.… 0B
<missing> 2 years ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.13.9-… 0B
<missing> 2 years ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B
<missing> 2 years ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 2 years ago /bin/sh -c #(nop) ADD file:27ffb1ef53bfa3b9f… 55.3MB
[root@server1 ~]# docker history ubuntu:latest
IMAGE CREATED CREATED BY SIZE COMMENT
07c86167cdc4 4 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 4 years ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$… 1.9kB
<missing> 4 years ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/poli… 195kB
<missing> 4 years ago /bin/sh -c #(nop) ADD file:b9504126dc5590898… 188MB
对比发现,nginx 镜像只最后只运行了 nginx,没有交互式进入 shell,而 ubuntu 有 /bin/bash
[root@server1 ~]# docker start web1
web1
[root@server1 ~]# docker exec -it web1 bash #-it 是 exec 的选项,bash 表示在容器内执行的操作,打开一个 bash
root@109d379bec09:/# ls
bin dev home lib64 mnt proc run srv tmp var
boot etc lib media opt root sbin sys usr
root@109d379bec09:/# cd /usr/share/nginx/html/
root@109d379bec09:/usr/share/nginx/html# ls
index.html
root@109d379bec09:/usr/share/nginx/html# echo www.westos.org >> index.html
root@109d379bec09:/usr/share/nginx/html# cat index.html
www.westos.org
www.westos.org
www.westos.org
root@109d379bec09:/usr/share/nginx/html#
[root@server1 ~]# curl localhost
www.westos.org
www.westos.org
www.westos.org # 更改了
[root@server1 ~]# cat /opt/website/index.html
www.westos.org
www.westos.org
www.westos.org # 而且容器上的数据其实只是更改了宿主机的数据
这种方式,不存在的目录会自动新建(无论容器还是宿主机),但是会以宿主机为准,绑定挂载到容器后,容器中的对应目录如果有数据,就会被覆盖。
bind mount 方式挂载时默认权限是 rw,可以在挂载时指定只读(ro)。
[root@server1 ~]# ls /opt/
containerd data website
# 此时宿主机时没有 /opt/data1 和 /opt/data2 的,而且容器中没有/data1,data2的目录
[root@server1 ~]# docker run -it --name vm1 -v /opt/data1:/data1 -v /opt/data2:/data2:ro ubuntu
root@0ad999a9ad2f:/# ls
bin data1 dev home lib64 mnt proc run srv tmp var
boot data2 etc lib media opt root sbin sys usr #自动创建了data1和data2的目录
root@0ad999a9ad2f:/# cd data1/
root@0ad999a9ad2f:/data1# touch file1 #data1中可以创建文件
root@0ad999a9ad2f:/data1# cd ../data2 #data2中不可以,因为我们刚才设置了只读挂载
root@0ad999a9ad2f:/data2# touch file2
touch: cannot touch 'file2': Read-only file system
[root@server1 ~]# cd /opt/data2
[root@server1 data2]# touch file2 #但是我们在宿主机中却可以创建
[root@server1 data2]# docker attach vm1
root@0ad999a9ad2f:/data2#
root@0ad999a9ad2f:/data2# ls
file2
5.docker managed volume
查看现有的管理卷:
docker volume ls
我们有些是时候在删除了容器后会有残存的管理卷存在,这是我们就需要去清理它,不然会占用我们的资源:
docker volume prune
docker volume ls
docker run -d --name registry registry
cd /var/lib/docker/volumes/
ls
docker history registry:latest
通过docker volume可以将容器内的内容复制到挂载点:
[root@server1 volumes]# docker run -d --name vm2 -v /usr/share/nginx/html nginx
19a0084bd2802d8388940f2eff0a94bdd62e3b29f47d4138b0064b16df77181a
[root@server1 volumes]# ls
5dccd42f7717a7db4fdb821e60891c991447b5d0f94513b70d22e633083631a7 metadata.db
d0d3d2a058b6da2e1ac7f7c51ee8bbe473b5fcfc18acd84ae0876be662a8f08a
[root@server1 volumes]# cd 5dccd42f7717a7db4fdb821e60891c991447b5d0f94513b70d22e633083631a7/
[root@server1 5dccd42f7717a7db4fdb821e60891c991447b5d0f94513b70d22e633083631a7]# ls
_data
[root@server1 5dccd42f7717a7db4fdb821e60891c991447b5d0f94513b70d22e633083631a7]# cd _data/
[root@server1 _data]# ls
50x.html index.html
docker inspect vm2
curl 172.17.0.4 #nginx默认发布页
echo hello docker! > index.html
curl 172.17.0.4 #可以直接在挂载的目录修改默认发布页
6.docker卷插件简介
docker卷默认使用的是local类型的驱动,只能存在宿主机,跨主机的volume就需要使用第三方的驱动
可以查看链接:
https://docs.docker.com/engine/extend/legacy_plugins/#volume-plugins
Docker Plugin 是以Web Service的服务运行在每一台Docker Host上的,通过HTTP协议传输RPC风格的JSON数据完成通信。Plugin的启动和停止,并不归Docker管理,Docker Daemon依靠在缺省路径下查找Unix Socket文件,自动发现可用的插件。
当客户端与Daemon交互,使用插件创建数据卷时,Daemon会在后端找到插件对应的 socket 文件,建立连接并发起相应的API请求,最终结合Daemon自身的处理完成客户端的请求。
7.convoy卷插件
convoy卷插件支持三种运行方式:devicemapper、NFS、EBS。下面的实验以nfs的运行方式来演示
实验目的:在server1和2底层用nfs来实现数据共享
step1 首先在server1和server2上搭建nfs文件系统:
server1:
yum install -y nfs-utils
systemctl start rpcbind
mkdir /nfs #创建共享目录
chmod 777 /nfs #修改共享目录权限
vim /etc/exports #编辑共享目录文件,否则将不会被共享出去
/nfs *(rw,no_root_squash)
systemctl start nfs
注意:rpcbind服务必须是开启的。这是因为:他是一个RPC服务,主要是在nfs共享时候负责通知客户端,服务器的nfs端口号的。简单理解rpc就是一个中介服务。
server2:
yum install -y nfs-utils
systemctl start nfs-server.service
showmount -e server1 #寻找server1的挂载目录
mkdir /nfs
mount server1:/nfs /nfs
df
测试:
在server2中:
cd /nfs/
touch file
在server1中:
cd /nfs/
ls #查看到file
说明两个节点的/nfs实现同步了
step2 配置convoy环境:
docker官方只提供了卷插件的api,开发者可以根据实际需求定制卷插件驱动。
在server1中:
tar zxf convoy.tar.gz
cd convoy/
cp convoy* /usr/local/bin/ #将二进制文件加入到PATH路径
mkdir /etc/docker/plugins #创建docker的插件目录
convoy daemon --drivers vfs --driver-opts vfs.path=/nfs &> /dev/null &
cd /nfs
ls
注意:第一次运行上面的convoy daemon命令的时候,会在/nfs目录下生成一个config文件夹,这个文件夹不要删除,不然客户端的convoy命令就会用不了
echo "unix:///var/run/convoy/convoy.sock" > /etc/docker/plugins/convoy.spec
#将convoy守护进程开启生成的.sock文件放入/etc/docker/plugins目录下的convoy.spec文件中,docker就可以识别。(其中convoy.spec文件之前是不存在的)
cat /etc/docker/plugins/convoy.spec
在server2中同样配置convoy环境:
scp -r server1:convoy .
cd convoy/
cp convoy* /usr/local/bin/ #将二进制文件加入到PATH路径
mkdir /etc/docker/plugins #创建docker的插件目录
echo "unix:///var/run/convoy/convoy.sock" > /etc/docker/plugins/convoy.spec
convoy daemon --drivers vfs --driver-opts vfs.path=/nfs &> /dev/null &
cd /nfs
ls
step3 创建卷:
docker volume ls
convoy create vol1
step4 操作卷:
在server2中运行容器,指定卷为刚才新创建的vol1:
[root@server1 nfs]# docker run -it --name vm5 -v vol1:/data ubuntu
root@b63cf80ce665:/# ls
bin data etc lib media opt root sbin sys usr
boot dev home lib64 mnt proc run srv tmp var
root@b63cf80ce665:/# cd data/
root@b63cf80ce665:/data# ls
50x.html index.html
root@b63cf80ce665:/data# touch file{1..10}
root@b63cf80ce665:/data# ls
50x.html file1 file10 file2 file3 file4 file5 file6 file7 file8 file9 index.html
root@b63cf80ce665:/data# [root@server1 nfs]#
[root@server1 nfs]# cd /nfs/
[root@server1 nfs]# ls
config file vol1
[root@server1 nfs]# cd vol1/
[root@server1 vol1]# ls
50x.html file1 file10 file2 file3 file4 file5 file6 file7 file8 file9 index.html
可以看到创建的文件在本地
而且server2也能看见了
这个时候容器就算 crash 掉,数据也已经保存在了本地,可以随时迁移容器
[root@server1 vol1]# docker rm -f vm5 # 挂掉VM1 容器
[root@server2 ~]# docker run -it --name vm6 -v vol1:/data ubuntu
##在 server2 上恢复,这里不用再次去新建 convoy 是因为插件是通过 nfs 同步的,一个节点新建,其他节点都能看到
root@4bfc57f68d52:/# cd data/
root@4bfc57f68d52:/data# ls
file1 file10 file2 file3 file4 file5 file6 file7 file8 file9
清理convoy存储
先删除所有占用存储的容器。
[root@server2 ~]# umount /mnt/nfs/
[root@server2 ~]# docker rm -f vm1
dvm1
[root@server2 ~]# convoy list
{}
## server2上取消挂载后就不见这个卷了
[root@server1 vol1]# docker rm -f vm5
[root@server1 ~]# convoy delete vol1
DEBU[12302] Calling: DELETE, /volumes/, request: DELETE, /v1/volumes/ pkg=daemon
DEBU[12302] event=delete object=volume pkg=daemon reason=prepare volume=vol1
DEBU[12302] Cleaning up /mnt/nfs/vol1 for volume vol1 pkg=vfs
DEBU[12302] event=delete object=volume pkg=daemon reason=complete volume=vol1
[root@server1 ~]# convoy list
{}
## server1上删除容器后 执行第二部删除操作,就看不到了
[root@server1 ~]# cd /mnt/nfs/
[root@server1 nfs]# ls
config file1 ## 数据目录也不见了
补充几条命令:
docker container prune #删除停止的容器
docker volume prune #删除没有被使用的卷