乐趣区

关于SegmentFault:对容器镜像的思考和讨论

作者 | Liu,Bo
起源 | 阿里巴巴云原生公众号

前言

常言道,startup 有 startup 的好,大厂有大厂的好,那么大厂到底好在哪呢?拿硅谷老牌大厂们 FLG 来说,如果要问最令人思念的是什么?Free food 和基础设施 (Infrastructure) 肯定是会上榜的,两者均极大晋升了宽广利用开发者的幸福指数。那么能不能“让天下没有难做的利用”呢?请大家把眼光投向正在衰亡的云原生生态。

在云原生生态中,容器服务包含了镜像和容器引擎两个局部。其中容器镜像作为外围的云原生利用制品,打包了残缺的操作系统和利用运行环境,利用的迭代也因为应用了这种不可变架构而变得更简略,更频繁。

本文将围绕着容器镜像这一外围,分享它的相干常识和业界的思考与实际。

容器镜像的概念

1)容器镜像

容器镜像有一个官网的类比,” 生存中常见的集装箱 ”,尽管领有不同的规格,但箱子自身是不可变的(Immutable),只是其中装的内容不同。

对于镜像来说,不变的局部蕴含了运行一个应用软件(如 mysql)所须要的所有元素。开发者能够应用一些工具(如 Dockerfile)构建出本人的容器镜像,签名并上传到互联网上,而后须要运行这些软件的人能够通过指定名称(如 _example.com/my-app_)下载、验证和运行这些容器。

2)OCI 规范镜像标准

在 OCI 规范镜像标准出台之前,其实有两套宽泛应用的镜像标准,别离是 appc 和 docker v2.2,但“合久必分,分久必合”,有意思的是两者的内容曾经在各自的倒退中逐渐异化了,所以 OCI 组织因势利导地在 docker v2.2 的根底上推出了 oci image format spec,规定了对于符合规范的镜像,容许开发者只有对容器打包和签名一次,就能够在所有的容器引擎上运行该容器。

这份标准给出了 OCI image 的定义:

This specification defines an OCI Image, consisting of a manifest, 
an image index (optional), a set of filesystem layers, and a configuration.

3)容器的工作流程

一个典型的容器工作流程是从由 developers 制作容器镜像开始的(build),而后上传到镜像存储核心(ship),最初部署在集群中(run)。

容器镜像技术倒退中遇到的问题

不得不说,容器镜像的设计是很出彩的,首先它蕴含了“残缺的操作系统就是一个包”的优良思维,带着大家跳出了安装包的思路,又提出了诸如 dockerfile 这样的晋升开发者体验的 killer features,还能利用分层构造来节约工夫空间。

不过,” 金无足赤,人无完人 ”,优良的设计并不等于优良的实际,上面来聊一聊问题具体出在哪。

1. 容器镜像使用者

1)问题一:启动容器慢

容器启动慢的状况广泛产生在当用户启动一个很大 size 的容器镜像时,因为在容器筹备阶段须要三步(以 overlayfs 为例):

  • download 镜像。
  • unpack 镜像。
  • 应用 overlayfs 将容器可写层和镜像中的只读层聚合起来提供容器运行环境。

其中,download 镜像时须要 download 整个镜像,不能实现文件数据按需加载。再加上 download 镜像自身受限于网络带宽的影响,当容器镜像 size 在到几个 G 时,下载工夫会较长,毁坏了容器本来优良的用户体验。

2)问题二:较高的本地存储老本

不同镜像之间能够共享的最小单位是镜像中的层,它的毛病之一是在 deduplication 上的效率是较低的,起因是:

  • 首先,层外部存在反复的数据。
  • 其次,层与层之间可能存在大量反复的数据,但即便有渺小的差异,也会被作为不同的层。
  • 再次,依据 OCI image spec 对删除文件和 hardlink 的设计,一个镜像外部可能存在曾经被下层删除的文件依然存在于上层中,并蕴含在镜像中。

