窥探Docker中的Volume Plugin内幕

izcdxtfrc 3年前
   <p>作者介绍: 张朝潞,有容云(Yourun Cloud)平台存储架构师。曾工作于UIT,华三,腾讯,专注分布式存储的研究和开发,对云计算存储解决方案方面有很深的技术造诣和行业理解。</p>    <p>本次交流将与大家分享Docker Volume plugin相关的内容。今日主题是窥探Docker中的Volume plugin内幕。</p>    <p>因为应用数据对安全,可用性,共享,性能等方面的要求和Root Image的要求完全不一样,所以Docker并不推荐采用Root Image的存储方式来存储应用数据,而是采用了Volume这样一个独立的数据访问接口。</p>    <p>应用通过Volume去访问相关的数据,Volume的实现和CoW的分层文件系统完全独立,通过Volume plugin机制可轻易驱动外部存储。</p>    <p>下面我们就围绕Volume Plugin Introduction、Container and Volume、Docker Volume Plugin、自定义Volume Plugin四个方面来展开。</p>    <p>一. Volume Plugin Introduction 通过Volume机制,Docker可以轻易地将主机目录挂载到容器中;通过Docker的Volume Plugin机制,使Docker能够方便地整合第三方存储,为Docker提供Volume。</p>    <p><img src="https://simg.open-open.com/show/8414e7d30cf86fcb13a64a80541dc408.jpg"></p>    <p>出处链接:https://github.com/docker/docker/blob/master/docs/extend/plugins.md</p>    <p>二. Container and Volume</p>    <p>1 .Container如何使用Volume? Volume机制可以使容器访问存储都使用统一的接口(文件接口),对于容器中的进程来说,Volume就是一个已挂载的目录,容器内进程使用该目录就与普通目录一样。</p>    <p>Docker使用Container结构管理容器,Container结构中有map类型的MountPoints变量,用于存储Container中所有已挂载的Volume即是MountPoint结构,MountPoint结构保存挂载目录和Volume结构的基本信息,并且管理目录的权限控制、挂载传播方式等特性。</p>    <p>Volume是个interface,Docker实现两种Volume:①基于主机文件系统。②基于Volume Plugin。</p>    <p><img src="https://simg.open-open.com/show/483e772b49ba31bec2fc1eb0cf5f3590.jpg"></p>    <p>Container中的Volume</p>    <p><img src="https://simg.open-open.com/show/0c0ed6d49c6da7a11a961dd9fcb5b210.jpg"></p>    <p>2.基于主机文件系统提供Volume 容器启动:docker run -i -v /data ubuntu:latest /bin/bash 容器内看到的挂载信息:</p>    <p>/etc/resolv.conf、/etc/hostname和/etc/hosts三个文件是为了解决每个Container都拥有自己的Hostname和DNS配置,使用了bind mount将主机的文件,挂载到Container内部。/data也是使用了同样的方式将主机的目录挂载到Container中。下面是主机挂载到Container的文件:</p>    <p><img src="https://simg.open-open.com/show/9ce66fbd32032944d0c0b8aec03e4f41.jpg"></p>    <p>查看/dev/disk/by-uuid/88f22c9e-9d5d-4c7e-8984-eba8446361e6是链接文件指向/dev/sda2,这是主机的根目录文件系统。</p>    <p><img src="https://simg.open-open.com/show/158a8d06eab797b3c29d3031570730be.jpg"></p>    <p>3 .Container中的Volume 容器启动: docker run -i -v cvol1:/data –volume-driver=convoy ubuntu:latest /bin/bash 将volume: dockervol挂载到容器目录/data 容器内看到的挂载信息:</p>    <p><img src="https://simg.open-open.com/show/eaaac1e5fe262c41aac4c23ace6ef1f3.jpg"></p>    <p>此时挂载卷信息:</p>    <p><img src="https://simg.open-open.com/show/e07d93194a27162308562db655295357.jpg"></p>    <p>三. Docker Volume Plugin</p>    <ol>     <li>Docker Volume Plugin框架</li>    </ol>    <p><img src="https://simg.open-open.com/show/91ae6f5d474fdfb5191f34d6c2f1c145.jpg"></p>    <p>Docker volume框架 1.) Docker Daemon对Volume的管理</p>    <p><img src="https://simg.open-open.com/show/0783ffbad0f52030b7aec327b2c849c1.jpg"></p>    <p>Docker Daemon中的Volume</p>    <p>如上图,Docker Daemon结构中有个成员Volumes,类型是VolumeStore类型,包含一组管理Volume的函数:Create、Remove、List、Get、Refs ...。通过两个变量,管理Container和Volume的关系。 names : map结构,Key是Volume的name,value是实现Volume接口的结构对象。存储该Daemon内所有的Volume。 ReFS: map结构,key是volume的name,value是string数组保存引用该Volume的Container ID。 Docker Daemon通过Volumes变量,就可以管理所有的Volume,提供如下命令:</p>    <p><img src="https://simg.open-open.com/show/0f077e92877631007f5044cf1ee88af3.jpg"></p>    <p>2.) docker volume 管理</p>    <p><img src="https://simg.open-open.com/show/dec11a6dc2a622a2f9e916fe6404d733.jpg"></p>    <p>基于本地文件系统的volume框架</p>    <p>Docker提供两个接口Volume和Driver,所有提供给Docker使用的Volume必须实现Volume接口。后端驱动需要实现Driver接口。Driver是对提供出去的Volume进行管理。目前Docker实现了两种Volume &Driver。</p>    <p>① 基于本地文件系统的Volume</p>    <p>可以在执行Docker create或Docker run时,通过-v参数将主机的目录作为容器的数据卷。这部分功能便是基于本地文件系统的volume管理。上图中蓝色部分LocalVolume和Root。这两个结构就是对主机目录和文件进行管理,具体的对应关系如下图:</p>    <p><img src="https://simg.open-open.com/show/d4f9396605f8aeb474e4e09c6f851927.jpg"></p>    <p>从上图可以看出,基于本地文件系统的卷管理,Driver便是卷的根目录/var/lib/docker/volumes。一个卷就是根目录下的一个子目录。</p>    <p>② 适配Plugin的Volume Docker为了支持第三方存储方案,在1.8版本引入Volume Plugin机制,Volume Adapter和Volume Driver Adapter分别实现了接口Volume和Driver接口,用于表示由Plugin提供的Volume和Plugin driver。在下一小节详细描述。</p>    <p>基于本地文件系统的Volume框架中,全局变量drivers保存了所有注册到Docker Daemon的Driver。Docker Daemon需要对Volume进行管理操作时,通过GetDriver函数从drivers变量中获取指定名称的Driver,通过Driver可以通过Create,Remove,List,Get对Driver中Volume进行管理。通过Get函数获取Volume结构,可以对卷进行管理操作:Name,DriverName,Path,Mount,Unmount。</p>    <p>3 .) docker plugin 实现原理</p>    <p><img src="https://simg.open-open.com/show/1b2a16f3ffdde5e95810c0342abd4f60.jpg"></p>    <p>Volume Plugin实现原理 ① Docker Plugin机制</p>    <pre>   上一节已经说过Docker针对Volume Plugin实现了两个适配类型Volume Driver Adapter和Volume Adapter。  </pre>    <p>Volume Driver Adapter : 实现Driver接口,用于抽象各种Plugin的驱动,该类型可以适配所有符合规范的Volume Plugin,对Plugin进行管理。 Volume Adapter : 实现Volume接口,用于抽象所有Plugin提供的Volume,该类型可以适配所有符合规范的Volume Plugin提供的类型,对Volume进行管理。 通过抽象,对于Plugin和其提供的Volume,Docker Daemon结构和Container结构使用上述两个适配类型,对Plugin和Volume进行管理和使用。</p>    <p>上述两个类型都有一个Volume Driver Proxy结构变量proxy,用于与Plugin进行通信。Volume Driver Proxy结构实现了与plugin通信的接口volume Driver,提供Create、Remove、Path、Mount、Unmount、List、Get接口与通信。Volume Driver Proxy结构的client变量,使用这个变量与Plugin Daemon进行通信。client变量是Plugin结构中的Client变量是Client结构类型,包含了http.Client类型对象。</p>    <pre>  上图中左下方,Plugins结构类型的全局变量Storage保存所有被Docker Daemon发现的Plugin结构,Plugin结构代表外部插件。    ② Docker Plugin的发现过程  </pre>    <p>Docker Daemon通过Unix域套接字与Plugin Daemon进行通信。所以Plugin需要让Docker Daemon知道Plugin的Unix域套接字文件的路径。再执行命令:docker run ... -v volumename:/data --volume-driver=convoy</p>    <p>发现步骤: Docker Daemon首先会在/run/docker/plugins搜索对应的套接字文件,套接字文件名必须和Volume Driver名一致,如上述命令,便是搜索convoy.sock。 如果上一步搜索不到,则到/etc/docker/plugins和/usr/lib/docker/plugins两个目录搜索spec或json文件。文件中指定套接字文件的URL。如:unix:///var/run/convoy/convoy.sock 。 根据前面两步发现的Unix域套接字URL,构建Plugin对象,并将新建对象加入到全局变量storage的Plugins字段中。</p>    <pre>  ③ Docker Volume Plugin的使用  执行docker run命令时,指定参数--volume-driver=convoy。Docker Daemon会先从storage.plugins中寻找Plugin结构,如果没有找到,就发起②的发现流程。找到则直接使用Client构建volume Driver Proxy。    ④ Docker Daemon与Plugin Daemon通信的API  前面已经提到过,Docker Daemon和Plugin Daemon基于Unix域套接字,使用Restful API进行通信,下面是详细的API:  </pre>    <p>Plugin.Activate : 发送一个请求到Plugin,Plugin返回其类型,如convoy就返回Volume Driver。相当于Docker和Plugin Daemon直接的连接建立的握手报文。 VolumeDriver.Create : 创建一个卷,Docker会发送卷名称和参数发送给插件,卷插件会根据Docker发送过来的参数创建一个卷,并和这个卷名称关联。 VolumeDriver.Mount : 挂载一个卷到本机,Docker会把卷名称和参数发送给参数。插件会返回一个本地路径给Docker,这个路径就是卷所在的位置。Docker在创建容器的时候,会将这个路径挂载到容器中。 VolumeDriver.Path : 一个卷创建成功后,Docker会调用Path API来获取这个卷的路径,随后Docker通过调用Mount API,让插件将这个卷挂载到本机。 VolumeDriver.Unmount : 当容器退出时,Docker daemon会发送Umount API给插件,通知插件这个卷不再被使用,插件可以对该卷做些清理工作(比如引用计数减一,不同的插件行为不同)。 VolumeDriver.Remove : 删掉特定的卷时调用,当运行"docker rm -v"命令时,Docker会调用该API发送请求给插件。 VolumeDriver.List : 执行docker volume ls命令时,会向plugin发送该请求,获取volume list。 VolumeDriver.Get : 获取Plugin Volume的详细信息。 四. 自定义Volume Plugin 为了方便实现Volume Plugin,Docker提供go-plugins-helper包(https://github.com/docker/go-plugins-helpers),提供基础的功能,仅仅需要实现一个接口volume.Driver,并启动http server便可。</p>    <p><img src="https://simg.open-open.com/show/f206b9842d9e9e1584f98520a6404807.jpg"></p>    <p>例子:GlusterFS就是使用这个包,基于GlusterFS提供Volume。https://github.com/calavera/docker-volume-glusterfs</p>    <p>关于Docker存储方面的内容,我们之前分享过一篇叫《Docker容器对存储的定义(Volume 与 Volume Plugin) 》的文章,感兴趣的朋友可以关注有容云搜索下。好的,我们今天的分享先告一个段落,谢谢大家!</p>    <p>温馨提示:</p>    <p>对Docker容器技术或容器生产实施感兴趣的朋友欢迎加群讨论。我们汇集了Docker容器技术落地实施团队精英及业内技术派高人,在线为您分享Docker技术干货。我们的宗旨是为了大家拥有更专业的平台交流Docker实战技术,我们将定期邀请嘉宾做各类话题分享及回顾,共同实践研究Docker容器生态圈。</p>    <p>via: http://www.ituring.com.cn/article/215542</p>