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 的插件式设计的更多相关文章

  1. atitit.插件体系设计总结o73.doc

    1. 两大类型:微内核(级联树形结构)与巨内核(管理容器,并联结构). 1 2. 通用插件接口 1 3. 插件的绑定and 初始化 2 4. 微内核插件平台设计 2 5. 参考 2 1. 两大类型:微 ...

  2. (1)从底层设计,探讨插件式GIS框架的实现

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 研一时,听当时的师兄推荐,买了蒋波涛的一本关于GIS插件框架的书.当时 ...

  3. [连载]《C#通讯(串口和网络)框架的设计与实现》- 9.插件引擎设计

    目       录 第九章           插件引擎设计... 2 9.1           框架的契约-接口... 2 9.2           插件的雏形-抽象类... 3 9.3     ...

  4. 使用 SailingEase WinForm 框架构建复合式应用程序(插件式应用程序)

    对于一些较小的项目,具备一定经验的开发人员应该能够设计和构建出便于进行维护和扩展的应用程序.但是,随着功能模块数量(以及开发维护这些部件的人员)的不断增加,对项目实施控制的难度开始呈指数级增长. Sa ...

  5. HTML5、CSS3响应式设计——笔记

    1.1.响应式网页设计 响应式网页设计(RWD,Responsive Web Design)这个术语,由伊桑·马科特(EthanMarcotte)提出.他在A List Apart 发表了一篇开创性的 ...

  6. Android应用插件式开发解决方法

    转自:http://blog.csdn.net/arui319/article/details/8109650 一.现实需求描述 一般的,一个Android应用在开发到了一定阶段以后,功能模块将会越来 ...

  7. 超棒的响应式设计测试书签和工具(bookmarks)(转)

    一.测试书签(bookmarks) Viewport Resizer 这个书签号称拥有158个国家3万多活跃的用户,主要特性: 完全自定制 方便的添加自定义尺寸 手动的横竖屏切换 自动的横竖屏切换 ( ...

  8. C#实现插件式架构的方法

    插件式架构,一种全新的.开放性的.高扩展性的架构体系.插件式架构设计近年来非常流行,基于插件的设计好处很多,把扩展功能从框架中剥离出来,降低了框架的复杂度,让框架更容易实现.扩展功能与框架以一种很松的 ...

  9. AvalonDock 2.0+Caliburn.Micro+MahApps.Metro实现Metro风格插件式系统(一)

    随着IOS7由之前UI的拟物化设计变为如今的扁平化设计,也许扁平化的时代要来了,当然我们是不是该吐槽一下,苹果什么时候也开始跟风了,自GOOGLE和微软界面扁平化过后,苹果也加入了这一队伍. Aval ...

随机推荐

  1. 反向传播算法(前向传播、反向传播、链式求导、引入delta)

    参考链接: 一文搞懂反向传播算法

  2. Linux下C语言的socket网络编程

    关于详细的服务器建立的步骤以及相关的socket套接字的知识我已经在python socket编程的文章中提到过了,大家可以参看那一篇博客来历接socket套接字编程的内容,由于要是用C相关的API所 ...

  3. Java 之 Web前端(五)

    1.过滤器 a.定义:是一个中间组件,用于拦截源数据和目的数据之间的消息,并过滤二者之间传递的数据 b.步骤: ①建class继承Filter实现抽象方法 public class EncodingF ...

  4. B. Divisiblity of Differences

    B. Divisiblity of Differencestime limit per test1 secondmemory limit per test512 megabytesinputstand ...

  5. Kafka 概念、单机搭建与使用

    目录 Kafka 概念.单机搭建与使用 基本概念介绍 Topic Producer Consumer Kafka单机配置,一个Broker 环境: 配置zookeeper 配置Kafka 使用Kafk ...

  6. Python常用模块--re

    Python内部的re--传闻中的正则模块,是无数初学者心中的噩梦,几乎到了谈正则色变的地步. 1.正则是干什么的 正则表达式,又称规则表达式.(英语:Regular Expression,在代码中常 ...

  7. macOS packages安装时的降级处理

    一.降级安装 在制作macOS的pkg安装包时,一般新发布的程序版本会比旧版本更高.但是有的时候我们也希望在安装的时候,对程序的某些依赖库进行降级安装.比如高版本依赖库中出现了一些Bug,使用旧版本的 ...

  8. BZOJ-6-2460: [BeiJing2011]元素-线性基

    链接 :https://www.lydsy.com/JudgeOnline/problem.php?id=2460 思路 :线性基不唯一,所以排序 进行贪心选择,价值最大的线性基, #include& ...

  9. JS实现缓动动画效果

    原理如下: 假设要从数值A变化到数值B,如果是线性运动,则每次移动距离是一样:如果是缓动,每次移动距离不一样.那如何才能不一样呢?很简单,按比例移动就可以. 例如:每次移动剩余距离的一半. 对吧,超容 ...

  10. 在UnrealEngine中用Custom节点实现径向模糊

    //input NotUse 为了开启SceneTextureLookup函数而连接的节点,但是不参与逻辑 //input UV 屏幕缓存的坐标坐标 //input Strength 力度 //inp ...