所以,当不同镜像的容器被调度到同一台机器上运行时,镜像自身在本地文件系统中所占的存储空间是一笔不可漠视的老本开销。

2. 镜像提供者侧

这里的提供者次要指容器服务的镜像核心。

1)问题一:微小的存储节约

  • 存在大量类似镜像 造成这种状况有两个起因:

    • 首先,下面提到的层的毛病,在容器镜像核心会产生许多类似镜像。
    • 其次,OCI image 应用了 tar+gzip 格局来表白镜像中的层,而 tar 格局并不辨别 tar archive entries ordering,这带来一个问题,即如果用户在不同机器上 build 去同一个镜像,最终可能会因为应用了不同的文件系统而失去不同的镜像,而后用户上传之后,镜像核心中会存在若干不同镜像的本质内容是完全相同的状况。
  • 镜像去重效率低:尽管镜像核心有垃圾回收来实现去重性能,但其依然以层为单位,所以只能在有完全相同 hash value 的层之间去重。

2)问题二:云原生软件供应链带来的新需要

随着时间推移,和软件供应链一起倒退的还有对软件供应链环节的多样性攻打伎俩。平安防护是软件供应链中十分重要的组成,不光体现在对软件自身的平安加强,也体现在对供应链自身的平安加强。而因为利用运行环境被前置到了容器镜像中,所以对容器镜像的平安,包含对镜像的破绽扫描和签名成为了容器服务提供者的必要能力。

对容器镜像的思考和探讨

1. 业界的尝试

针对后面所述的问题,业界大小厂也是集思广益,各显神通,上面提几个典型的我的项目:

1)CernVM-FS

应用了 fuse 按需从近程加载须要的数据。

2)Slacker

通过设计一个镜像的 benchmark 奉献了几个有意思的实践根底:

  • 事实上,容器启动工夫很长。
  • 启动时数据读写放大系数很大(启动时中只应用 6% 的数据)。
  • 剖析了 57 个 docker image 的 layer 数量,发现一半以上的 image 的 layer 数量大于 9。

Slacker 最终应用了按需加载和缩小镜像层数将启动速度进步了 5-20 倍。

3)SquashFs

Oracle 应用 Linux SquashFS 来代替 targz 存储容器 image layer 的内容,去掉了 unpack tar 的环节。

2. OCI 社区中的探讨

自 2019 年开始,对于镜像自身的吐槽缓缓多了起来,发酵了一年多,OCI 社区感觉时机成熟了,从 2020 年 6 月开始,花了一个多月工夫密集探讨了以后 OCI 镜像标准的缺点,以及 OCIv2 镜像格局(*)须要满足哪些要求。

(*)OCIv2 在这里只是一个宣传命名,实际上 OCIv2 是以后 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)下一代镜像格局的要求

这次镜像格局大探讨从一个邮件和一份共享文档开始,并促成了屡次在线的 OCI 社区探讨会议。最初的论断也很鼓舞人心,在这份共享文档中能够找到对 OCIv2 镜像格局须要满足的要求的详细描述。咱们能够将这些要求分类为:

(*): 诸如 file timestamp 等只在一个特定机器上有意义的 metadata 是没有必要存在于镜像中的。

能够看出,下面这些要求明确了容器镜像的下一步重点在易用、效率、平安三个方面,达到在 “build – ship – run” 这三个阶段协同优化的目标。

3. 阿里云在容器镜像上的思考

阿里云始终踊跃地推动和倒退云原生生态,提供了基础设施“阿里云容器镜像服务(ACR)”作为用户云原生容器化的第一站,负责提供容器镜像、Helm Chart 等 OCI Artifacts 治理和散发服务。同时咱们也在联合容器业务现状深入对容器镜像格局的了解,一直地总结什么是满足倒退需要的容器镜像格局,这里能够概括为以下几点,新的镜像格局须要:

  • 满足容器 “build once, run anywhere” 的理念。
  • 实现在镜像核心和容器运行结点上存储资源上的高效应用。
  • 在容器镜像的全链路上(build, ship, run)比现有的 OCI 镜像格局速度更快。
  • 可能扩大在平安上的能力。
  • 最大水平兼容已有基础设施,普惠大多数用户。

