前面我们从实际操作角度学习了containerd,部署了containerd,并学习使用了ctr, crictl, nerdctl等命令行工具,体验了容器镜像的构建、拉取、推送,容器的启动和管理,并结合containerd容器学习了容器隔离性和资源限制背后的技术namespace和cgroups。在积累了这些实际操作的经验之后,本节一起来从概念角度学习一下Containerd的架构,进一步加深理解。本节学习内容主要来自Containerd的官方文档Containerd Architecture

1.服务端子系统(Bundle和Runtime)和组件

下图是containerd的架构示意图:

containerd-architecture.png

containerd是典型的C/S架构,在服务端包含Bundle和Runtime两大Subsystems(子系统)服务,外部用户通过GRPC API与服务进行交互。

  • Bundle: bundle服务允许用户从磁盘镜像中提取和打包bundles
  • Runtime: 运行时服务支持运行bundles,包括运行时容器的创建

一般每个子系统都有一个或多个相关的控制器组件来实现子系统的功能,并以服务的形式暴露给外部访问。 除了子系统之外,还有几个可能跨越子系统边界的组件,包含:

  • Executor: 执行器实现实际的容器运行时
  • Supervisor: 监控器监视并报告容器状态
  • Metadata: 元数据存储在bolt db,存储对images和bundles的任何持久引用
  • Snapshot: 管理文件系统上容器镜像的快照,对标Docker中的 graphdriver。镜像的层被分解成快照
  • Events: 支持事件的收集和使用,提供一致的事件驱动的行为和审计。事件可以重播到各个模块
  • Metrics: 每个组件都将暴露一些指标,通过Metrics API进行访问

2.客户端组件(Distribution)

为了灵活性,一些组件是在客户端实现的:

  • Distribution: 提供镜像的拉取和推送上传功能

3.containerd创建bundle的数据流

containerd在架构上被设计为主要是协调bundles的创建和执行。bundles是指被Runtime使用的配置、元数据、rootfs数据。一个bundle就是一个运行时的容器在磁盘上的表现形式,简化为文件系统中的一个目录。

下图是containerd创建bundle的数据流图:

containerd-data-flow.png

  1. 指示Distribution Controller去拉取一个具体的镜像,Distribution将镜像分层内容存储到内容存储中(content store),将镜像名和root manifest pointers注册到元数据存储中(metadata store)。
  2. 一旦镜像拉取完成,用户可以指示Bundle Controller将镜像分解包到一个bundle中。从内容存储中消费后,图像中的层被解压缩到快照组件中。
  3. 当容器的rootfs的快照准备好时,Bundle Controller控制器可以使用image manifest和配置来准备执行配置。其中一部分是将挂载从snapshot模块输入到执行配置中。
  4. 然后将准备好的bundle给Runtime子系统以执行, Runtime子系统将读取bundle配置来创建一个运行的容器。

以上对应containerd的数据根目录下的各个子目录:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
ll /var/lib/containerd/
io.containerd.content.v1.content
io.containerd.grpc.v1.introspection
io.containerd.metadata.v1.bolt
io.containerd.runtime.v1.linux
io.containerd.runtime.v2.task
io.containerd.snapshotter.v1.btrfs
io.containerd.snapshotter.v1.native
io.containerd.snapshotter.v1.overlayfs
tmpmounts

content中保存是OCI标准tar和mainfest配置文件的,即保存的是镜像,解压后,可以mount给容器使用。 snapshotter的主要作用是通过mount各个层,为容器准备rootfs。

参考