• Docker引擎是用来运行和管理容器的核心软件(类似VMware的ESXi),基于开放容器计划(OCI)相关标准,采用模块化的设计原则。
  • 当前Docker引擎的模块化工作仍在进行中。

    1. Docker引擎版本

    1.1 Docker引擎历史版本

  • Docker首次发布时,Docker引擎由核心组件:LXC和Docker daemon。

  • LXC提供了命名空间(Namespace)和控制组(GGroup)等功能,它们是基于Linux内核的容器虚拟化技术。
  • Docker daemon是单一的二进制文件,包含Docker客户端、Docker API、容器运行时、镜像构建等。

Docker引擎 - 图1

1.2 如今版本的Docker引擎

  • Docker引擎主要的构成组件:Docker客户端、Docker守护进程(Docker daemon)、containerd和runc,它们共同负责容器的创建和运行。

Docker引擎 - 图2

1.2.1 用Libcontainer替代LXC

  • LXC是基于Linux的,对于跨平台的Docker来说是隐患;其次是核心组件依赖外部有风险。
  • 在 Docker 0.9 版本中,Libcontainer 取代 LXC 成为默认的执行驱动。Libcontainer 的目标是成为与平台无关的工具,可基于不同内核为 Docker 上层提供必要的容器交互功能。

    1.2.2 摒弃大而全的Docker daemon

  • 大而全的Docker daemon带了难于变更、运行越来越慢等问题,于是对其进行拆解并模块化,用小而专的工具来实现它。

Docker引擎 - 图3

  • Docker daemon 不再包含任何容器运行时的代码,所有的容器运行代码在一个单独的 OCI 兼容层中实现。Docker 使用 runc 来实现这一点。
  • 旧版daemon升级需要停止daemon,因此会kill所有物理机运行中的容器,而新版daemon不存在该问题。
  • daemon主要功能包括但不限于镜像管理、镜像构建、REST API、身份验证、安全、核心网络、编排和卷等。

    1.2.3 runc

  • runc 实质上是一个轻量级的、针对 Libcontainer 进行了包装的命令行交互工具。

  • runc 只有一个作用——创建容器。不过它是一个 CLI 包装器,实质上就是一个独立的容器运行时工具。

    1.2.4 containerd

  • 在对 Docker daemon 的功能进行拆解后,所有的容器执行逻辑被重构到 containerd 中。它的主要任务是容器的生命周期管理——start | stop | pause | rm….,随着时间的推移,它被赋予了更多的功能,比如镜像管理,其原因之一在于,这样便于在其他项目中使用它(如: Kubernetes)。

  • containerd 在 Linux 和 Windows 中以 daemon 的方式运行,从 1.11 版本之后 Docker 就开始在 Linux 上使用它。
  • Docker 引擎技术栈中,containerd 位于 daemon 和 runc 所在的 OCI 层之间。Kubernetes 也可以通过 cri-containerd 使用 containerd。

    1.2.5 shim

  • shim 是实现无 daemon 的容器(用于将运行中的容器与 daemon 解耦,以便进行 daemon 升级等操作)不可或缺的工具。每次创建容器时 shim 都会 fork 一个新的 runc 实例,容器创建完会退出对应的 runc 进程,相关联的 containerd-shim 进程就会成为容器的父进程。

  • shim的职责:

    • 保持所有 STDIN 和 STDOUT 流是开启状态,从而当 daemon 重启的时候,容器不会因为管道(pipe)的关闭而终止。
    • 将容器的退出状态反馈给 daemon。

      1.3 启动一个新的容器过程

      执行命令 docker container run --name ctr1 -it alpine:latest sh

  • 当执行如上命令时,Docker客户端会将其转换为合适的API格式,发送到正确的API端点。

  • daemon接收到创建容器的命令,会向containerd发出调用(daemon使用CRUD风格的API,通过gRPC与containerd进行通信)。
  • containerd将Docker镜像转为OCI bundle,并让runc基于此创建一个新的容器。
  • runc与操作系统内核接口进行通信,基于必要的工具(Namespace、GGroup等)来创建容器。容器进程作为runc的字进程启动,启动完毕后runc退出。

Docker引擎 - 图4