阿里云沙箱容器的镜像减速

相比于社区的探讨重点放在了新的镜像格局的设计上,阿里云更关怀如何设计出一套优化全链路的镜像计划,为客户带来可能利用在生产中的价值。

在明确以上在技术倒退过程中产生的需要之后,咱们为阿里云沙箱容器设计了新的镜像格局 Rafs,并为 CNCF 下的 Dragonfly 我的项目引入了容器镜像服务,它可能极大缩短镜像下载工夫,并提供端到端的镜像数据一致性校验,从而让用户可能更平安快捷地治理容器利用。

1. Rafs: 镜像格局

Rafs 把一个容器镜像只分成元数据和数据两层。其中:

  • 元数据层:元数据层是一颗自校验的哈希树。每个文件和目录都是哈希树中的一个附带哈希值的节点。一个文件节点的哈希值是由文件的数据确定,一个目录节点的哈希值则是由该目录下所有文件和目录的哈希值确定。
  • 数据层:每个文件的数据被依照固定大小切片并保留到数据层中。数据切片能够在不同文件以及不同镜像中的不同文件共享。

2. Nydus: Dragonfly 的容器镜像服务

除了应用下面的镜像格局 Rafs,Nydus 还蕴含一个负责解析容器镜像的 FUSE 用户态文件系统过程。

nydus 可能解析 FUSE 或者 virtiofs 协定来反对传统的 runC 容器或者阿里云沙箱容器。容器仓库、OSS 对象存储、NAS、以及 Dragonfly 的超级节点和 peer 节点都能够作为 nydus 的镜像数据源。同时,nydus 还能够配置一个本地缓存,从而防止每次启动都从远端数据源拉取数据。

基于这个设计架构,nydus 别离在 build, ship, run 和兼容性方面提供上面这些优化:

3. 为什么抉择基于文件的设计

在设计之初,Nydus 抉择了基于文件的设计而不是基于块的设计,为什么这样做呢?

次要的起因是,咱们想在镜像减速的根底上做基于容器特点的附加能力,这所有都建设在可能获取到镜像中的文件元数据;而基于块的设计只应用 disk LBA,人造的无奈获取其下层(即文件系统)中的信息。

有了文件元数据之后,咱们轻松地实现了以下几个增值性能:

  • 镜像优化倡议:在 build container 环节,提醒用户有哪些文件是基本没有拜访过的,能够思考借此来优化镜像。
  • 预读:在 run container 环节预加载,猜到用户要读文件,那就事后在读操作产生之前送过来,从而优化访问速度。
  • 平安审计:在 run container 环节,如果一个容器拜访镜像内容的模式和其余容器产生了显著差别,那么,这有可能是一个安全性危险。
  • 变更危险发现:在 run container 环节,如果一个镜像降级之后,发现它拜访内容的模式和之前产生了显著差别,那么,要么是程序本人无意变了,要么就可能是引入 bug 了,这时能够思考揭示开发者这个变动。

总结

OCI image 分层镜像机制尽管极大中央便了开发,但在大规模集群运行时,也有颇多有余,对此,OCI 镜像社区也在寻求着如何利用镜像内容可感知性,让它更加疾速、节俭资源,也更加平安。阿里云在这个根底上本着为客户带来价值的准则,提出了私有云上对镜像的稳定性、预读等需要,并为阿里云沙箱容器研发出了相应的的镜像减速计划,实现 “build-ship-run” 整个镜像链路上的对立优化,让用户不光听着冷落,也能用着开心,切实失去云原生基础设施倒退的红利。

点击参加“容器镜像应用考察问卷”填写,将有 10 位随机获赠阿里云容器镜像服务(企业版)ACR EE 50 元优惠券哦~

退出移动版