Docker 的插件式设计
http://www.tuicool.com/articles/MnIRZvJ
http://uzhima.com/2016/08/02/what-is-docker-volume-plugin/
在之前的『闲话云计算』一文中曾提到过:构成云计算的『干细胞』是 计算、存储和网络 。Docker 作为云计算领域的新生力量,自然少不了对这三要素的关注,我们来具体看看他是怎么设计的。
众所周知,Docker 提供了容器的运行环境,通过LXC对CPU计算资源进行隔离,运行在每一台OS之上, 计算资源 是他最容易控制和管理的,但对 网络 和 存储 的支持在之前一直不是特别理想,直到1.7之后的版本推出了 插件(Plugin) 的概念才有所改善。有了插件系统提供的能力,开发者可以根据Docker的运行周边环境,定制属于自己的网络和存储方案,对Engine进行扩展,满足自己容器化的需求。比如,阿里云上的 Docker 容器就可以使用 VPC网络插件 ,给容器分配虚拟IP,实现容器在VPC内的网络互联;还可以使用 OSS 存储插件 ,实现容器对 OSS Bucket 的访问,扩展容器的存储能力。
这就是Docker,对『计算』的完全掌控,对『存储』和『网络』的基本支持,同时通过插件扩展存储和网络的边界,与现有云计算的成果无缝融合。 除了有存储Volume、网络Network,Docker还提供了认证Authorization、IP地址管理IPAM等插件类型。
存储插件(Volume Plugin)
下面主要介绍下 Docker存储插件 的设计和实现。

