作者:高性能存储 SIG
容器化是最近几年 devops 界风行的趋势,通过业务的容器化咱们将创立一个齐全打包、自蕴含的计算环境,让软件开发人员可能更加疾速地创立和部署本人的应用程序。然而长期以来,因为镜像格局的限度,容器启动镜像的加载是很慢的,相干背景细节能够参考“容器技术之容器镜像篇”。为了减速容器的启动,咱们能够将优化后的容器镜像搭配 P2P 网络等技术,从而无效升高容器部署启动的工夫,并可保障容器继续稳固运行,“让容器利用治理更快更平安,Dragonfly 公布 Nydus 容器镜像减速服务”。
除了启动速度,镜像分层、去重、压缩、按需加载等外围个性在容器镜像畛域也尤为重要。然而因为没有原生的文件系统反对,大多数都抉择了用户态计划,Nydus 最后亦如此。随着计划和需要的一直演进,用户态计划遇到了越来越多的挑战,如 性能与原生文件系统相比有较大差距、高密场景下资源开销较大等等。究其原因,次要是在用户态实现镜像格局的解析、按需加载等操作,将带来大量内核态 / 用户态通信开销。所以看起来解决了启动速度,这些外围性能的研发就会有不小的挑战,有点顾此失彼。
那么有没有可能做到鱼和熊掌兼得呢?为此,龙蜥社区做了一个大胆的尝试,咱们设计并实现了兼容内核原生 EROFS 文件系统的 RAFS v6 格局,心愿将容器镜像计划下沉到内核态 。 同时为了一劳永逸,咱们也尝试将这个计划推动到内核主线以让更多的人收益。最终,随着 Linux 内核各位大佬的挑战和咱们的不断完善,erofs over fscache 按需加载技术终于被合入 5.19 内核(链接见文末),至此 Nydus 镜像服务的下一代容器镜像散发计划逐步清晰起来。这也是 Linux 主线内核 首个原生反对、开箱即用的容器镜像散发计划 ,容器镜像的高密、高性能、高可用和易用性从此不再是个问题! 作者为此也给本人加了个鸡腿,哈哈!
本文将从 Nydus 架构回顾、RAFS v6 镜像格局和 EROFS over Fscache 按需加载技术三个角度来别离介绍这一技术的演变历程,并通过比照数据展现了以后计划的卓越性能,心愿大家可能尽早享受到容器启动飞一样的体验!
Nydus 架构回顾
一句话总结一下,Nydus 镜像减速服务 是一种优化了现有的 OCIv1 容器镜像架构,设计了 RAFS (Registry Acceleration File System) 磁盘格式,最终出现为一种文件系统的容器镜像格局的镜像减速实现。
容器镜像的基本需要,实质上是为了 提供容器的根目录 (rootfs),这能够通过文件系统 (file system) 或者是归档格局 (archive format) 来承载,当然也能够在文件系统的根底上二次套娃 (例如通过自定义的块格局来承载),但实质载体是一个目录树,体现为文件接口。
先看一下OCIv1 规范镜像,OCIv1 格局是一种基于 Docker Image Manifest Version 2 Schema 2 格局的镜像格局标准,由 manifest、镜像索引 (optional)、一系列容器镜像层及配置文件组成,细节能够参见相干文档,本文不再赘述。实质上说 OCI 镜像是一个以层为根本单位的镜像格局,每个层存储了文件级别的 diff data,以 tgz 归档格局存储,如下所示:
因为 tgz 的限度,OCIv1 存在一些固有问题,例如无奈按需加载、较粗的层级的去重粒度、每层 hash 值易变等等。
而一些“二次套娃”计划(例如基于自定义块格局的容器镜像计划),也存在一些原理性的设计缺点。例如:
- 容器镜像最终要体现为一棵目录树,那么就须要相应的文件系统来承载 (例如 ext4),这样整个链路为“自定义块格局 + 用户态块设施 + 文件系统”,绝对于文件系统计划其链路更长更简单,端到端稳定性不可控;
- 因为块格局对下层的文件系统不感知,无奈辨别文件系统的元数据和数据并别离解决 (例如压缩);
- 无奈实现基于文件的镜像剖析个性例如平安扫描、热点剖析和运行时拦挡等;
- 对于多个“二次套娃”容器镜像,无奈做到不批改 blob 内容间接 merge 成一个大镜像,也无奈做到不批改 blob 内容的状况下筛选局部文件造成一个子镜像,而这是文件系统计划的人造能力;
- 块设施 + 传统文件系统不反对 rootless 挂载,而 rootless 挂载是 rootless container 的要求。
而咱们实现的 Nydus 则是一种基于文件系统的容器镜像存储计划 。 其中将容器镜像文件零碎的数据 (blobs) 和元数据 (bootstrap) 拆散,让原来的镜像层只存储文件的数据局部。并且把文件以 chunk 为粒度宰割,每层 blob 存储对应的 chunk 数据;因为采纳了 chunk 粒度,这细化了去重粒度,chunk 级去重让层与层之间,镜像与镜像之间共享数据更容易,也更容易实现按需加载。因为元数据被独自分离出来合为一处,因而对于元数据的拜访不需拉取对应的 blob 数据, 须要拉取的数据量要小很多,I/O 效率更高。Nydus RAFS 镜像格局如下图所示:
RAFS v6 镜像格局
RAFS 镜像格局演变
在 RAFS v6 格局引入之前,Nydus 应用的是一个齐全用户态实现的镜像格局,通过 FUSE 或 virtiofs 接口提供服务。但用户态文件系统计划在设计上存在以下缺点:
- 大量零碎调用开销不可疏忽,例如深度为 1 的随机小 I/O 拜访;
- 当容器镜像中存在大量文件时,频繁的文件操作会产生大量的 fuse 申请,造成内核态 / 用户态上下文的频繁切换,造成性能瓶颈;
- 非 FSDAX 场景下,用户态到内核态的 buffer copy 会耗费 CPU 占用;
- 在 FSDAX (virtiofs 作为接口) 场景下,大量小文件会大量占用 DAX window 资源,存在潜在的性能抖动;频繁切换拜访小文件也会产生大量 DAX mapping setup 开销。
这些问题是用户态文件系统计划的人造限度带来的,而如果将容器镜像格局的实现下沉到内核态,就能够从原理上根治上述问题。因此咱们 引入了 RAFS v6 镜像格局,一个依靠于内核 EROFS 文件系统,实现于内核态的容器镜像格局。
EROFS 文件系统介绍
EROFS 文件系统自 Linux 4.19 内核开始存在于 Linux 主线中,过来次要用于嵌入式和挪动终端畛域,存在于以后各大风行发行版中 (例如 Fedora、Ubuntu、Archlinux、Debian、Gentoo 等等)。用户态工具 erofs-utils 也曾经存在于这些发行版和 OIN Linux system definition 列表中,社区较沉闷。
EROFS 文件系统具备如下特色:
- 实用于多种场景的原生本地只读块文件系统,磁盘格式具备最小 I/O 单位定义;
- page-sized 块对齐的不压缩元数据;
- 通过 Tail-packing 内联技术无效节俭空间,同时维持高拜访性能;
- 数据均以块为单位寻址 (mmap I/O 敌对,不需 I/O 后处理);
- 随机拜访敌对的磁盘目录格局;
- 外围磁盘格式非常简单,且易于减少 payload,扩展性更好;
- 反对 DIRECT I/O 拜访,反对块设施、FSDAX 等多种后端;
- 同时 EROFS 预留了 boot sector,可反对 bootloader 自启动等需要。
RAFS v6 镜像格局
过来一年,阿里云内核团队对 EROFS 文件系统进行了一系列的改良与加强,拓展其在云原生下的应用场景,使其适应容器镜像存储系统的需要,最终出现为一个实现于内核态的容器镜像格局 RAFS v6。而除了将镜像格局下沉到内核态,RAFS v6 还在镜像格局上进行了一系列优化,例如块对齐、更加精简的元数据等等 。
新的 RAFS v6 镜像格局如下:
改良后的 Nydus 镜像服务架构如下图所示,减少了对 (EROFS based) RAFS v6 镜像格局的反对:
EROFS over Fscache 按需加载技术
erofs over fscache是阿里云内核团队为 Nydus 开发的下一代容器镜像 按需加载 技术,同时也是 Linux 内核原生的镜像按需加载个性,于 5.19 版本合入 Linux 内核主线。
并被 Linux 内核权威媒体 LWN.net 整合入 5.19 合并窗口高亮个性(链接地址见文末):
在此之前业界已有的按需加载简直都是用户态计划。用户态计划会波及频繁的内核态 / 用户态上下文切换,以及内核态 / 用户态之间的内存拷贝,从而造成性能瓶颈。这一问题在容器镜像曾经全副下载到本地的时候尤其突出,此时容器运行过程中波及的文件拜访,都还是会陷出到用户态的服务过程。
事实上咱们能够将按需加载的 1) 缓存治理和 2) 缓存未命中的时候,通过各种路径 (例如网络) 获取数据,这两个操作解耦开。缓存治理能够下沉到内核态执行,这样当镜像在本地 ready 的时候,就能够防止内核态 / 用户态上下文的切换。而这也正是 erofs over fscache 技术的价值所在。
计划原理
fscache/cachefiles (以下统称 fscache) 是 Linux 零碎中绝对成熟的文件缓存计划,广泛应用于网络文件系统 (例如 NFS、Ceph 等)。咱们对其进行了性能加强与拓展,使其反对本地文件系统 (例如 erofs) 的按需加载个性。在该计划中,fscache 接管了缓存治理的工作。
此时容器在拜访容器镜像的时候,fscache 会查看以后申请的数据是否曾经缓存,如果缓存命中 (cache hit),那么间接从缓存文件读取数据。这一过程全程处于内核态之中,并不会陷出到用户态。
否则 (cache miss) 须要告诉用户态的 Nydusd 过程以解决这一拜访申请,此时容器过程会陷入睡眠期待状态;Nydusd 通过网络从远端获取数据,通过 fscache 将这些数据写入对应的缓存文件,之后告诉之前陷入睡眠期待状态的过程该申请曾经解决实现;之后容器过程即可从缓存文件读取到数据。
计划劣势
正如之前所形容的,在镜像数据曾经全副下载到本地的状况下,用户态计划会导致拜访文件的过程频繁陷出到用户态,并波及内核态 / 用户态之间的内存拷贝。而 erofs over fscache 下则不会再陷出到用户态,让按需加载真的 “按需”,从而在提前下载容器镜像的场景下实现 简直无损的性能和稳定性 ,最终取得 1) 按需加载与 2) 提前下载容器镜像这两种场景下真正 对立、无损 的计划。
具体来说 erofs over fscache 绝对于用户态计划具备以下劣势。
- 异步预取
容器创立之后,当容器过程尚未触发按需加载 (cache miss) 的时候,用户态的 Nydusd 就能够开始从网络下载数据并写入缓存文件,之后当容器拜访的文件地位恰好处于预取范畴内的时候,就会触发 cache hit 间接从缓存文件读取数据,而 不会再陷出到用户态。用户态计划则无奈实现该优化。
- 网络 IO 优化
当触发按需加载 (cache miss) 的时候,Nydusd 能够一次性从网络下载比以后理论申请的数据量更多的数据,并将下载的数据写入缓存文件。例如容器拜访 4K 数据触发的 cache miss,而 Nydusd 理论一次性下载 1MB 数据,以减小单位文件大小的网络传输延时。之后容器拜访接下来的这 1MB 数据的时候,就 不用再陷出到用户态。用户态计划则无奈实现该优化,因为即便触发 cache miss 的时候,用户态的服务过程同样实现了该优化,因为用户态计划实现的缓存治理在用户态,下一次容器拜访位于读放大范畴内的文件数据的时候,同样须要陷出到用户态以查看以后拜访的数据是否曾经缓存。
- 更佳的性能体现
当镜像数据曾经全副下载到本地的时候 (即不思考按需加载的影响),erofs over fscache 的性能体现显著优于用户态计划,同时与原生文件系统的性能相近,从而实现与原生容器镜像计划 (未实现按需加载) 相近的性能体现。以下是几个工作负载下的性能测试数据 [1]。
1、read/randread IO
以下是文件 read/randread buffer IO [2] 的性能比照:
“native” 示意测试文件间接位于本地的 ext4 文件系统中
“loop” 示意测试文件位于 erofs 镜像内,通过 loop 设施的 DIRECT IO 模式挂载 erofs 镜像
“fscache” 示意测试文件位于 erofs 镜像内,通过 erofs over fscache 计划挂载 erofs 镜像
“fuse” 示意挂载测试文件位于 fuse 文件系统 [3] 内
“ 性能 ” 一栏对各个模式下的性能进行归一化解决,以原生 ext4 文件系统的性能为基准,比拟其余模式下的性能
能够看到,fscache 模式下的 read/randread 性能与 loop 模式下的性能根本持平,同时要优于 fuse 模式;然而与原生 ext4 文件系统的性能仍存在肯定差距,咱们正在进一步剖析和优化,实践上该计划能够达到原生文件系统的程度。
2、文件元数据操作测试
通过对大量小文件执行 tar 操作 [4] 测试文件元数据操作的性能。
能够看到 erofs 格局的容器镜像的元数据性能甚至优于原生 ext4 文件系统,这是 erofs 非凡的文件系统格局导致的。因为 erofs 是一个只读 (read-only) 文件系统,因此其所有元数据能够严密排布在一起,而 ext4 作为可写文件系统,其元数据则扩散排布在多个 BG (block group) 中。
3、典型工作负载测试
测试 Linux 源码编译 [5] 这一典型工作负载下的性能体现。
能够看到,fscache 模式下的 Linux 编译负载性能与 loop 模式、原生 ext4 文件系统的性能根本持平,同时要优于 fuse 模式。
- 高密部署
因为 erofs over fscache 计划基于文件实现,即每个容器镜像都体现为 fscache 下的一个缓存文件,因此其 人造反对高密部署的场景。例如一个典型的 node.js 容器镜像在该计划下对应 ~20 个缓存文件,那么在一个部署有上百个容器的机器中,只须要保护上千个缓存文件。
- 故障复原与热降级
当镜像文件全副下载到本地的时候,镜像中文件的拜访不再须要用户态服务过程的染指,因此用户态服务过程存在更加富余的工夫窗口来 实现故障复原与热降级性能。这种场景下甚至不再须要用户态过程,从而实现与原生容器镜像计划 (未实现按需加载) 相近的稳定性体现。
- 对立的容器镜像计划
有了 RAFS v6 镜像格局和 erofs over fscache 按需加载技术,Nydus 同时实用于 runc 与 rund,作为这两种容器场景下的对立的容器镜像散发计划。
另外更重要的,erofs over fscache 是 1) 按需加载与 2) 提前下载容器镜像这两种场景下真正对立、无损的计划。一方面,它实现了按需加载个性,在容器启动的时候不须要容器镜像全副下载到本地,从而助力极致的容器启动速度。另一方面,它又完满兼容容器镜像曾经下载到本地的这一场景,在文件拜访过程中不再频繁陷出到用户态,从而实现与原生容器镜像计划 (未实现按需加载) 近乎无损的性能和稳定性体现。
瞻望与感激
之后咱们会对 erofs over fscache 计划进行继续迭代与欠缺,例如不同容器之间的镜像复用、FSDAX 反对以及性能优化等。
此外,目前 erofs over fscache 计划曾经合入 Linux 5.19 主线,后续咱们也会将 该计划回合到 OpenAnolis (5.10 和 4.19 内核),使得龙蜥内核真正开箱可用,届时欢送大家应用。
最初感激计划开发过程中反对和帮忙过咱们的所有集体与团队,感激字节跳动与快手的同学对该计划的大力支持,包含但不限于社区声援、测试、代码奉献等,欢送感兴趣的小伙伴退出龙蜥社区 SIG 钉钉群(文末扫描二维码或搜寻群号:34264214)和 Nydus 镜像服务 钉钉群(群号:34971767)交换,让咱们携手一起构建一个更好的容器镜像生态。
[1] 测试环境 ECS ecs.i2ne.4xlarge (16 vCPU, 128 GiB Mem),本地 NVMe 盘
[2] 测试命令 “fio -ioengine=psync -bs=4k -direct=0 -rw=[read|randread] -numjobs=1”
[3] 应用 passthrough 作为 fuse daemon,e.g. “passthrough_hp <src_dir> <tgt_dir>”
[4] 测试 “tar -cf /dev/null <linux_src_dir>” 命令的执行工夫
[5] 测试 “time make -j16” 命令的执行工夫
相干链接地址:
1. 龙蜥社区高性能存储技术 SIG 地址:
https://openanolis.cn/sig/hig…
2.erofs over fscache 合入 5.19 内核提交链接:
https://git.kernel.org/pub/sc…
- FUSE passthough_hp daemon:
https://github.com/libfuse/li…
- Nydus image service(请大家多多关注,欢送奉献):
https://github.com/dragonflyo…
- LWN.net 报道链接:
https://lwn.net/SubscriberLin…