在部署较大的容器应用集群时,把应用镜像发布到所有节点常常需要大量时间。我们VMware的研发团队测试了P2P的方法,能够较好地解决大规模镜像分发的问题,为运维实践提供了很好的指引。
在使用Docker运行容器化应用时,宿主机通常先要从Registry服务(如Docker Hub)下载相应的镜像(image)。这种镜像机制在开发环境中使用还是很有效的,团队成员之间可以很方便地共享同样的镜像。在实际的生产环境中,从效率和安全角度,往往会部署私有的Registry服务,专供产线机器集群使用。当大量主机需要同时从Registry下载镜像运行容器应用时(比如发布新版本,打补钉等情形),Registry 服务往往会成为镜像分发的瓶颈,应用镜像需要较长时间才能传送到所有主机上,使得应用发布的周期大大延长。不少用户采取了预先下载镜像的方法,让各节点事先分批获取镜像,然后在预定时刻统一启动应用。这种办法在一定程度上缓解了问题,但实质上并没有解决Registry分发的瓶颈。
为了解决这个问题,我们着手设计实现了一种利用Bit Torrent协议来加速镜像分发的系统(Decentralized Image Distribution,DID),其主要思路是允许在不同的host之间共享镜像,形成分布式的P2P下载网络,提高网络吞吐量。通过测试,我们验证了该系统在集群节点数目较多、镜像较大的情况下具有较大的性能优势。
管理界面Admin Console
管理员可以通过Admin Console定义下发镜像的任务(如集群的大小、机器的IP地址、镜像URL等),并且可实时了解任务的完成情况。
控制器
控制器是DID系统的核心组件,控制镜像分发的整个过程。当接收到来自Admin Console的镜像分发任务之后,控制器完成镜像的准备工作,并将具体的镜像下载任务下发给各个节点的客户端代理。
客户端代理
客户端代理部署在集群的每个节点中,配合控制器的调度,完成整个镜像的分发过程。代理接收来自控制器的镜像下载任务,调用BT客户端下载镜像,并最终将镜像导入到Docker daemon中。
BT客户端
部署在集群节点的BT客户端和部署在控制器中的BT客户端以及Tracker共同组成了一个完整的P2P文件传输系统。在整个镜像的分发过程中,它们利用BT协议完成镜像下载。
BT Tracker
Tracker是BT系统的一部分,它存储了BT客户端下载过程中所需要的元数据信息,并协助各个BT客户端完成整个通信过程。
镜像分发原理
当用户通过Admin Console向DID系统提交一个镜像分发任务(Job)之后,控制器会进行以下处理:
❶ 通过本地的Docker Daemon REST API从Registry下载镜像到本地镜像仓库;
❷ 调用Docker Daemon API从镜像仓库导出(export)镜像文件(镜像tar文件);
❸ 从镜像tar文件中抽取出组成镜像的所有layer并进行压缩;
❹ 为每一个压缩后的layer制作BT种子文件;
❺ 启动BT客户端载入所有压缩后的layer和相应的种子文件,此时控制器所在节点的BT客户端将成为一个Seeder;
❻ 向各个客户端代理发送该镜像的下载任务。任务说明中包含了组成该镜像的所有layer的ID和其对应的种子文件的URL。
在客户端代理接收到来自控制器的镜像下载任务后会进行以下处理:
❶ 针对该镜像的每一个layer,通过调用检查其在本地是否已存在,把不存在的layer ID加入到待下载列表中;
❷ 对下载列表中的layer,下载对应的种子文件,并启动BT客户端完成layer文件的下载;
❸ 通过Docker daemon的API将下载完成的layer文件导入到Docker daemon中;
❹ 重复步骤2和步骤3直到所有待下载layer全部被导入到Docker daemon中。注:每个layer的下载过程是并发执行的。
由于Docker的镜像是按照layer存储的,不同的镜像可共享layer,这种机制不仅减少了对存储的消耗,而且下载镜像时只需要下载缺少的layer即可,从而也就减少了镜像的下载时间。在上述DID的镜像分发过程中,我们将镜像拆分为多个layer利用BT协议进行传输。另外,由于BT协议原本是为因特网上的文件分发而设计的,有部分设计并不适用于局域网镜像集中分发的场景。比如,在BT下载过程中,每个客户端都会维护一个参与下载该文件的peer列表,默认情况下每隔1800秒BT客户端才会与Tracker进行一次通信来更新。在DID的设计中将这一时间修改为了1秒,以便各个节点的BT客户端可更快地获取其他peer的信息。
由于测试环境的限制,我们搭建了一个由10台物理机组成,每台物理机上又部署了10台虚拟机的实验环境来模拟拥有100个节点的集群场景。10台物理机连接在1Gbps的以太网交换机上。每台虚拟机都配置了2个vCPU、4GB内存和一块500GB的硬盘。
第一组实验用来比较在节点数目一定(如100个节点)的集群中,不同镜像大小对下载过程的影响。实验结果如图2所示,图中纵轴的Propagation Time是从整个集群开始下载镜像到所有节点将镜像导入所用的时间:
从测试结果可以看出两点规律。一是Docker和DID的下载时间都随着镜像的增大而增大,但是DID的增长斜率较小。而且随着镜像的增大,DID的优势表现得越来越明显,例如,在镜像超过500MB后,DID所需的时间不到Docker的一半。二是存在这样的一个平衡点,当镜像小于该值时,Docker的下载速度会更快,这是由于Docker的直接下载方式在此时并未造成太多的网络拥堵。另外在用BT协议下载的过程中,BT客户端同Tracker之间以及客户端之间需要不断的进行通信,本身控制流量也有一定程度的消耗。上图中,当镜像增大到130MB以上时,BT协议带来的额外开销可以通过网络的节省来弥补,因而总体性能提升。
既然两种方式各有利弊,我们做了第二组实验来研究Docker和DID之间性能的平衡点。当Docker和DID在同一个集群中分发相同镜像所消耗的时间相等时,此时的集群节点数和镜像大小即称为一个平衡点。实验结果如图3所示:
从实验结果中可以看出,随着集群节点数目的不断增大,达到平衡点所需要的镜像大小是在不断降低的。也就是说DID在集群节点数目较大、镜像较大的场景中性能优势最为明显。
我们所设计和实现的DID镜像发布机制,利用了BT协议完成镜像的分发过程,相对于Docker自身的下载方式而言,在大负荷的场景下具有较为明显的优势。原生的BT协议并不完全适应于镜像分发的使用场景,DID已经对其进行了部分优化。但是,我们相信可以调整的地方还有很多,优化之后性能应该还会有一定程度的提高。
关于镜像P2P的传输是个热门话题,目前这方面的测试数据很少。我们的测试结果可作为今后研发类似系统的基础。例如,在我们团队已经开源的企业级Registry项目Harbor中,不仅提供了完整的用户权限控制(RBAC)和友好的用户管理界面,还计划使用P2P的传输方式来增强镜像发布效率,为生产环境中快速发布容器应用提供有力的支持。欢迎大家关注和使用Harbor开源Registry项目:
https://github.com/vmware/harbor
本文作者Henry Zhang/尹文开