编者注:
本文根据有容云技术实施团队原创分享内容整理。对Docker技术感兴趣、或对本文中细节需继续探讨的朋友,欢迎加入我们参与讨论!
特别鸣谢中生代技术群分享支持。
注:本期分享由张朝潞原创,有容云整理发布,转载请注明出处
作者介绍:
张朝潞,有容云(Yourun Cloud)平台存储架构师。曾工作于UIT,华三,腾讯,专注分布式存储的研究和开发,对云计算存储解决方案方面有很深的技术造诣和行业理解。
本次交流将与大家分享Docker Volume plugin相关的内容。今日主题是窥探Docker中的Volume plugin内幕。
因为应用数据对安全,可用性,共享,性能等方面的要求和Root Image的要求完全不一样,所以Docker并不推荐采用Root Image的存储方式来存储应用数据,而是采用了Volume这样一个独立的数据访问接口。
应用通过Volume去访问相关的数据,Volume的实现和CoW的分层文件系统完全独立,通过Volume plugin机制可轻易驱动外部存储。
下面我们就围绕Volume Plugin Introduction、Container and Volume、Docker Volume Plugin、自定义Volume Plugin四个方面来展开。
一. Volume Plugin Introduction
通过Volume机制,Docker可以轻易地将主机目录挂载到容器中;通过Docker的Volume Plugin机制,使Docker能够方便地整合第三方存储,为Docker提供Volume。
出处链接:https://github.com/docker/docker/blob/master/docs/extend/plugins.md
二. Container and Volume
1 .Container如何使用Volume?
Volume机制可以使容器访问存储都使用统一的接口(文件接口),对于容器中的进程来说,Volume就是一个已挂载的目录,容器内进程使用该目录就与普通目录一样。
Docker使用Container结构管理容器,Container结构中有map类型的MountPoints变量,用于存储Container中所有已挂载的Volume即是MountPoint结构,MountPoint结构保存挂载目录和Volume结构的基本信息,并且管理目录的权限控制、挂载传播方式等特性。
Volume是个interface,Docker实现两种Volume:①基于主机文件系统。②基于Volume Plugin。
Container中的Volume
2.基于主机文件系统提供Volume
容器启动:docker run -i -v /data ubuntu:latest /bin/bash
容器内看到的挂载信息:
/etc/resolv.conf、/etc/hostname和/etc/hosts三个文件是为了解决每个Container都拥有自己的Hostname和DNS配置,使用了bind mount将主机的文件,挂载到Container内部。/data也是使用了同样的方式将主机的目录挂载到Container中。下面是主机挂载到Container的文件:
查看/dev/disk/by-uuid/88f22c9e-9d5d-4c7e-8984-eba8446361e6是链接文件指向/dev/sda2,这是主机的根目录文件系统。
3 .Container中的Volume
容器启动:
docker run -i -v cvol1:/data –volume-driver=convoy ubuntu:latest /bin/bash
将volume: dockervol挂载到容器目录/data
容器内看到的挂载信息:
此时挂载卷信息:
三. Docker Volume Plugin
1. Docker Volume Plugin框架
Docker volume框架
1.) Docker Daemon对Volume的管理
Docker Daemon中的Volume
如上图,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,提供如下命令:
2.) docker volume 管理
基于本地文件系统的volume框架
Docker提供两个接口Volume和Driver,所有提供给Docker使用的Volume必须实现Volume接口。后端驱动需要实现Driver接口。Driver是对提供出去的Volume进行管理。目前Docker实现了两种Volume &Driver。
① 基于本地文件系统的Volume
可以在执行Docker create或Docker run时,通过-v参数将主机的目录作为容器的数据卷。这部分功能便是基于本地文件系统的volume管理。上图中蓝色部分LocalVolume和Root。这两个结构就是对主机目录和文件进行管理,具体的对应关系如下图:
从上图可以看出,基于本地文件系统的卷管理,Driver便是卷的根目录/var/lib/docker/volumes。一个卷就是根目录下的一个子目录。
② 适配Plugin的Volume
Docker为了支持第三方存储方案,在1.8版本引入Volume Plugin机制,Volume Adapter和Volume Driver Adapter分别实现了接口Volume和Driver接口,用于表示由Plugin提供的Volume和Plugin driver。在下一小节详细描述。
基于本地文件系统的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。
3 .) docker plugin 实现原理
Volume Plugin实现原理
① Docker Plugin机制
上一节已经说过Docker针对Volume Plugin实现了两个适配类型Volume Driver Adapter和Volume Adapter。
- Volume Driver Adapter : 实现Driver接口,用于抽象各种Plugin的驱动,该类型可以适配所有符合规范的Volume Plugin,对Plugin进行管理。
- Volume Adapter : 实现Volume接口,用于抽象所有Plugin提供的Volume,该类型可以适配所有符合规范的Volume Plugin提供的类型,对Volume进行管理。
通过抽象,对于Plugin和其提供的Volume,Docker Daemon结构和Container结构使用上述两个适配类型,对Plugin和Volume进行管理和使用。
上述两个类型都有一个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类型对象。
上图中左下方,Plugins结构类型的全局变量Storage保存所有被Docker Daemon发现的Plugin结构,Plugin结构代表外部插件。
② Docker Plugin的发现过程
Docker Daemon通过Unix域套接字与Plugin Daemon进行通信。所以Plugin需要让Docker Daemon知道Plugin的Unix域套接字文件的路径。再执行命令:docker run ... -v volumename:/data --volume-driver=convoy
发现步骤:
- 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字段中。
③ 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:
- 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便可。
例子:GlusterFS就是使用这个包,基于GlusterFS提供Volume。https://github.com/calavera/docker-volume-glusterfs
关于Docker存储方面的内容,我们之前分享过一篇叫《Docker容器对存储的定义(Volume 与 Volume Plugin) 》的文章,感兴趣的朋友可以关注有容云搜索下。好的,我们今天的分享先告一个段落,谢谢大家!
温馨提示:
对Docker容器技术或容器生产实施感兴趣的朋友欢迎加群讨论。我们汇集了Docker容器技术落地实施团队精英及业内技术派高人,在线为您分享Docker技术干货。我们的宗旨是为了大家拥有更专业的平台交流Docker实战技术,我们将定期邀请嘉宾做各类话题分享及回顾,共同实践研究Docker容器生态圈。