【Docker】进阶之路:(五)Docker引擎
- Docker引擎简介
- Docker引擎的组件构成
- runc
- containerd
Docker引擎简介
Docker引擎是用来运行和管理容器的核心部分。Docker首次发布时,Docker 引擎由LXC 和 Docker daemon 两个核心组件构成。
Docker daemon 是单一的二进制文件,包含诸如 Docker 客户端、Docker API、容器运行时、镜像构建等。 LXC 提供了对诸如命名空间(Namespace)和控制组(CGroup)等基础工具的操作能力,它们是基于 Linux 内核的容器虚拟化技术。
在 Docker 旧版本中,Docker daemon、LXC 和操作系统之间的交互关系。
其中,LXC是基于Linux的,存在对外部工具的严重依赖关系,对于Docker的跨平台目标的实现是个问题。因此,Docker公司开发了名为Libcontainer的自研工具,用于替代LXC。Libcontainer的目标是成为与平台无关的工具,可基于不同内核为Docker上层提供必要的容器交互功能。在Docker 0.9版本中,Libcontainer取代LXC成为默认的执行驱动。同时,Docker的整体性带来了越来越多的问题。难于变更、运行越来越慢,这对于Docker生态的发展来说弊大于利。Docker公司意识到了这些问题,开始努力着手拆解这个大而全的Dockerdaemon,并将它模块化。尽可能地拆解出其中的功能特性,并用小而专的工具来实现它。这些小工具可以是可替换的,也可以被第三方拿去用于构建其他工具。目前,所有容器执行和容器运行时的代码已经完全从daemon中移除,并重构为小而专的工具。
在改进版本中,基于开放容器计划,Docker引擎采用了模块化的设计原则,组件是可以替换的。Docker引擎主要由Docker Client、Docker daemon、containerd、runc组成,共同负责容器的创建和运行。
Docker引擎的组件构成
runc
在 Docker daemon 进程的拆解和重构时,OCI 也正在着手定义两个容器相关的规范,即镜像规范和容器运行时规范,两个规范均于 2017 年 7 月发布了 1.0 版。
Docker 公司参与了这些规范的制定工作,并贡献了许多代码。从 Docker 1.11 版本(2016 年初)开始,Docker 引擎尽可能实现了 OCI 的规范。例如,Docker daemon 不再包含任何容器运行时的代码——所有的容器运行代码在一个单独的 OCI 兼容层中实现。默认情况下,Docker 使用 runc 来实现这一点。runc 是 OCI 容器运行时标准的参考实现,如上面图5-3中的 runc 容器运行时层。runc 项目的目标之一就是与 OCI 规范保持一致。目前 OCI 规范均为 1.0 版本,我们不希望它们频繁地迭代,毕竟稳定胜于一切。除此之外,Docker 引擎中的 containerd 组件确保了 Docker 镜像能够以正确的 OCI Bundle 的格式传递给 runc。其实,在 OCI 规范以 1.0 版本正式发布之前,Docker 引擎就已经遵循该规范实现了部分功能。
runc实质上是一个轻量级的、针对Libcontainer进行了包装的命令行交互工具。Libcontainer取代了早期Docker架构中的LXC。runc的作用是创建容器,而且速度非常快。不过runc是一个CLI包装器,实质上就是一个独立的容器运行时工具。因此,直接下载runc或基于源码编译二进制文件,即可拥有一个全功能的runc。但runc只是一个基础工具,并不提供类似Docker引擎所拥有的丰富功能。
有时也将runc所在的架构层称为OCI层。关于runc的发布信息见GitHub中opencontainers/runc库的release。
containerd
在对 Docker daemon 的功能进行拆解后,所有的容器执行逻辑被重构到一个新的名为 containerd(发音为 container-dee)的工具中。Containerd的主要任务是容器的生命周期管理,即start | stop | pause | rm … containerd等。它在 Linux 和 Windows 中以 daemon 的方式运行,从 1.11 版本之后 Docker 就开始在 Linux 上使用。
Docker 引擎技术栈中,containerd 位于 daemon 和 runc 所在的 OCI 层之间。Kubernetes 也可以通过 cri-containerd 使用 containerd。
正如Docker 引擎简介中所述,containerd 最初被设计为轻量级的小型工具,仅用于容器的生命周期管理。然而,随着时间的推移,它被赋予了更多的功能,例如镜像管理等。其原因之一是,这样便于在其他项目中使用containd。例如,在 Kubernetes 中,containerd 就是一个很受欢迎的容器运行时。然而在 Kubernetes项目中,如果 containerd 能够完成一些诸如 push 和 pull 镜像这样的操作就更好了。因此,如今的 containerd 还能够完成一些除容器生命周期管理之外的操作。不过,所有的额外功能都是模块化的、可选的,便于自行选择所需功能。 Kubernetes项目在使用 containerd 时,可以仅包含所需的功能。