- Docker引擎是用来运行和管理容器的核心软件(类似VMware的ESXi),基于开放容器计划(OCI)相关标准,采用模块化的设计原则。
-
1. Docker引擎版本
1.1 Docker引擎历史版本
Docker首次发布时,Docker引擎由核心组件:LXC和Docker daemon。
- LXC提供了命名空间(Namespace)和控制组(GGroup)等功能,它们是基于Linux内核的容器虚拟化技术。
- Docker daemon是单一的二进制文件,包含Docker客户端、Docker API、容器运行时、镜像构建等。
1.2 如今版本的Docker引擎
- Docker引擎主要的构成组件:Docker客户端、Docker守护进程(Docker daemon)、containerd和runc,它们共同负责容器的创建和运行。
1.2.1 用Libcontainer替代LXC
- LXC是基于Linux的,对于跨平台的Docker来说是隐患;其次是核心组件依赖外部有风险。
在 Docker 0.9 版本中,Libcontainer 取代 LXC 成为默认的执行驱动。Libcontainer 的目标是成为与平台无关的工具,可基于不同内核为 Docker 上层提供必要的容器交互功能。
1.2.2 摒弃大而全的Docker daemon
大而全的Docker daemon带了难于变更、运行越来越慢等问题,于是对其进行拆解并模块化,用小而专的工具来实现它。
- 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的职责:
当执行如上命令时,Docker客户端会将其转换为合适的API格式,发送到正确的API端点。
- daemon接收到创建容器的命令,会向containerd发出调用(daemon使用CRUD风格的API,通过gRPC与containerd进行通信)。
- containerd将Docker镜像转为OCI bundle,并让runc基于此创建一个新的容器。
- runc与操作系统内核接口进行通信,基于必要的工具(Namespace、GGroup等)来创建容器。容器进程作为runc的字进程启动,启动完毕后runc退出。