6.1.4 云原生应用场景下的镜像分发加速方案
概述
容器镜像是容器技术的基础设施之一,是容器运行时文件系统视图基础。而在云原生应用场景中,应用的构建、分发、部署和运行 都需要与容器镜像的生命周期紧密地绑定在一起,可以说,没有容器镜像,就没有当代的云原生应用架构。
虽然容器镜像在云原生领域中至关重要,但自它诞生以来,镜像设计本身并没有多少改进,在在云原生技术被广泛、稳定地应用之 后,传统容器镜像也逐渐表现出了一些缺陷:
启动速度慢,在镜像体积较大的情况下,容器的启动会很长;
较高的本地存储成本和镜像仓库存储成本;
集中式的镜像存储仓库,对镜像仓库的资源有了很高的要求。
而以上的问题在生产集群规模庞大的时候,会带来以下问题:
集群弹性效果差:在一些突发流量的场景下,用户往往需要在短时间内拉起大量的容器用来应对流量高峰,典型的业务场景如电商 双11大促、社交平台热点等等。而由于容器启动时间长,往往应对高峰的时候准备时间需要很长,进而影响集群业务表现;
集群运行性能差:在大数据、AI场景下,用户往往需要同时运行多个容器,不同容器计算结果聚合形成最终的计算结果。而由于容 器启动时间长,在单一时间内完成的任务数量就会有限,影响大数据、AI集群的吞吐能力,集群运行性能不加;
成本高抬:上述的弹性效果差、运行性能差,往往需要通过扩大集群规模以满足业务的诉求,简介抬升了集群成本,另外,由于存 储成本、仓库成本的太高也会影响整体的运行时成本。
龙蜥社区(OpenAnolis)的云原生特殊兴趣小组对容器镜像技术进行了深层次的研究、探索和开发,与上游社区深度配合,一同推 出了镜像分发加速解决方案 --- Nydus + Dragonfly,通过先进的技术,解决云原生应用场景下传统容器镜像带来的启动慢、资源浪 费等问题,基于龙蜥社区的镜像分发加速方案,用户可以在应用服务弹性场景、大数据AI场景等主流的云原生应用场景中获得集群 弹性性能、运行时性能的极大提升,在获得集群表现竞争力的同时,降低集群的运行成本。
场景挑战
容器在使用之前,需要容器运行所在的物理机上执行下面3个步骤
- download镜像,
- unpack镜像,
- 使用overlayfs将容器可写层和镜像中的只读层聚合起来提供容器运行环境。
其中,download镜像时需要download整个镜像,再加上download镜像本身受限于网络带宽的影响,当容器镜像size在到几个G 时,下载时间会较长,而unpack大体积的包往往也要耗费不少的时间。而由于容器镜像的版本机制和分层机制,生产中镜像的规模 会越来越大,比如某电商平台内部的容器镜像体积都在1G以上,因此集群内的弹性能力受到了很大的限制,在应对大促时,往往需 要提前部署好大量的容器,这些容器所耗费的资源都带来较大的成本抬升。
因此,如果不解决容器镜像的使用方式、镜像格式,很难在根本上解决弹性性能问题。
另外,在download容器镜像的服务器成为容器镜像仓库,一般是集中式的仓库,既集群里所有的机器都会从这个(这批)服务器 上拉取镜像。在集群规模很大(万级node),弹性并发情况下,数万节点同时从仓库拉取镜像,往往会造成镜像仓库DOS,进而引 发故障,所以在大规模集群内提升镜像分发效率也有很大的挑战。
方案特色
2016年的usenix的论文Slacker: Fast Distribution with Lazy Docker Containers中曾发表数据,在容器启动的过程中,平均只需 要读取镜像数据中的7%不到的数据,因此在实际应用过程中,通常不需要全量拉取数据我们就可以完成业务的发布过程。
基于这个研究,Nydus镜像格式提出了按需加载的方案,既仅下载运行过程中需要的文件,对于冗余的镜像内容不予下载,进而减 少容器运行过程中的数据下载量,减少镜像下载时间,提升容器启动性能,进而提升集群弹性性能。
为了达到这个效果,Nydus提出了一种新的镜像格式,做了如下创新:
镜像元数据/数据分离,用户态按需加载与解压;
更细粒度的块级别数据切割与去重;
扁平化元数据层(移除中间层),直接呈现Filesystem视图;
端到端的文件系统元数据树与数据校验。
基于以上的创新,用户在启动容器时,仅需要下载少量的metadata数据和manifest数据就可以实现容器的启动,然后在真正读取 文件的时候从远端拉取数据,大幅度减少了启动过程中的数据拉取量,提升了启动性能。
同时,引入了Dragonfly作为集群间镜像P2P分发方案,将镜像数据由南北向传输转变为东西向传输,避开了容器镜像仓库瓶颈,大 幅度提升集群镜像分发的效率,也提升了镜像分发的稳定性。
实践验证
使用Nydus + Dragonfly在快速弹性场景下的模拟效果对比。 在电商应用或者社交软件中,经常会遇到突发流量的场景,如果用户将业务托管在云厂商上,通常的做法是在云厂商上快速地创建 新的VM节点,并在节点里面部署容器,这时,往往节点内并没有缓存镜像,镜像拉取会成为扩容速度的关键影响因素之一。本实践 通过在一个具备50个全新node的k8s集群里面,并发启动50个nginx容器,用于模拟突发流量场景下的集群极速扩容场景。
这里我们用deployment的方式部署50个nginx容器,nginx镜像的大小约为70M。我们创建deployment之后,不断探测ready的 POD数量直到在线容器数量为50,用来检测启动完成的容器的数量。本实践中,我们将普通容器镜像和Nydus容器镜像的每秒启动 完成PDO时间做一个对比。
可以看到,使用nydus镜像达到50 ready的时间比使用normal镜像少1s,同时,在deployment创建4s之后,nydus的ready数量 就已经达到了40个,而使用normal镜像的场景,还没有容器ready,到了接近11s才达到了40个ready的pod,以80%的扩容水位 作为安全水位的话,使用nydus相比使用normal镜像可以减少一半以上,集群的弹性性能可以提升100%。
使用Nydus + Dragonfly在大数据/AI Job场景下的模拟效果对比。
在AI或者大数据计算中,一次计算往往需要切分成多个任务独立运算,在计算完成之后再进行数据聚合,因此需要每一个单独的计 算结果都完成了,才能聚合出最终的结果。在云原生应用中,既每一个独立的任务用一个POD来承载,只有每一个POD都完成了才 算最终完成,K8S将这种计算模型用Job类型来抽象。而AI或者大数据计算通常会有新的计算模型出现,每一个计算模型的出现会在 镜像中集成新的数据,因此在计算启动的时候,往往需要进行镜像的下载,因此镜像也成为了Job类型运算的关键影响因素之一。
本实践我们模拟一个经常会出现的大镜像的场景,镜像中存放了用来做AI计算或者大数据计算的数据,用来展示使用了Nydus以及 Dragonfly之后,镜像加速带来的集群镜像性能的提升效果。
首先,我们构建一个容器镜像:
FROMregistry.anolis.com/library/anolisos:8.6RUNbase64/dev/urandom|head-c1073741824>/root/origin.txtRUNdnfinstall-ydiffutilsCOPYtest.sh/rootCMD ["/root/test.sh"]
在这个镜像中,以anolisos:8.6为基础镜像,在镜像中生成了一个1G大小的随机文件origin.txt,同时安装了diff工具。然后在容器 启动的时候,运行/root下面的test.sh脚本,脚本内容如下:
#!/bin/bashbase64/dev/urandom|head-c1073741824>/root/new.txtdiff/root/origin.txt/root/new.txt>diff.txtexit0
脚本的内容也很简单,再生成一个1G的随机文件new.txt,然后比较origin.txt和new.txt,将两个文件对比,对比的结果再放置到 diff.txt中。
这个testcase包含了几个部分:
在镜像中存储了大量的,需要用于计算的数据 ---> 镜像加载和镜像数据读取
在容器运行过程中生成新的数据,并与镜像数据进行比较 ---> 计算
再把数据存回到容器汇总 ---> 存储
本次实践,以1个并发、5个并发、50个并发为例,对比不同并发能力下,普通镜像跟nydus+dragonfly的性能差异,通过普通镜像 和Nydus + Dragonfly镜像的Job运行时长对比得出不同方案的性能差异。
普通镜像的Job类型的计算时间如下:
随机挑选3个不同job的pod,可以印证我们的猜测,50并发下,原本20s可以拉下来的镜像花了接100s:
接下来我们测试nydus + dragonfly的情况,方案升级之后Job的计算时间如下:
在这个use case中,需要访问到大量的镜像内的文件,在1并发、5并发场景下,虽然没有打到Harbor的瓶颈,但是由于使用nydus 减少了镜像数据的传输,所以整体的job运行时间也有了较大的提升,提升比例基本与镜像中base image的大小相当。
而在50并发场景下,并发pull已经打爆了Harbor带宽,这时候Nydus的按需加载 + dragonfly的P2P就很好的体现了效果,整体的 Job运行时长缩减了一半以上,意味着集群的吞吐能力提升了接近100%。
总结
基于龙蜥提供的Nydus + Dragonfly解决方案,用户在云原生场景中可以大幅度提升镜像的分发效率,不仅可以帮助用户提升自己的 集群业务表现,还能够降低集群的运行时成本。此方案已经在云厂商、互联网企业中有了广泛、稳定地应用,持续地为龙蜥用户贡 献业务价值。