1、docker引擎简介
- Docker引擎是用来运行和管理容器的核心软件。
- Docker引擎主要的组构成:Docker户端(Docker Client)、Docker守护进程(Docker daemon)、containerd以及runc。它们共同负责容器的创建和运行。
2、早期的docker引擎
- 早期的Docker引擎由两个核心组件构成:LXC和Docker daemon。
- Docker daemon是单一的二进制文件,包含诸如Docker客户端、Docker API、容器运行时、镜像构建等。
- LXC提供了对诸如命名空间(Namespace)和控制组(CGroup)等基础工具的操作能力,它们是基于Linux内核的容器虚拟化技术。
- 早期docker引擎架构
![](https://img2020.cnblogs.com/blog/2270526/202109/2270526-20210901003830877-1309162162.png)
3、现在的docker引擎
![](https://img2020.cnblogs.com/blog/2270526/202109/2270526-20210901004833228-141573784.gif)
1、现在的docker引擎形成诱因
1、去LXC
- 为什么要去LXC?
- 一是LXC是基于Linux的。这对于一个立志于跨平台的项目来说是个问题。
- 二是如此核心的组件依赖于外部工具,这会给项目带来巨大风险,甚至影响其发展。
- Docker公司开发了名为Libcontainer的自研工具,用于替代LXC。Libcontainer的目标是成为与平台无关的工具,可基于不同内核为Docker上层提供必要的容器交互功能。
- 在Docker0.9版本中,Libcontainer取代LXC成为默认的执行驱动。
2、模块化docker引擎
- 为什么模块化大而全的docker引擎
- 难于变更。
- 运行越来越慢。
- 这并非生态(或Docker公司)所期望的。
- 拆解大而全的Docker daemon进程,并将其模块化。这项任务的目标是尽可能拆解出其中的功能特性,并用小而专的工具来实现这些功能特性。
- 这些小工具可以是可替换的,也可以被第三方拿去用于构建其他工具。
- 遵循了在UNIX中得以实践并验证过的一种软件哲学:小而专的工具可以组装为大型工具。
3、docker生态
- 许多Docker内置的组件都可以替换为第三方的组件。
- 网络技术栈就是,一个很好的例子。Docker核心产品内置有网络解决方案。但是网络技术栈是可插拔的,这意味着Docker内置的网络方案可以被替换为第三方的方案。许多人都会这样使用。
4、开放容器计划
- OCI(The Open Container Initiative,开放容器计划)是一个旨在对容器基础架构中的基础组件进行标准化的管理委员会。
- OCI已经发布了两份规范(标准):镜像规范和运行时规范。
2、现在的docker引擎的组件
1、docker daemon
- daemon目前的主要功能包括镜像管理、镜像构建、RESTAPI、身份验证、安全、核心网络以及编排。
2、containerd
- containerd(发音为container-dee)的主要任务是容器的生命周期管理--start | stop | pause | rm...
- Docker引擎技术栈中,containerd位于daemon和runc所在的OCI层之间。Kubernetes也可以通过cri-containerd使用containerd。
- containerd在Linux(1.11版本之后)和Windows上以daemon的方式运行。
- 最初containerd仅用于容器的生命周期管理。现在它被赋予了更多的功能,比如镜像管理。但所有的额外功能都是模块化的、可选的。其原因之一是方便在其他项目中使用它,例如在Kubernetes中,containerd能够完成一些诸如push和pull镜像的操作。
3、runc
- runc是OCI容器运行时规范的参考实现。有时也将runc所在的那一层称为"OCI层”
- runc是一个轻量级的、对Libcontainer进行了包装的命令行交互工具(Libcontainer取代了早期Docker架构中的LXC)。
- runc是一个CLI包装器,实质上就是一个独立的容器运行时工具。
4、shim
- shim是实现无daemon(守护进程)的容器不可或缺的工具。
- 每次创建容器时,containerd都会启动一个新的runc进程。一旦容器创建完毕,对应的runc进程就会退出。容器进程的父进程runc退出后,相关联的containerd-shim进程就会成为容器进程的父进程。
- shim的部分功能如下:
- 保持所有STDIN和STDOUT流是开启状态,从而当daemon重启的时候,容器不会因为管道(pipe )的关闭而终止。
- 将容器的退出状态反馈给daemon
4、现在的docker引擎启动容器
1、启动容器的过程
docker container run --name ctrl -it alpine:latest sh
- 启动容器过程
- (1)docker客户端工具发送启动容器的命令给docker daemon。
- (2)docker daemon的API接收到命令后,调用containerd。(docker daemon已经不包含任何创建容器的代码)
- (3)containerd将Docker镜像转换为OCI bundle后调用runc,并指挥runc基于此创建容器。(containerd并不创建容器)
- (4)runc与操作系统内核接口进行通信,基于所有必要的工具(Namespace, CGroup等)来创建容器。容器进程作为runc的子进程启动,容器启动完毕后,runc将会退出。
- 启动容器过程的示意图
![](https://img2020.cnblogs.com/blog/2270526/202109/2270526-20210901012739305-1564585052.gif)
2、此模型的显著优势
- 将所有的用于启动、管理容器的逻辑和代码从daemon中移除,意味着容器运行时与Dockerdaemon是解耦的,有时称之为“无守护进程的容器( daemonless container )"。
- 对Docker daemon的维护和升级工作不会影响到运行中的容器。
- 在旧模型中,所有容器运行时的逻辑都在daemon中实现,启动和停止daemon会导致宿主机上所有运行中的容器被杀掉。