Knoppix|镜像格式二十年:系统镜像的螺旋进化( 三 )


但 Docker 跳出了软件包这个思路 , 他们是这么看的——

  • 完整的操作系统就是一个包 , 它必然是自包含的 , 而且如果在开发、测试、部署时都保持同样完整、不变的操作系统环境 , 那么也就没有那么多依赖性导致的问题了;
  • 这个完整的操作系统都是不可变的 , 就像 Live CD 一样 , 我们叫它镜像 , 可以用 aufs 这样的 union FS 在上面放一个可写层 , 应用可以在运行时写东西到可写层 , 一些动态生成的配置也可以放在可写层;
  • 如果一些应用软件镜像 , 它们共用同一部分基础系统 , 那么 , 把这些公共部分 , 放在 Unionfs 的下层作为只读层 , 这样他们可以给不同的应用使用;当然 , 如果两个应用依赖的东西不一样 , 那它们就用不同的基础层 , 也不需要互相迁就了 , 自然没有上面的依赖性矛盾存在了;
  • 一个镜像可以包含多个层 , 这样 , 更方便应用可以共享一些数据 , 节省存储和传输开销;
大概的示意图是这样的:
Knoppix|镜像格式二十年:系统镜像的螺旋进化
文章图片

这样 , 如果在同一台机器上跑这三个应用(容器) , 那么这些共享的只读层都不需要重复下载 。
更进一步说 , Docker 这种分层结构的另一个优点在于 , 它本身是非常开发者友好的 , 可以看到 , 下面这个是一个 Dockerfile 的示意 , FROM 代表最底下的基础层 , 之后的 RUN, ADD 这样改变 rootfs 的操作 , 都会将结果存成一个新的中间层 , 最终形成一个镜像 。 这样 , 开发者对于软件依赖性的组织可以非常清晰地展现在镜像的分层关系中 , 比如下面这个 Dockerfile 是一个 packaging 用的 image , 它先装了软件的依赖包、语言环境 , 然后初始化了打包操作的用户环境 , 然后拉源代码 , 最后把制作软件包的脚本放到镜像里 。 这个组织方式是从通用到特定任务的组织方式 , 镜像制作者希望让这些层可以尽量通用一些 , 底层的内容可以在其他镜像中也用得上 , 而上层则是和本镜像的工作最直接相关的内容 , 其他开发者在看到这个 Dockerfile 的时候已经可以基本知道这个镜像里有什么、要干什么 , 以及自己是否可以借鉴了 。 这个镜像的设计是 Docker 设计里最巧妙的地方之一 , 也是为什么大家都愿意认同 , Solomon 要做的就是开发者体验(DX, Developer Experiences) 。
Knoppix|镜像格式二十年:系统镜像的螺旋进化
文章图片

【Knoppix|镜像格式二十年:系统镜像的螺旋进化】一个规范的 Docker Image 或者脱胎于其中的 OCI Image , 实际上就是一组元数据和一些层数据 , 这些层数据每个都是一个文件系统内容的打包 , 从某种意义上说 , 典型的 Live CD 的 OS , 基本上可以理解成一个只读层加上一个可写层的 Docker Container 的 rootfs 。 在 Docker 这里 , Union FS 可以说是 Great Again 了 。
未来:现代化的镜像系统 然而 , 尽管 Docker Image (或者说 OCI Image)的设计蕴含了“完整的操作系统就是一个包”的优秀思想 , 又利用 Union FS 实现了“分层”这种既实现漂亮的开发者体验 , 又能节约时间空间的精巧设计 , 但随着时间的推移 , 还是暴露出来了一些问题 。 从去年(2019年)年初 , OCI 社区中开始有人讨论下一代镜像格式的问题 , 这个热烈的讨论中 , 集中讨论了 OCIv1(实际也是 Docker 的)镜像格式的一些问题 , Aleksa Sarai 也专门写了一篇博客来讨论这个话题 , 具体说 , 除了 tar 格式本身的标准化问题外 , 大家对当前的镜像的主要不满集中在:
  • 内容冗余:不同层之间相同信息在传输和存储时都是冗余内容 , 在不读取内容的时候无法判断到这些冗余的存在;
  • 无法并行:单一层是一个整体 , 对同一个层既无法并行传输 , 也不能并行提取;
  • 无法进行小块数据的校验 , 只有完整的层下载完成之后 , 才能对整个层的数据做完整性校验;
  • 其他一些问题:比如 , 跨层数据删除难以完美处理;
上述这些问题用一句话来总结就是“层是镜像的基本单位” , 然而 , 镜像的数据的实际使用率是很低的 , 比如 Cern 的这篇论文中就提到 , 一般镜像只有6%的内容会被实际用到 , 这就产生了实质性的升级镜像数据结构 , 不再以层为基本单位的动力 。
可见 , 下一代镜像的一个趋势就是打破层的结构来对这些只读进行更进一步的优化 , 是的 , 反应快的同学可能已经回想起了前面提到的 Live CD 里常用的 Squashfs , 它可以根据读取的需要来解压相应的文件块来放入内存、供应用使用 , 这里是不是可以扩展一下 , 变成在需要的时候再去远端拉回镜像的内容来供应用使用呢——从文件的 Lazy decompress 到 Lazy Load , 一步之遥 , 水到渠城 。


推荐阅读