随着Docker技术的日渐火热,一些容器相关的问题也浮出水面。本文就容器数量激增后造成的分发效率低下问题进行了探讨,并提出了一种新的解决方法。发现问题,解决问题,正是IT技术不断进步的真谛,小数深以为然。
容器技术令应用程序的配置与部署工作效率极大提高,但在不同IT运维领域的具体提升效果却又不尽相同。举例来说,其在利用数据中心存储与网络容量进行容器镜像的大规模分发时往往表现得比较不尽如人意。
这确实有点反直觉:容器技术一直以高效的服务运行能力与相较于虚拟机的可观资源节约水平为人们所称道,为什么也会存在效率低下问题?答案出在Docker镜像的存储与下载方式上。从传统角度讲,存储在库中的每套镜像都必须拥有与其各层相关的全部文件,这意味着对任意主机设备上的Docker镜像进行更新,我们就需要从该库中下载完整的镜像。
在面对小型Web应用中的少量容器时,这种机制似乎构不成什么大问题。然而我们可以设想一旦容器系统数量增长至数百、数千乃至数百万之巨时,那么库的体积就会随着容器系统的增加而不断攀升,而每一次更新所需要下载的数据量也会越来越大。与此同时,获取Docker镜像的下载流量也将提升至数GB,这显然是我们所无法接受的。
像Twitter这样的企业无疑运行有大量容器系统,自然也经历过上述困扰。这种状况会随着更多企业采用容器技术而变得愈发普遍,并导致大型容器库的规模不断向外扩展。
我们认为解决容器臃肿难题的可行方案之一正是名为CernVM文件系统(简称CernVM-FS)的技术成果。其由CERN(欧洲核子研究中心)开发而成,同时囊括了来自世界各地的高能物理学领域、特别是费米实验室的辛勤贡献。
CernVM-FS的作用在于帮助科学家分发每年开发出的高达2TB的软件数据——其中一部分软件甚至每周都需要面向数十万台计算机进行多次分发与上传。CernVM-FS采用一整套独特的索引、重复数据删除、缓存与地理分布机制,旨在最大程度降低与每次下载相关的组件数量,同时尽可能加快必要的下载数据量。
Mesosphere与CERN目前正在探索如何将CernVM-FS同Apache Mesos 加以结合,从而了解其在容器下载工作中的实际效果以及我们在大型容器环境下的处理效率。
CernVM-FS的基本构想诞生于2008年,当时CERN的研究人员与现在的人们一样因为底层硬件虚拟化的需求而开始审视容器技术,即寻求新的应用程序部署机制。相较于创建镜像或者软件包,他们想到使用一套全局分布式文件系统帮助科学家们将自己的软件一次性安装在一台Web服务器上,而后立足世界任意位置对其进行访问。
作为一套分布式文件系统,其首要原则就是在任意时间段之内,全部可用文件都只有一小部分接受实际访问。举例来说,要运行一台Web服务器,大家只需要使用操作系统(例如glibc与OpenSSL)中的一部分库。负责承载操作系统的分布式文件系统只需要使用必要的文件,而且事实上CVMFS只需要下载并在本地缓存这部分必要数据。当时研究人员们选择了HTTP作为下载协议,从而接入其它可用Web缓存基础设施(例如Akamai、CloudFront、Squid以及NGINX代理服务器等等)。
而第二项原则在于元数据(即与文件存在相关的信息,而非文件内容)被优先对待。一般来讲,文件系统所承载的软件会受到“libssl.so是否存在于lib32当中?抑或是存在于/lib64目录当中?还是存在于/usr/lib32当中?”这类请求所困扰。在处理这些请求时,通用型分布式文件系统往往表现得很差。CernVM-FS则将全部元数据保存在SQlite文件当中,并将其作为常规文件进行下载与缓存。在这种情况下,数百万计的元数据请求就能够以本地方式进行解析,使得CernVM-FS在速度表现上几乎与本地POSIX文件系统保持一致。
第三项原则在于,软件只能够在发布点处进行修改——其他一切客户都只能在高可用性水平下实现只读操作。多数此前存在的文件系统在设计中都无法处理CAP定理提到的权衡难题,因为这类系统会假定客户的目标始终是读取最新的可用数据版本。在这种情况下,软件必须在应用或者容器运行的同时保持即时性,这意味着运行过程中该文件系统必须交付单一快照。
有鉴于此,CERN决定使用内容可寻址存储与梅克尔树状结构。与git类似,各文件会在内部根据其(惟一的)加密内容散列进行命名。存在于不同目录内的同一文件的多套副本(例如不同Ubuntu镜像当中的“ls”实体)会被合并为单一文件。SQlite文件目录取决于其中文件的内容散列值。如此一来,其root散列值(一个160位整数)将最终确定整套文件系统快照。要关闭信任链,系统会提供一个经过加密的root散列值,每个客户端则对接收到的数据的每一位进行验证,从而确保其来自预期来源且未被篡改。
如今,CernVM-FS负责为分布在全球各地的约十万台计算机交付来自大型强子对撞机实验软件的数百万个文件与目录。
Mesos通过所谓“容器化器(containerizer)”利用多种实现手段提供任务容器化能力。容器化器负责对运行当中的各个任务加以隔离,同时限定各个任务可以使用的资源量(例如CPU、内存、磁盘以及网络)。Mesos容器化器的作用可以通过所谓“隔离器(isolator)”轻松得到扩展,后者可被视为一种执行特定任务的插件(举例来说,在容器当中启动外部分卷)。
最后但同样重要的是,容器化器还能够为任务提供一套运行时环境。它允许用户将全部关联性同任务本体一道预打包至文件系统镜像当中。这套镜像随后可进行任意分发,并被用于启动该项任务。
Mesos目前已经拥有多种类型的容器化器。其默认选项为Mesos容器化器——这款工具采用Linux命名空间与cgropus以实现任务隔离同资源使用量控制,同时配合一整套隔离器实现外部功能交付。另外还有一套Docker容器化器,其利用Docker工具以提取镜像并启动Docker容器。
Mesos容器化器目前已通过扩展实现了多种镜像格式的支持能力,其中包括Docker与AppC,旨在建立一套“统一容器化器”。这套方案令新镜像格式的支持变得非常轻松:利用统一容器化器,我们只需要为新的镜像格式构建一款所谓“配置器(provisioner)”,并复用全部现有隔离代码即可。举例来说,Mesos用户可以继续使用Docker镜像,但全部提取、隔离与其它任务都通过Mesos而非Docker工具实现。
内容可寻址存储的介入、出色的安全水平以及久经考验的扩展能力使得CernVM-FS成为一款极具吸引力的容器镜像分发处理工具。为了测试其实际效果,我们创建了一套新的CernVM-FS库,并将其添加到一套Ubuntu安装软件包当中。在此之后,我们立足于CVMFS构建了一款Mesos容器镜像配置器。相较于直接下载完整镜像,它能够利用CernVM-FS客户端以本地方式远程启动镜像root目录。该配置器可将CernVM-FS库名称作为输入数据(其在内部被映射至该CernVM-FS服务器的URL)以及充当容器镜像root所必需的库内路径。
如此一来,我们就能够立足于同一CernVM-FS库实现多种容器镜像的发布。从本质上讲,CernVM-FS库的作用等同于一套安全且可扩展的容器镜像注册表。
从容器化器的角度来看,一切其实丝毫未受影响。它仍然负责处理包含有镜像目录树的本地目录,并利用一切必要元素实现容器启动。不过这种作法的最大优势在于CernVM-FS拥有经过细化调整的重复数据删除功能(在配合Docker的情况下,基于文件或者块而非层),这意味着我们现在能够在无需下载完整镜像的前提下启动容器。一旦容器启动完成,CernVM-FS会下载必要的文件以进行任务处理。
在我们的测试当中,我们启动了一套Ubuntu容器,而后在shell当中运行了一条命令。在传统场景当中,我们需要下载完整的Ubuntu镜像,其体积约为300 MB。然而,当我们使用CernVM-FS配置器时,我们只需要下载该任务所必需的文件——具体为不足6MB。
由于CernVM-FS采用内容可寻址存储,因此我们不需要重复下载同样的文件。所以,如果我们进一步启动其它容器(例如一套CentOS镜像)并运行不同的命令,那么我们只需要下载新命令所需要的文件,并复用此前已经在Ubuntu镜像中下载过的全部通用关联性(例如Bash或者libc.so)。在这种模式下,容器层的概念已经不复存在,重复数据删除将立足于更为细化的层面进行。
我们还计划添加对该配置器的支持以利用对应的检验机制启动任意CernVM-FS目录。这将帮助开发人员更为轻松地在处理容器镜像时实现快速迭代,并使得运维人员便捷地在不同容器镜像版本间来回切换。
CERN与Mesosphere的技术团队对于将CernVM-FS与Apache Mesos集成充满期待,这也意味着IT行业将更为广泛地采纳应用程序容器技术。如果各企业与机构希望从根本层面改善应用程序构建、代码部署以及数据中心运行的实现方式,则必须要立足于高于当前实际规模的水平上进行容器的启动、关闭、更新以及管理。CernVM-FS与Mesos之间的紧密集成将能够成为一种可行途径,帮助各类用户解决容器采纳道路上的存储容量及网络带宽瓶颈。
作为一项新兴的技术,关于Docker的坑很多,需要我们共同来填满。大家如果有相关的疑问和困惑,也欢迎留言和小数进行讨论。
原文链接: