5分钟!带你吐槽容器镜像中那些让人一言难尽的事情

容器镜像中那些让人一言难尽的事情

现如今容器生态大受欢迎有许多原因,其中一个就是应用部署上的优秀体验,容器镜像的出现是这种体验能够成为现实的一个基础。
简要回顾一下容器镜像,概括为几点,

  1. 它提供了一个应用软件运行的完整操作系统包,是不可变基础设施的一部分,其格式如下图1 所示。
  2. 镜像中一个重要的概念是layer,引用docker documentation中的介绍,"Each layer is only a set of differences from the layer before it. The layers are stacked on top of each other."
  3. 一个容器镜像是由一个或多个layer组成的。
  4. 容器镜像是存储在registry中的,诸如Docker Hub和公有云提供商的registry service.

5分钟!带你吐槽容器镜像中那些让人一言难尽的事情

但今天我们要一起来吐槽一下容器镜像。

1. 首次启动中大型镜像很慢

5分钟!带你吐槽容器镜像中那些让人一言难尽的事情

第一次运行时总是需要等上几分钟,这是因为在正式启动一个容器之前,还需要一些准备工作,

  1. 从registry下载镜像,包含了镜像的所有layers,比如上图的镜像"rust:latest"中包含6 layers,
  2. 把镜像layers一个接一个地解压到本地filesystem中,
  3. 最后用overlayfs 将这些layer组合为一个mount point作为容器的rootfs,
  4. 启动容器。

其中,当启动一个庞大的镜像时,最耗时的是第一步中的下载镜像,但因为单一layer是一个整体,对同一个layer无法并行下载,与此同时镜像size很大通常意味着包含的layer size也很大,所以大部分时间都花在了网络传输上。

2. 去重低效

5分钟!带你吐槽容器镜像中那些让人一言难尽的事情

如果大家关注过镜像base layer,比如上图中的CentOS 和 Ubuntu,其中的目录结构是相同的标准操作系统根分区结构,内容也是类似的,那么我们很自然地可以想到,在registry中存储的时候会将他们去重(Deduplication)吗?
对于上图这种情况,registry 现在不会做去重,原因仍是“单一layer是一个整体”,registry 的去重是基于layer的digest id的,即如果两个不同镜像使用了同一个layer,那么在registry上只存一个layer。

3. 对删除和链接的处理

先说删除,当对lower layer中的一个file做unlink时,会在upper layer 发生什么?
现在的逻辑是在upper layer添加一个特殊的white-out empty file来表示被unlink的file,以达到最终视图上的unlink效果。
5分钟!带你吐槽容器镜像中那些让人一言难尽的事情

“你说你一个没点,完事我们还得搭一个是不"

想象一下,如果想删掉一个password file,这个white-out 做法会带来什么后果?

再说hardlink,当对lower layer中的一个file做hardlink时,会在upper layer发生什么?当前采取了一种笨拙的做法,即把lower layer file拷贝一份到upper layer,再添加对应的hardlink。当这个file size 很大时,那么浪费的存储空间就相当可观了。

4. 安全审计不完整

再说一个安全审计上的问题,因为镜像每一个layer都有对应的digest id,在镜像下载之后可以对layer进行校验,但当把镜像layers一个接一个地解压到本地filesystem中之后呢?我们没有办法再去校验了,因为本质上解压后的filesystem目录和镜像的layers是两种不同的形态了,所以运行时的安全审计是缺失的。

成也layer,败也layer

我们回过头来看,上面的问题除了最后的安全审计,其它的原因都可以归结为“以layer为基本单位的镜像格式”。所以,虽然选择layer的设计是为了让不同的应用软件镜像能够共用同一部分基础系统,达到节省存储和节省传输的目的,但是在实践中用户们也逐渐发现了它带来的难以workaround的问题。

上一篇:【无标题】


下一篇:三个姑娘:NAS网络存储与SAN和DAS的区别