
镜像是容器的基础,如今有很多用户在实践使用 Harbor 作为镜像存储与分发方案,本文介绍了 Harbor 在支持镜像加速方面的能力,以及 Nydus 这种改进的镜像格式,用于解决镜像在网络,存储,端到端可信方面的问题。使用 Harbor 结合按需加载,P2P 分发与预热技术加速镜像拉取,可以将容器启动时间从分钟级降低到秒级。
OCI 镜像格式

OCI 容器镜像格式是由 tar/tar.gz/tar.zstd 等格式的多层数据组成的,在容器创建时 Containerd 等组件需要将所有层分别下载并且解压到目录下,再用 OverlayFS 这类文件系统按目录顺序堆叠起来,提供给容器一个只读的 RootFS 文件系统。这样的分层设计可以在多个镜像间复用数据,做到增量构建,但同时它也有很多局限。
数据全量下载
下载并解压镜像层是一个非常耗时的过程,有篇 研究[1]表明,镜像拉取操作所需的时间占容器启动时间的 76%,但只有约 6.4% 的数据在运行时被容器读取,这意味着大量可能用不到的数据会占用网络带宽而拖慢启动速度。
元数据更新成本高
镜像中文件的元数据和数据是以 tar 格式放在同一层的,因此文件名、权限位等元数据的改动,会导致整个镜像层的 Hash 变化。例如一个 10GB 的层,重命名了其中一个文件,即使文件数据部分没有更新,也会导致整个镜像层在构建阶段重新压缩、上传、存储以及在运行时重新下载。
不被察觉的冗余数据
在 Dockerfile 指令中删除文件时,根据 OCI Image Spec[2],会在 Upper 层生成一个 Whiteout 特殊空文件表示删除,而修改(甚至只修改元数据)文件,会在 Upper 层生成完整的新文件。这两种情况下,旧版本文件还存在于 Lower 层,在运行时,用不到的旧版本数据依然会被下载和解压,镜像越来越复杂时这样的问题很难察觉,导致镜像体积很难优化。
数据去重效率低
镜像只能以层的粒度,以 Hash 比较的方式在存储和下载中去重,但在文件内、层内、层与层之间、镜像与镜像间等多个维度上都可能存在重复数据,镜像越复杂,问题越明显。
本地镜像数据不可信
镜像下载后可以通过 Hash 校验一致性,但在容器启动后,访问镜像数据过程中不会再次校验数据完整性,无法知道 RootFS 中的文件是否被篡改,这导致镜像在运行时是不可信的。
重构 OCI 镜像格式
要解决上述数据全量下载的问题,一种思路是按需加载,即容器启动时只拉取需要读取的部分数据,而不是整个镜像层。但传统镜像层都是先用 tar 格式打包,再使用 gzip/zstd 等压缩算法压缩整个 tar 数据,无法直接做到随机访问的解压。

这里简单介绍一下 Nydus 镜像加速框架[3],Nydus 基于高性能只读文件系统格式 EROFS[4] 扩展出了 RAFS(Registry Acceleration File System) 文件系统格式,它将 gzip/zstd 镜像层,转换为 RAFS 数据和元数据两大部分。
镜像构建
RAFS 文件数据部分,按指定 Size(如 1MB)大小切割成小块数据(Chunk),例如一个 10.5MB 的文件会切割成 11 个小的 Chunk,将每个 Chunk 块使用
Zstd/LZ4等方式单独压缩,然后聚合在一起打包到一个文件(Blob)中,在打包过程中可以根据 Chunk Hash 或外部 Chunk 字典做去重,以降低镜像的增量尺寸;RAFS 文件元数据部分,全部存放在一个元数据(Metadata)文件中,除了文件元信息外,它还记录了文件有哪些 Chunk,Chunk 的 Hash 值,以及在上述 Blob 中的偏移位置(Offset);
另外,Nydus 镜像层始终比 OCI v1 镜像多了一层 Overlayed Metadata,这层 Metadata 是所有层的元数据堆叠,它通常很小,例如几十 GB 的镜像,Overlayed Metadata 通常有 10MB。

本文介绍了Harbor如何通过结合Nydus镜像格式和按需加载技术,提升镜像拉取速度,减少冗余数据,并实现更高效的存储和数据分发。Nydus重构了OCI镜像格式,通过FUSE和内核态文件系统优化,显著缩短了容器启动时间。
最低0.47元/天 解锁文章
887

被折叠的 条评论
为什么被折叠?



