初识 Containerd

559 阅读5分钟

初识 Containerd

Containerd 与 Docker

在之前的工作中,containerd 一直都是薛定谔的存在,只闻其名但没啥用武之地,直到前一段时间 K8s 官方宣布要废弃 Docker 时,才把这家伙又拉回到大众视野里。在技术层面上,我在之前的文章中也讨论过 CRI、containerd 以及 Docker 这些容器运行时之间的关系。有兴趣的小伙伴可以去看一下这几篇文章:

理解容器运行时

K8s、CRI 与 container

现在只是单纯好奇在非技术层面上 CNCF 为啥要这么做呢,所以还是去八卦一下 Containerd 和 Docker 的前世今生以及爱恨情仇。

在几年之前,Docker 公司在容器技术领域强势崛起,一家独大,Google、RedHat 这样的巨头们都产生了很大的危机感,因此他们想与 Docker 公司一起联合研发推进一个开源的容器运行时作为 Docker 技术的核心依赖。然鹅 Docker 公司很高冷的表示:我不干!巨头们听到这个反馈就很不爽啊,因此通过一些手段对 Docker 公司软硬兼施,使其将 libcontainer 捐给了开源社区,也就是现在的 runc,一个 low level 的容器运行时。此外,巨头们又成立了 CNCF 去对抗 Docker 公司的一家独大,CNCF 成立的思路很明确:在容器领域干不过 Docker,那就搞容器上层的建设——容器编排,从此 K8s 诞生了。虽然 Docker 公司也尝试使用 Swarm 去对抗 K8s,但最终也失败了。

自此,K8s 慢慢成为云原生领域的标配,其生态也越做越大、越做越完善。Docker 公司为了融入生态,开源了 Docker 的核心依赖 containerd 。此外 K8s 为了对接下一层的容器,也因为其中立性搞了一个运行时接口,也就是 CRI(Container Runtime Interface),runc、containerd 等运行时都去支持此接口。由于当时确实没有啥 high level 的 runtime,oci-o 虽然支持 CRI 接口,但其功能太弱;containerd 也尚未成熟,而且其当时的定位是嵌入到系统中,并非给终端用户使用;rkt 有自己的一套体系(后来这个项目也失败了)。只能暂时为了适配 Docker 搞了个 shim,将 CRI 转换为 Docker API。K8s 和 Docker 进入了冷静期,双方都在各自优化自己,互不干扰。然而平静之下仍是暗潮汹涌,CNCF 社区一直在不断完善 containerd,其定位也发生了改变,由原来的系统嵌入组件,变成了今天的“工业标准容器运行时”,并提供给终端用户使用。直到去年,K8s 宣布废弃使用 Docker,而改用 Containerd。其实除了这些商业因素,另一方面 K8s 已经提供了标准接口对接底层容器运行时,不再想单独维护一个 类似于 Docker shim 的适配层去迎合不同的运行时,这样做也无可厚非(其实我看就是自己做大了,把锅扔给底层了~嘘~)。

Containerd 架构

好了,现在瓜也吃完了,回到技术层面上来看看 Containerd 的架构是什么样的。首先看 containerd 的功能:

  • 官网宣称自己支持 OCI 的镜像标准
  • OCI 的容器运行时
  • 镜像的推送和拉取
  • 容器运行时生命周期管理
  • 多租户的镜像存储
  • 网络管理以及网络 namespace 管理,支持容器网络加入已有的 namespace

我就直接好家伙,Docker 核心功能该有的都有了。再看看架构图

containerd architecture diagram

Containerd 的设计是一个大的插件系统,图中每一个虚线框其实就对应一个插件。

从下往上看,底层支持 arm 和 x86 架构,支持 Linux 和 windows 系统。

中间 containerd 包含三层: Backend、core、API。其中 Backend 中 Runtime plugin 提供了容器运行时的具体操作,为了支持不同的容器运行时 containerd 也提供了一系列的 containerd-shim,如之前的文章 K8s & kata container 原理实践 提到的 shim 就是这个。 Core 则是核心部分,提供了各种功能的服务,其中比较常用的是 Content service ,提供对镜像中可寻址内容的访问,所有不可变的内容都被存储在这里;Images Service 提供镜像相关的操作;Snapshot Plugin : 用来管理容器镜像的文件系统快照。镜像中的每一个 layer 都会被解压成文件系统快照,类似于 Docker 中的 graphdriver。再往上则是 API 层,通过 GRPC 与客户端连接,这其中提供了给 Prometheus 使用 API 来进行监控,这里给个好评!

最上层则是各种客户端,包括 K8s 的 kubelet,containerd 自己的命令行 ctr 等。

简化一下上图:

architecture.png

简化后,Containerd 分为三大块: Storage 管理镜像文件的存储; Metadata 管理镜像和容器的元数据;另外由 Task 触发运行时。对外提供 GRPC 方式的 API 以及 Metrics 接口。

总结

其实 containerd 的社区一直很活跃,这个项目的成熟离不开社区各位的贡献。这篇文章重点在于回顾一下历史,以及现在它现在的架构。作为使用者就暂时不去研究其内部设计,相信也是及其复杂且精妙的。仅在使用方面,我发现 containerd 和 Docker 还是有很多共同之处的,那么下一篇文章则着重聊一聊 containerd 的实践吧。