[ECUG专题回顾]深入理解Docker的架构与实现-孙宏亮(InfoQ《Docker源码分析》专栏作者)

孙宏亮:大家好,感谢ECUG提供这样的机会,分享一下Docker,在分享之前我就在想容器技术要不要花时间讲一下,现在京东的田兄已经关于这些讲了很多了,那我关于cgroup和namespace方面的内容尽量少涉及,而是涉及Docker在架构和实现方面是怎么做的?我先自我介绍一下,我叫孙宏亮,我来自浙江大学现在还是一名研究生,就读期间参加了初创性团队Daocloud。关于我的经历,我更多接触的是云计算方面的PAAS层,后面的话因为在PaaS的发展中容器渐渐体现出来,但是或多或少总有些缺陷,在13年末Docker非常的火,我们也投入了一些精力在研究Docker方面。对PAAS也有一些研究,也发在了博客。介绍一下Daocloud,是一家Startup的公司,目前是提供基于DockerMirror,在不久的未来肯定会提供更多的服务。



今天我所讲的内容主要是分成三个方面,第一个是Docker的简介,真的很简洁,第二个是Docker的架构,可能会花一半的时间。



这个Docker的介绍,有几个部分,第一个是Open-source,第二个是automate deployment,第三个是additional layer,第四个是operatingsystem-levelvirtualization,第五个是linux。



这个在昨天Docker的1.3.3版本和1.4.0版本发布了,这里提供了更多的功能,包括刚刚田先生讲的都在这里有了体现。



接下来的话,我会介绍一下Docker的架构,Docker的架构本身就是一个CS的架构,首先有一个Docker Clinet,就是用户能摸得到的,后面有一个Docker Daemon建立一些连接,之下可能就是Daemon下面操控一些资源,再往下就是Driver层,就是代理对资源的那些操作的工作。那这个Driver就是涉及内核的东西,文件系统,libcontainer这些系列,右边这个是Docker简单的架构图,还是想从三个方面讲Docker,第一个方面就是Daemon层,就是上面的一块,第二个就是Docker的Driver第三个就是内核,大致就是这样子。



首先说到Docker,具体一点就是一个进程,就是在一台机器上运行的进程,是Daemon进程,是什么样的架构呢?首先肯定是提供一个提供一个server的接口,这个是Docker建立请求分发请求的一块,由谁处理呢?由他的一个Engine引擎做这部分的工作,然后在Docker Daemon当中,所有要完成的工作都以一个JOB的形式完成,比如说下拉一个Image。在底层操作这些,Docker Server就是一个有路由分发模块,最后涉及一个Handler模块做一些处理的工作,下面的话router也非常的清楚,提供一些API接口。下面的话可能是Docker最重要的部分,也就是Driver层,真正逻辑需要做的工作都是由Driver层完成的,Driver层主要包括三个方面,第一个方面Execdriver,就是管理容器当中怎么运行,运行过程当中资源怎么限制和隔离,说到了资源的限制和隔离,很容易想到Cgroups,这个Driver会操纵底层的Cgroups。第二个是Networkdriver,Docker必须创造一个网络环境,就是由Networkdriver做,当然也提供一些其他的一些模式,后面我会讲到,还有一个方面的话是Docker的Graphdriver就是用来下拉Docker Image成为一个文件系统,供这个容器使用的,然后这里面的话默认AUFS。



