容器技术之容器镜像篇
前言
容器镜像的概念
1)容器镜像
2)OCI 标准镜像规范
This specification defines an OCI Image, consisting of a manifest,
an image index (optional), a set of filesystem layers, and a configuration.
3)容器的工作流程
容器镜像技术发展中遇到的问题
1. 容器镜像使用者
1)问题一:启动容器慢
download 镜像。
unpack 镜像。
使用 overlayfs 将容器可写层和镜像中的只读层聚合起来提供容器运行环境。
其中,download 镜像时需要 download 整个镜像,不能实现文件数据按需加载。再加上 download 镜像本身受限于网络带宽的影响,当容器镜像 size 在到几个 G 时,下载时间会较长,破坏了容器原本优秀的用户体验。
2)问题二:较高的本地存储成本
首先,层内部存在重复的数据。
其次,层与层之间可能存在大量重复的数据,但即使有微小的差别,也会被作为不同的层。
再次,根据 OCI image spec 对删除文件和 hardlink 的设计,一个镜像内部可能存在已经被上层删除的文件仍然存在于下层中,并包含在镜像中。
2. 镜像提供者侧
1)问题一:巨大的存储浪费
存在大量相似镜像 造成这种情况有两个原因:
首先,上面提到的层的缺点,在容器镜像中心会产生许多相似镜像。
其次,OCI image 使用了 tar+gzip 格式来表达镜像中的层,而 tar 格式并不区分 tar archive entries ordering,这带来一个问题,即如果用户在不同机器上 build 去同一个镜像,最终可能会因为使用了不同的文件系统而得到不同的镜像,然后用户上传之后,镜像中心中会存在若干不同镜像的实质内容是完全相同的情况。
镜像去重效率低:虽然镜像中心有垃圾回收来实现去重功能,但其仍然以层为单位,所以只能在有完全相同 hash value 的层之间去重。
2)问题二:云原生软件供应链带来的新需求
对容器镜像的思考和讨论
1. 业界的尝试
1)CernVM-FS
事实上,容器启动时间很长。
启动时数据读写放大系数很大(启动时中只使用 6% 的数据)。
分析了 57 个 docker image 的 layer 数量,发现一半以上的 image 的 layer 数量大于 9。
3)SquashFs
2. OCI 社区中的讨论
1)OCI 镜像规范的缺陷
-
tar 格式标准 -
tar 格式并不区分 tar archive entries ordering,这带来一个问题,即如果用户在不同机器上去 build 同一个镜像,最终可能会因为使用了不同的文件系统而得到不同的镜像,比如在文件系统 A 上的 order 是 foo 在 bar 之前进入 tar,在文件系统 B 上的 order 是 bar 在 foo 之前进入 tar,那么这两个镜像是不同的。 -
当 tar 被 gzip 压缩过之后不支持 seek,导致 run container 之前必须先下载并解压 targz 的 image layers,而不能实现文件数据按需加载。
-
以层为镜像的基本单位 -
内容冗余:不同层之间相同信息在传输和存储时都是冗余内容,在不读取内容的时候无法判断到这些冗余的存在。 -
无法并行:单一层是一个整体,对同一个层既无法并行传输,也不能并行提取。 -
无法进行小块数据的校验,只有完整的层下载完成之后,才能对整个层的数据做完整性校验。 -
其他一些问题:比如,跨层数据删除难以完美处理。
2)下一代镜像格式的要求
3. 阿里云在容器镜像上的思考
-
满足容器 "build once, run anywhere" 的理念。 -
实现在镜像中心和容器运行结点上存储资源上的高效使用。 -
在容器镜像的全链路上(build, ship, run)比现有的 OCI 镜像格式速度更快。 -
能够扩展在安全上的能力。 -
最大程度兼容已有基础设施,普惠大多数用户。
阿里云沙箱容器的镜像加速
在明确以上在技术发展过程中产生的需求之后,我们为设计了新的镜像格式 Rafs,并为 CNCF 下的 Dragonfly 项目引入了容器镜像服务,它能够极大缩短镜像下载时间,并提供端到端的镜像数据一致性校验,从而让用户能够更安全快捷地管理容器应用。
1. Rafs: 镜像格式
-
元数据层:元数据层是一颗自校验的哈希树。每个文件和目录都是哈希树中的一个附带哈希值的节点。一个文件节点的哈希值是由文件的数据确定,一个目录节点的哈希值则是由该目录下所有文件和目录的哈希值确定。 -
数据层:每个文件的数据被按照固定大小切片并保存到数据层中。数据切片可以在不同文件以及不同镜像中的不同文件共享。
2. Nydus: Dragonfly 的容器镜像服务
nydus 能够解析 FUSE 或者 协议来支持传统的或者阿里云沙箱容器。容器仓库、、NAS、以及 Dragonfly 的超级节点和 peer 节点都可以作为 nydus 的镜像数据源。同时,nydus 还可以配置一个本地缓存,从而避免每次启动都从远端数据源拉取数据。
3. 为什么选择基于文件的设计
-
镜像优化建议:在 build container 环节,提示用户有哪些文件是根本没有访问过的,可以考虑借此来优化镜像。 -
预读:在 run container 环节预加载,猜到用户要读文件,那就预先在读操作发生之前送过去,从而优化访问速度。 -
安全审计:在 run container 环节,如果一个容器访问镜像内容的模式和其他容器产生了明显差异,那么,这有可能是一个安全性风险。 -
变更风险发现:在 run container 环节,如果一个镜像升级之后,发现它访问内容的模式和之前发生了明显差异,那么,要么是程序自己有意变了,要么就可能是引入 bug 了,这时可以考虑提醒开发者这个变化。
总结