图片来源: slideshare
Docker Plugin 是以Web Service的服务运行在每一台Docker Host上的,通过HTTP协议传输RPC风格的JSON数据完成通信。
Plugin的启动和停止,并不归Docker管理,Docker Daemon依靠在缺省路径下查找Unix Socket文件,自动发现可用的插件。
当客户端与Daemon交互,使用插件创建数据卷时,Daemon会在后端找到插件对应的 socket 文件,建立连接并发起相应的API请求,最终结合Daemon自身的处理完成客户端的请求。
VolumePlugin所定义的 API接口 有8个:
- /VolumeDriver.Create
- /VolumeDriver.Remove
- /VolumeDriver.Mount
- /VolumeDriver.Path
- /VolumeDriver.Unmount
- /VolumeDriver.Get
- /VolumeDriver.List
- /VolumeDriver.Capabilities
在Docker官方文档中列出的Volume Plugins有近 20个 。
OSS存储插件(OSSFS Volume Plugin)
OSS 是阿里云的对象存储服务,解决文件类存储的需求。有一个基于 OSS 的文件系统的实现,叫 ossfs 。可以把某个 OSS Bucket 挂载到OS中的一个分区上,这样就好像访问本地文件一样,访问 OSS 文件。
文件系统 1K-块 已用 可用 已用% 挂载点
ossfs 256T 0 256T 0% /mnt/i/ossfs/mytestbucket
在这个基础上,编写了一个Docker数据卷的插件,使用该插件,可以创建 ossfs 数据卷,并Bind到容器中使用。
认识插件接口(Volume Plugin API)
为了对插件有一个直观的认识,我们从实践出发,拿OSSFS插件举例,追踪接口调用,来帮助我们理解整个过程,加深理解。
OSSFS插件并不是这次的重点,因此它的启动过程以及内部实现细节并不在这次讨论范围,会逐一略过。我用的是阿里云容器服务自带的OSSFS插件,插件启动后会生成一个unix socket文件 /run/docker/plugins/ossfs.sock 。
为了便于监控插件API的日志,我们通过socat搭建一个代理,伪造一个新的socket文件 ossfs2.sock ,对这个代理的请求会被输出到终端上。
sudo socat -t100 -v UNIX-LISTEN:/run/docker/plugins/ossfs2.sock,mode=777,reuseaddr,fork UNIX-CONNECT:/run/docker/plugins/ossfs.sock
这时,在Docker Daemon看来我们就拥有了两个插件,ossfs和ossfs2,不过二者本质上是同一个web service。
创建Volume
下面,使用插件ossfs2创建数据卷 ff001 ,目的是为了获取请求日志。
[root@node1 ~]# docker volume create -d ossfs2 --name=ff001 -o bucket=mytestbucket -o ak_id=xxx -o ak_secret=xxxxxx -o url=vpc100-oss-cn-hangzhou.aliyuncs.com
ff001
在刚才的socat命令下,可以看到打印日志,摘取如下:
2016/08/02 17:04:49.780139 length=143 from=0 to=142 POST /Plugin.Activate HTTP/1.1\r Host: \r
User-Agent: Go-http-client/1.1\r
Content-Length: 1\r
Accept: application/vnd.docker.plugins.v1.2+json\r
\r < 2016/08/02 17:04:49.780763 length=165 from=0 to=164
HTTP/1.1 200 OK\r
{"Implements": ["VolumeDriver"]}
2016/08/02 17:04:49.781068 length=162 from=143 to=304 POST /VolumeDriver.Get HTTP/1.1\r {"Name":"ff001"} < 2016/08/02 17:04:49.781364 length=229 from=165 to=393
HTTP/1.1 500 Internal Server Error\r
{"Mountpoint":"","Err":"Invalid ossfs options.","Volumes":null,"Volume":null}
2016/08/02 17:04:49.781708 length=316 from=305 to=620 POST /VolumeDriver.Create HTTP/1.1\r {"Name":"ff001","Opts":{"ak id":"xxxx","ak secret":"xxxxxx","bucket":"mytestbucket","url":"vpc100-oss-cn-hangzhou.aliyuncs.com"}} < 2016/08/02 17:04:49.849440 length=188 from=394 to=581
HTTP/1.1 200 OK\r
{"Mountpoint":"","Err":"","Volumes":null,"Volume":null}
2016/08/02 17:04:49.867860 length=163 from=621 to=783 POST /VolumeDriver.Path HTTP/1.1\r {"Name":"ff001"} < 2016/08/02 17:04:49.872552 length=220 from=582 to=801
HTTP/1.1 200 OK\r
{"Mountpoint":"/mnt/i/ossfs/mytestbucket","Err":"","Volumes":null,"Volume":null}
2016/08/02 17:04:51.984118 length=148 from=784 to=931 POST /VolumeDriver.List HTTP/1.1\r {} < 2016/08/02 17:04:51.985414 length=316 from=802 to=1117
{"Mountpoint":"","Err":"","Volumes":[{"Name":"ff001","Mountpoint":"/mnt/i/ossfs/mytestbucket"}],"Volume":null}
所有的请求都是POST方法,URI、request body和response body我都已经加粗。简单解释下, /Plugin.Activate 接口是Plugin的公共接口,是做第一次Handshake,Plugin会返回类型是Volume还是Network等。一个Volume的创建过程,会先通过 Get 判断名称是否存在,然后执行 Create 做数据卷的初始化,接着通过 Path 接口获取Volume的本地路径。最后的 List 是用来查询这个插件下有哪些Volume的,是被其他客户端发起的,跟这次的创建过程并无关系。
至此,一个Volume就创建完成了,但还没有被容器使用。
使用Volume
下面我们看看,在容器使用Volume时,Plugin提供了哪些接口。执行下面命令:
[root@node1 ~]# docker run -ti --volume-driver=ossfs2 -v ff001:/oss busybox ls /oss
1.jpeg
创建并启动一个容器,挂载数据卷 ff001 到容器内的 /oss 目录,然后执行命令 ls /oss 查看目录下文件,命令退出后容器停止。
在ossfs2.sock处抓取到请求日志如下:
2016/08/02 17:35:18.680407 length=164 from=13610 to=13773 POST /VolumeDriver.Mount HTTP/1.1\r {"Name":"ff001"} < 2016/08/02 17:35:18.680923 length=220 from=24348 to=24567
HTTP/1.1 200 OK\r
{"Mountpoint":"/mnt/i/ossfs/mytestbucket","Err":"","Volumes":null,"Volume":null}
2016/08/02 17:35:19.143549 length=166 from=13774 to=13939 POST /VolumeDriver.Unmount HTTP/1.1\r {"Name":"ff001"} < 2016/08/02 17:35:19.144880 length=188 from=24568 to=24755
HTTP/1.1 200 OK\r
{"Mountpoint":"","Err":"","Volumes":null,"Volume":null}
启动容器时,Daemon会请求Plugin的 Mount 接口,然后与容器内的目录绑定,容器停止时会请求 Unmount 接口。
删除Volume
我们使用以下命令删除Volume
docker volume rm ff001
当有容器在使用Volume时,删除会失败(Daemon中有记录)。成功删除时,会打印以下日志:
2016/08/02 18:29:23.833416 length=162 from=34688 to=34849 POST /VolumeDriver.Get HTTP/1.1\r {"Name":"ff001"} < 2016/08/02 18:29:23.833928 length=249 from=59892 to=60140
HTTP/1.1 200 OK\r
{"Mountpoint":"","Err":"","Volumes":null,"Volume":{"Name":"ff001","Mountpoint":"/mnt/i/ossfs/mytestbucket"}}
2016/08/02 18:29:23.834274 length=165 from=34850 to=35014 POST /VolumeDriver.Remove HTTP/1.1\r {"Name":"ff001"} < 2016/08/02 18:29:24.027498 length=188 from=60141 to=60328
HTTP/1.1 200 OK\r
{"Mountpoint":"","Err":"","Volumes":null,"Volume":null}
一个 Get ,一个 Remove 就完成了删除时的接口调用。
自己写一个?
根据上面的介绍,相比你已经大概清楚一个Volume Plugin需要做哪些事情,提供哪些接口,以及在什么时候使用这些接口了。 如果,你想自己写一个Volume Plugin,也是很简单的事情。Docker已经提供了Go语言的 Plugin SDK 『go-plugins-helpers』 ,内置了基础API接口,你只需要引用它们,并根据自己存储的特征具体实现这些接口就可以了。同样可以参考这些 公开插件 的文档或代码,比如 Flocker , Glusterfs 等。
Docker 的插件式设计的更多相关文章
- atitit.插件体系设计总结o73.doc
1. 两大类型:微内核(级联树形结构)与巨内核(管理容器,并联结构). 1 2. 通用插件接口 1 3. 插件的绑定and 初始化 2 4. 微内核插件平台设计 2 5. 参考 2 1. 两大类型:微 ...
- (1)从底层设计,探讨插件式GIS框架的实现
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 研一时,听当时的师兄推荐,买了蒋波涛的一本关于GIS插件框架的书.当时 ...
- [连载]《C#通讯(串口和网络)框架的设计与实现》- 9.插件引擎设计
目 录 第九章 插件引擎设计... 2 9.1 框架的契约-接口... 2 9.2 插件的雏形-抽象类... 3 9.3 ...
- 使用 SailingEase WinForm 框架构建复合式应用程序(插件式应用程序)
对于一些较小的项目,具备一定经验的开发人员应该能够设计和构建出便于进行维护和扩展的应用程序.但是,随着功能模块数量(以及开发维护这些部件的人员)的不断增加,对项目实施控制的难度开始呈指数级增长. Sa ...
- HTML5、CSS3响应式设计——笔记
1.1.响应式网页设计 响应式网页设计(RWD,Responsive Web Design)这个术语,由伊桑·马科特(EthanMarcotte)提出.他在A List Apart 发表了一篇开创性的 ...
- Android应用插件式开发解决方法
转自:http://blog.csdn.net/arui319/article/details/8109650 一.现实需求描述 一般的,一个Android应用在开发到了一定阶段以后,功能模块将会越来 ...
- 超棒的响应式设计测试书签和工具(bookmarks)(转)
一.测试书签(bookmarks) Viewport Resizer 这个书签号称拥有158个国家3万多活跃的用户,主要特性: 完全自定制 方便的添加自定义尺寸 手动的横竖屏切换 自动的横竖屏切换 ( ...
- C#实现插件式架构的方法
插件式架构,一种全新的.开放性的.高扩展性的架构体系.插件式架构设计近年来非常流行,基于插件的设计好处很多,把扩展功能从框架中剥离出来,降低了框架的复杂度,让框架更容易实现.扩展功能与框架以一种很松的 ...
- AvalonDock 2.0+Caliburn.Micro+MahApps.Metro实现Metro风格插件式系统(一)
随着IOS7由之前UI的拟物化设计变为如今的扁平化设计,也许扁平化的时代要来了,当然我们是不是该吐槽一下,苹果什么时候也开始跟风了,自GOOGLE和微软界面扁平化过后,苹果也加入了这一队伍. Aval ...
随机推荐
- day67 ORM模型之高阶用法整理,聚合,分组查询以及F和Q用法,附练习题整理
归纳总结的笔记: day67 ORM 特殊的语法 一个简单的语法 --翻译成--> SQL语句 语法: 1. 操作数据库表 创建表.删除表.修改表 2. 操作数据库行 增.删.改.查 怎么连数据 ...
- sql 的一些总结
如果用到“每” 就要用到group by 例:每个部门有多少人,就要用到分组技术 聚合函数一般作用在多条记录上 having 是分组厚的筛选条件,分组厚的数据组内再筛选,where 则是在分组 ...
- Ubuntu16.04+Opencv3.3的安装教程
需要准备的基本材料(请先看完整个安装过程再进行下面的操作): 一.到Opecv-Release的Github项目上下载最新的Opencv版本,注意---基于python2.7,可选用 OpenCV2. ...
- sqlserver日志文件
过程: 昨天下午数据库奔溃,表现就是连不上数据库了,重启服务之后好了. 查询日文文件 , “Autogrow of file 'XX_log' in database 'XX' was cance ...
- 001.Redis简介及安装
一 Redis简介 1.1 Redis 简介 Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis 与其他 key-value 缓存产品有以下三个特点: ...
- linux基础 用户(组)管理
修改/etc/shadow文件 1.chage -m MINDAYS USERNAME#设置密码修改最小天数2.chage -M MAXDAYS USERNAME#设置密码修改最大天数3.chage ...
- Python、进程间通信、进程池、协程
进程间通信 进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的. 进程队列queue 不同于线程queue,进程 ...
- Yolov3实战 基于darknet window版
特此声明:训练过程预先认为你对yolov3神经网络有一定了解的基础上进行. 目录 一.先备齐下面的工具(预先善其事,必先利其器) 二.接下里使用我们的工具编译我们的环境 三. 训练自己的数据集 1. ...
- Qt控制台例子
功能:实现通过命令行方式保存文件 #include <QCoreApplication> #include <iostream> #include <QString> ...
- C++学习笔记54:关联容器,函数对象
关联容器的特点 1.每个关联容器都有一个键(key) 2.可以根据键高效查找元素 集合set 集合用来存储一组无重复的元素,由于集合的元素本身是有序的,可以高效地查找元素,也可以方便地指定大小范围的元 ...