首先讲这个Execdriver,在Docker是0.9版本的时候是直接由这个Driver操控LXC,他是怎么操控的呢?就是调用这些LXC的工具。这个于情于理都有一点说不过去,后续的版本Docker自己开发了一个工具,开发了一套叫NativeDriver,它往下操控,直接操控libconetainer,主要还是用来创建容器的。大致是这样子,然后操控libcontainre一些辅助性的工具完成一些功能。第二个就是networkdrivr功能,因为容器技术的话也不是刚刚出来嘛,然后关于镜像方面大家已经感受到了好处,但是在网络方面可能作为容器一种新的技术还不能和原生态的那些虚拟机的概念在实现上有一一映射,所以会带来一些网络上面的麻烦,networkDriver可以建立一个桥,在建立的情况下也可以帮助建立一个内部的网络接口,赋予一个怎么样的IP,然后又可以做什么样端口映射,其实在Docker Daemon架构当中,共提供了两种网络模式,一种就是建立一个桥,然后你自定义定制这个网络环境,所以一般桥的模式还是会使用的,还有一种是对Docker Daemon的网络不做任何处理。刚刚说的是Docker Daemon的网络模式,第二个是Docker Container的网络模式。Docker Dameon在外部管理所有的环境,在每个DockerContainer内又是怎么管理它自己的网络呢?Docker可以提供4种模式,第一种是bridge桥接模式,建立一个网卡对,有一张网卡是给自己的Container使用的,第二个是添加到网桥上的,内部的网卡接受的流量都要进到第二个网卡传到网桥上,由于建立网桥的时候打开所有的IPv4_Forwarding,这样就能保证容器内的流量就能流到宿主机的所有网卡上,之后就没有限制了。第二个模式就是Host模式,就是说不管你这个桥有没有建立,我都不管,我都默认使用宿主机上面的网卡进行通信,很简单这个容器拥有了一个和Docker所在的服务器的那个IP地址不需要隔出一个子网这样很多的便利,也有一个什么弊端呢?比如说10个Container,内部的应用都是使用了某一个端口,这样在Host上面有冲突,因为是公用一张网卡,是没有一个网络地址转换的功能,而使用桥接模式这一点很容易做到。还有一个就是DockerContainer模式,可以借用其他Container的网络模式,这个怎么说呢?假设有一个DockerContainer,这个Container里面已经建立了网卡对,然后网卡对跟网桥建立了联系,这个时候新起一个容器的时候可以指定使用刚刚容器的网卡对,这样可以保证多个Container是共用一套网络环境,都是在一起的。这个方面需要指出的一点就是在谷歌的那个Kubernetes为什么这么火,把多个DockerContainer的Namespace挂在一个当中,所以这个里面所有的DockerContainer都可以通过Localhost进行访问,这样可以缓解每个DockerContainer都占有一个IP的情况。第四个模式就是什么都不做,这个很简单,就是网卡什么都不做,大致的网络方面就是讲这么多东西。因为我的话也做一些Docker的源码分析,现在发到第5篇,后面会深入地跟大家交流一下这个DockerDaemon的网络模式和DockerContainer的网络模式,接下来就是关于Docker的镜像方面,可以说是Docker诞生以来的一大亮点。假如说Docker没有这样的镜像机制和LXC只不过是实现更好的区别,本质上没有什么大的区别,但是因为这个GraphDriver的形式使得其大行其道,关于CI和CB的真的很多,这个是借助了底层文件系统的性质,实现了多个层级,之前是支持aufs,btrfs,还有更底层的devmapper、下面是Vfs为什么写这个呢?因为Docker当中会有一个概念,就是DockerContainer可以使用你诉主机上的某一个目录volume,就是通过VFS挂载到了容器的内部,可以供容器内部使用。就在昨天1.4.0发布的时候,Overlay已经加入了Docker的一种,这个是巨大的改变。Graph运行的当中需要一堆的文件,可能以一个文件系统的形式在那个地方,传统的模式下使用这个稳健的话就是往里面添一个或者是删除一个,其实对做这些工作都没有记录的,但是这个Graph可以对这些东西进行很好的操作,保证你使用的环境是有一个关系的,很好的关系,把它给存起来,它是怎么存的呢?首先会把你的一个文件系统分层对待,每一层的话里面都是有一些内容,真正使用的过程当中把这些文件系统一个一个的联合起来,但是对上层的用户是透明的。然后AUFS会有一个比较大的缺点,就是说在写内容的时候使用的COW的机制,我要写的是这个层里面的东西,这个东西怎么做呢?就是说会把你要写的那个东西从Readonly层拿到那个writable层复制一份,再在上面一层把东西改掉,这个性能会有问题,假设我写的一个文件是有2个G,放到最上层需要多长时间,如果次数是非常多的你如何保证这个性能?所以会有这些方面的问题吧,然后这个Graph就是保证了你的一个容器的运行的文件系统,那这些Graph这些engine从哪来的呢?这个就涉及了Docker PULL的流程,把这个东西从某个地方拉下来,这个流程我已经写的非常清楚了。首先接触到这个请求就是JOB的形式执行这项任务,PULL的JOB把这个Graph拉下来,怎么存呢?就是由GraphDriver存在了本地,存在了Graph当中,这样一个PULL的工作完成了,把东西存下来了但是有没有用起来?还没有,必须在Docker创建一个Container的时候再把这个Graph里面的东西一个一个通过Graph把所有的东西创建起来,形成了一个可以用的一个文件系统,那这个东西到底从哪里来呢?Docker的话就是提供一个Docker Registry的功能,管理这些镜像的存储,可以从这个Registry里面下载这些镜像,同时把这个镜像上传到这个Registry里面去,关于Docker的话,这个官方提供了DockerHUB,维持最大的镜像库,里面有成千上万的镜像,然后它的域名是Index.Docker.IO,这个非常的丰富,全世界的Docker爱好者都把这个镜像把这个DockerHUB里面塞,供全世界的人使用,同时可以建立自己的帐号,但是这个东西需要自己付费,但是作为我们中国的程序员这个里面会有一些顾虑,因为DockerHUB在国外,这个网络环境是太让人头疼了,因为首先我记得在几个月之前DockerHUB上面的东西下不来的,但是现在可以下来,但是速度是非常头疼的,这个是您能想多慢就是有多慢,就是这样的环境。然后Docker官方的话提供了一个机制叫Docker HUB Mirror,为大家提供加速下载这个服务。提供的功能就是DockerHUB MIRROR在中间有一个管理的模块就是一个CDN,但是在使用的过程当中对于用户是透明的,用户不需要改任何的东西,只需要在你的启动DockerDaemon的过程当中传递一些参数指定使用哪个DockerMIrror。目前提供这个DockerHUBMirror服务给大家,最主要为国内的开发者提供一些便利,这边是整个DockerHUBmirror的架构图。这个就是自己的Docker,这个是DockerHUBmirror,这边是DockerHUB。假如说你要下载一个镜像,首先会看DockerHUBmirror当中有没有镜像,如果说有的话,如果说在七牛上已经有这个东西了,国内的网络CDN就不用你担心了,如果说不存在的话我们这个DockerHUBmirror在官方上面拉下来存在七牛上,拉完了之后然后再通过CDN的功能把这个传递给你用,整个过程对用户来说是感知不到的,但是他唯一感知到的是速度的快慢,东西已经在DockerHUBMIRROR上面了感觉特别快,如果暂时不到就是感觉慢了一点,所以对用户讲他的感觉就是这样子的,但是如果说很多的用户来用我们这个DockerHUBmirror,成千上万的镜像我们同步了90%了你会感觉到速度稍微慢一些这种可能性是几乎不存在的。



