容器那些事儿-从graph dirver谈起

时间拉回到五年前,当安装docker时,都会看到grahp driver这一目录,但在docker将libe-containerd独立,并捐献成为containerd项目后,为何不见了grahp driver?

Containerd 并没有 graph driver,但是它有snapshotter。这样处理不是为了造新轮子,而是为了填坑,毕竟当初 graph driver 的设计,一直多为诟病。先来回顾一下 graph driver 的历史。

该文目前不介绍docker、containerd的演化转变史,后续会单独出文逐一介绍。

容器文件系统类型

容器中使用两种文件系统:overlay 和 snapshotting。AUFS 和 OverlayFS 都为 overlay 文件系统,有多个目录为镜像中的每一层提供文件 diff。而 snapshot 文件系统包括 devicemapper、btrfs 和 ZFS,它们在块层级处理文件 diff。overlay 通常工作在 EXT4 和 XFS 这类文件系统上,而 snapshot 文件系统只在其格式化的卷上。

graph driver的历史

探究graph driver的历史,必然有此番定体问:什么是 graph driver?为什么有?为何叫 graph driver?

当然要回答这些问题,需要把时间线拉得更长一点,回到 Docker 0.8 所在的时间线。当时的 Docker 只支持 Ubuntu,因为它是唯一支持 AUFS 的Linux发行版,Docker 使用 overlay 文件系统来构建镜像和容器的读写层。为了让 Docker 能够支持老版本的内核,需要 Docker 除 AUFS 以为更多的文件系统。因此,对支持device mapper(LVM thinpool)成为替换 AUFS 兼容老版本的可选项。

起初,device mapper 似乎能够解决所有的问题,当然 Docker 支持所有内核运行的目标确实由 device mapper 实现了。当时社区一度想取消对 AUFS 的支持,只让容器的文件系统使用 device mapper,但在测试过POC后,发现这并不是理想的解决方案。发行版之间存在诸多问题。社区确实需要实现支持Docker兼容多架构多Linux发行版的目标,但前提是不能以牺牲现有 Ubuntu 用户运行的性能为代价。

为了让更多Linux发行版用户用上 Docker,文件系统的支持必须是可插拔的。Solomon 设计了一个新的驱动 API 以支持 Docker 中的多个文件系统。Solomon设计了 API,而 Crosby 则改造 AUFS实现,以验证API的完备性。

他们将新 API 命名为 graph driver,因为 Docker 将镜像各层的关系建模在“图”中,而文件系统主要存储镜像。

起初 graph driver 接口设计的简易且运作稳定。但是随着时间的推移,需求日益增多,graph driver API 逐步支持下述功能:

  • 构建方面的优化,例如缓存和共享层以加速构建

  • 内容可寻址性特性,以确保文件系统被安全地识别

  • 运行时从 LXC 变更为 runc

这些变更导致 graph driver API 及其底层实现与它们所支持的高级功能逐渐交织糅合到一起,从而导致了一些问题:

  • graph driver API 日渐复杂

  • 每个驱动中都有内置的代码以优化构建

  • 驱动程序与容器生命周期紧密耦合

  • 难以维护,因为 graph driver 的范围很广

没有如今这些需求,一开始是不可能预见到这些设计问题的。

破局者snapshotter

直到捐献Containerd,才有时间来厘清这些长期存在的问题,并尝试去解决其中的一部分问题,确实也是时候重新构思 graph driver 应该如何与容器协作。

API 复杂度

为了解决 graph driver 巨型API 的问题,首先需要明确 overlay 和 snapshot 文件系统理应支持的功能。正如所知道的,snapshot 不如 overlay 文件系统灵活,因为快照有严格的层级关系。因为snapshot 在块层级工作,因此需要在创建子快照前必须生成一个父快照。

尽量在 I/O 方面最不灵活的地方抽象一个接口,这是编码实现的基本路线,这也是为什么 containerd 将文件系统组件称为 snapshotter,因为是以它们为模型设计了接口。在为 snapshotter 开发了最初的 API 后,支持多种 overlay 文件系统就变得易如反掌。

可以在这看到完整的接口以及每种方法的相关信息:https://pkg.go.dev/github.com/containerd/containerd/snapshot?utm_source=godoc#Snapshotter

容器生命周期的变化

在使用graph driver时,一直是驱动为容器挂载和卸载 root文件系统。这源于 当初Docker 还在使用 LXC 时,必须在完全挂载的 rootfs 中执行容器。在切到 runc 后,这个问题也就不存在了。

我们希望所有挂载都发生在 mount 命名空间内而非宿主机上,同时我们不希望 snapshotter 挂载任何东西,这样有以下几点好处:

  • 调用者作为 builder 或执行组件,可以决定何时需要挂载 rootfs;何时执行结束,这样就可以卸载。

  • 在容器的命名空间挂载,当容器消亡时,内核将卸载该命名空间中的所有东西。这解决了一些 graph driver 遗留文件句柄的问题。

设计snapshotter API 返回一个序列化的mount调用来实现,由调用者挂载和卸载。containerd 中的执行组件 containerd-shim 挂载容器的 rootfs,并在任务执行结束后卸载。

社区维护

最后,我们希望确保 snapshotter 可被长期维护。这点通过一个简易的接口来实现,允许我们去掉大部分调用者的需求,这样 snapshotter 就可以专注于成为一个 snapshotter。

从这一点来看,snapshotter 可以视作 graph driver 的一个演变版本。这也算修复graph driver用户面临的长期未决的一部分问题。

参考文献

1.https://blog.mobyproject.org/where-are-containerds-graph-drivers-145fc9b7255

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值