Docker containerd CRI-O runc 傻傻分不清

May 11, 2022 23:00 · 2257 words · 5 minute read Container Kubernetes

Docker 掀起容器的热潮以来,各种容器工具、标准和 API 也相继爆发。docker 到底是啥,CRI 和 OCI 这些术语代表什么?本文来捋一捋。

Docker 一词可以表示 Docker 公司、Docker 容器、Docker 镜像 还有我们习惯使用的 Docker 工具。**但如今容器已不再与 Docker 紧密耦合。**我们可以使用 Docker 或一大堆非 Docker 工具运行容器。docker 只是诸多选项中的一个,Docker 公司支持容器生态中的一些工具,但并非全部。

我们将围绕容器生态和以及每个部分的作用。要记住:Docker 不能代表容器。

容器生态系统

容器生态系统由许多令人兴奋的技术、大量行话组成,还充斥着大公司之间的斗争。

幸运的是这些公司偶尔也会休战达成某些共识。标准使得生态系统更健壮,这样我们可以在不同的操作系统和平台上运行软件,并降低对单一公司或项目的依赖。

你要了解的容器相关的主流标准是:

  1. Open Container Initiative(OCI)发布了容器及其镜像的规范
  2. Container Runtime Interface(CRI)定义了 Kubernetes 和下层容器运行时之间的 API

下图准确展示了 Kubernetes、Docker、CRI、OCI、containerd 和 runc 在这个生态系统中是如何组织到一起的。

container-ecosystem

Docker

必须先从 Docker 开始,因为它是最流行的容器工具。而且对许多人来说,Docker 就是容器的同义词。

Docker 掀起了整场革命。Docker 创建了一个非常跟手的工具来处理容器——也被称为 docker。

docker 被设计成安装在个人电脑或服务器上的一组工具,不论开发者还是个人都能够轻松用它构建或运行容器。

docker 命令行工具可以构建容器镜像、从远程仓库拉取镜像、创建和管理容器。docker 现在主要由这些项目组成:

  • docker-clidocker ... 命令行交互工具
  • containerd:运行和管理容器的守护进程,推送和拉取镜像、管理存储和网络并监视容器的运行状态。
  • runc:底层的容器运行时,真正创建并运行容器的东西,包括 libcontainer(Go 编写的容器创建实现)。

当你使用 docker run 创建容器,实际上是通过 Docker daemon、containerd 和 runc 运行它。

Dockershim:Kubernetes 中的 Docker

Kubernetes 曾经包含了一个叫做 dockershim 的组件(v1.24 版本中彻底移除),使它能够支持 Docker。

Kubernetes 偏向于通过支持 Container Runtime Interface(CRI)接口的任何容易运行时运行容器,但 Docker 比 Kubernetes 出现得更早,并没有实现 CRI。这就是 dockershim 存在的原因,基本上可以认为是将 Docker 绑到 Kubernetes 上。

在软件系统中,shim 扮演不同 API 之间桥的角色,或作为兼容层。当你想使用一个第三方的组件,但需要少许胶水代码来使其奏效,有时候就会添加一个 shim。

移除 dockershim 并不意味着 Kubernetes 无法运行 Docker 格式的容器。不论 containerd 还是 CRI-O 都能运行 Docker 格式(实际上是 OCI 格式)的镜像,只是无需通过 docker 命令或 Docker daemon 来进行。

Docker 镜像

大家所说的 Docker 镜像,实际上是以 Open Container Initiative(OCI)格式打包的。

不论从 Docker Hub 还是其他 registry 拉取的镜像,都能够用 docker 命令使用它,亦或者是 Kubernetes 集群中,或者通过 podman 工具,还是其他支持 OCI 镜像格式规范的工具。

这就是有一个开放的标准的好处——任何人都可以编写支持标准的软件。

Container Runtime Interface(CRI)

CRI 是 Kubernetes 用来控制不同运行时创建和管理容器的协议。

CRI 抽象了容器运行时,Kubernetes 无需关心到底是哪一种。Kubernetes 不应该自身支持每一种容器运行时,那样代码库会更庞大而且难以管理,CRI API 描述了 Kubernetes 如何与运行时交互。这样,实际上管理容器的是下面的容器运行时。

cri

不论 containerd 还是 CRI-O 都可以使用,因为这两种运行时都实现了 CRI 规范。作为用户的我们无需关心,每种 CRI 实现略有不同但旨在可插拔和无缝修改。

红帽的 OpenShift 使用 CRI-O 并为其提供支持而 Docker 支持它们自己的 containerd。

containerd

containerd 是来自于 Docker 的高级别容器运行时,并实现了 CRI 规范。真正创建和运行容器进程的是它所控制的底层运行时。

  • containerd 从 Docker 项目中拆分出来使其更模块化。
  • Docker 自己内部使用 containerd,安装 Docker 同时也会安装 containerd。
  • containerd 通过它的 cri 插件实现了 Kubernetes CRI。

CRI-O

CRI-O 是另一种高级别的容器运行时,也实现了 CRI。它作为 containerd 的替代选项,也通过底层容器运行时运行容器进程。

  • CRI-O 诞生于 Red Hat、IBM、Intel、SUSE 等大公司。
  • 它就是专门作为 Kubernetes 容器运行时被打造的。

Open Container Initiative(OCI)

OCI 是一组科技公司维护的规范,定义了容器镜像格式还有容器应该如何运行。

  • OCI 背后的理念是我们可以选择不同的遵循该规范的运行时,这些运行时有着不同的底层实现。
  • 举个栗子,Linux 主机有 OCI 兼容的实现,Windows 也有。

runc

runc 是一种 OCI 兼容的底层容器运行时。它实现了 OCI 规范并运行容器进程。

runc 是 OCI 的参考实现

参考实现通常是第一个根据规范开发的软件。

runc 为容器提供了所有底层功能:利用底层的 Linux 功能,例如命名空间和控制组。

runc 的几个替代选项:

  • crun:C 编写的容器运行时(runc 是 Go 编写的)
  • KataContainer 项目的 kata-runtime:将 OCI 规范实现为轻量级的虚机
  • Google 的 gVisor:创建有自己内核的容器。它在自己的运行时 runsc 中实现 OCI。

runc 是在 Linux 上运行容器的工具;在 Windows 操作系统中则是微软的 Host Compute Service(HCS),包括了一个叫 runhcs 的工具。

containerd-shim

containerd 并不直接调用 runc 去创建和运行容器,而是通过 containerd-shim 来进行。

runc 创建完容器会直接退出,于是 containerd-shim 就成为容器的父进程,伴随容器的整个生命周期监控其运行状态。这样避免了 containerd 进程挂掉导致主机上的所有容器都退出。

总结

正如你所见,Docker 只是容器生态系统中的一小部分,两个主流标准(CRI 与 OCI)和 containerd、runc 还有 CRI-O 这些项目构成了如今开放的容器世界。