对于用户的话怎么选择,这边的话其实Docker HUB mirror不是一个registry,所以说不是做一个这个功能,而是做了一个Docker HUB在国内的代理工作,做了这部分的工作,然后但是对于一些企业的话,是说到企业了,肯定需要一个Private registry,因为整个企业的信息都放到这个里面,他不放心放到国外的DockerHUB上面去,然后Private registry,简单说一下这个跟mirror的区别。一个是镜像多样性方面,作为一个Private registry,首先都拉进去,但是不可能把所有的拉全,在丰富性方面缺少一些,而且他需要做一个工作,就是他手动做这个工作,这都是有用户选择的,用户要什么必须手动拉什么,但是对于mirror机制没有这个限制,就是说你想要什么我这边有就给你,我这边没有就去DockerHUB上面拉一份在自己的云存储里面存一下再给你,这样的话丰富性是非常强的,不需要人为的干预都是用户做这个决定的,跟DockerHUB是非常一致的。用户使用方面有一个问题:就是说镜像在DockerHUB上面update都是频繁的,你把它下下来就是在你们这边了,官方的东西更新过了,而自己下下来的镜像却没有update,这样会影响你的使用,但是用mirror就不存在这个情况。Private registry和Mirror有一个使用方式的区别,Private registry需要在URL中添加一些信息,比如说要Docker PULL XXX:XXX, 这种使用可能会嵌入到Dockerfile使用,你一个人使用Privateregistry其实影响不是很大,但是假如说你维护的Docker这个集群是很大的,上千台的这个东西你人为的改总会增加你很大的工作量。但是在mirror当中就不存在这个东西,mirror当中你只要在自己的Docker server启动的时候配置参数,在使用Docker mirror没有任何的影响。第三点可能是安全性方面的一些稳定性方面的考虑,就是说这个Registry会出现一些故障的时候,这两者有甚么区别呢?首先Private registry要升级,所有的功能不能用,这样子的话会手动回归到人为的去改那些配置,因为刚刚Private registry使用就是XXX.XXX,把这些人为的删除掉,才能去DockerHUB上面去。但是Docker Hub mirror刚刚那套机制决定了,假如说这个mirror出现故障了或者是升级了这个连接建立不起来了,镜像下载不起来了,完全可以自动去Docker HUB上面下载,这个是没有任何影响的。然后再DockerHUB镜像更新方面的话,Private registry区别就是老版本镜像,假如说这边是老版本的镜像失效用户的使用受阻了,这个站主或者是管理员必须手动去DockerHUB上面下载下来告诉大家我这个已经更新了,可以用更新版本了,这个过程当中管理员必须了解版本之间的差异性这样才能对你这个用户有一个更多的说明,才能辅佐你这个用户对镜像的使用。但是在mirror使用当中不存在这个现象,假如说存在老版本的镜像,失效了那mirror会做一个机制,发现一匹配不对,完全可以去DockerHUB上面下载,下载的过程当中DockerHUBmirror对这个东西做一个存储,他把老的版本覆盖掉,只要第一个人做这个事情,第二个人第三个人就可以完全享用这个最新版本。



这个就是说DAOCLOUD,大家可以体验一下Docker mirror的机制,是我们团队做的第一件事情。这个就是进去一个界面刚刚注册有一个链接,这个就是为了让你的DockerDaemon设置这个DockerHUBmirror地址的,在这个当中还会做了限制,毕竟我们是公有的,不能随便让你下东西,否则你开一百台机器下我们东西不行,大家可以提出建议,都可以提出,比如说流量方面目前是支持10个G的流量,然后在请求数的话,请求次数有相应的需求,最后一个独立IP是不同的连接会有一些区别,然后这个的话是我自己的一个帐号,简单的做了4个镜像,大致节约了时间,按照一个平均的访问DockerHUB的时间,节约了3-4个小时的时间,这边下载多一点的以后,时间成本的话可以节约,另外我们也提供了一些宣传的措施,比如说可以邀请一些好友,我们会在数量上面做一些优惠或者是奖赏。



刚刚是一些产品上面的,接下来就是一个使用方面的,这个DockerHUBmirror怎么使用呢?建立一个URL,把这个写到了你这个DockerDaemon所在机器的关于Docker的配置文件当中,写到了这个ETC文件当中,写过了就不用管了,其实你下次每次拉镜像的时候都会去这个DockerHUBmirror当中下拉这个镜像,当然这个版本控制好,必须是1.3.2版本以上,如果是老版本这个方面是享受不了这个福利的。



在Image方面,这些只是简单的例子,刚刚原理说明了不止这些东西,DockerHUB上面的东西都可以享受到,就算享受的不是最快,你下载下来后面的程序员都可以享受到,这个是比较好的地方,目前是redis 、ubuntu。所以欢迎大家体验一下这个DockerHUBmirror,由DAOCLOUD这个团队提供的。



另外提供一些分享资料,分析了Docker的源码关于Docker架构、Cleint、Daemon,尽量写到比较详细展现给大家,还有Docker Server,接下来是DockerDaemon的网络,不久的将来肯定和大家见面,我大致就是讲这么多,谢谢大家!



PPT:http://qiniuppt.qiniudn.com/sunhongliang.pptx


视频(田琪&孙宏亮):http://qiniu-opensource.qiniudn.com/ecug-2014-tianqi.mp4