Docker引擎架构
Docker引擎架构
1. Docker引擎的发展
1.1 Docker引擎首次发布时
Docker首次发布时,Docker引擎由两个核心组件组成:LXC和Docker daemon
Docker daemon是单一的二进制文件,包含诸如Docker客户端、Docker API、容器运行时、镜像构建等。
LXC提供对诸如命名空间(Namespace)和控制组(CGroup)等基础工具的操作能力,他是基于Linux内核的容器虚拟化技术。
1.2 首次发布Docker引擎存在的问题
首先,LXC是基于Linux的。这对于一个立志于跨平台的项目来说是个问题。
其次,如此核心的组件依赖于外部工具,这会给项目带来巨大的风险,甚至影响其发展。
因此,Docker公司开发了名为Libcontainer的自研工具,用于替代LXC。Libcontainer的目标是成为平台无关的工具,可基于不同的内核为Docker上层提供必要的容器交互功能。
1.3 现有的Docker引擎
runc:基于OCI实现,只有一个作用--创建容器
containerd:对Docker daemon进行拆解后,所有容器执行逻辑被重构到一个新的名为containerd(container-dee)的工具中。它的主要任务是容器的生命周期管理---start | stop | pause | rm ...
docker daemon:主要功能包括镜像管理、镜像构建、REST API、身份验证、安全、核心网络以及编排,不再包含任何创建容器的代码。
shim:runc创建容器完毕,对应的runc进程就会退出,相关联的containerd-shim进程就会成为容器的父进程。shim是实现无daemon的容器不可或缺的工具。
1.4 容器启动过程
Docker client向Docker daemon发送创建容器的命令,Docker daemon一旦接收到创建新容器的命令,它就会向containerd发出调用,虽然名叫containerd,但是它并不负责创建容器,二十指挥runc去做。containerd将Docker镜像转换为OCI bundle,并让runc基于此创建一个新的容器。
runc与操作系统内核接口进行通信,基于所有必要工具(Namespace、CGroup等)来创建容器。容器进程作为runc的子进程,启动完毕后,runc将会退出。相关联的containerd-shim进程成为容器的父进程。
该模型的优势:
将所有用于启动、管理容器的逻辑核代码从daemon中移除,意味着容器运行时与Docker daemon是解耦的,有时称之为“无守护进程的容器(daemonless container)”,如此,对Docker daemon的维护和升级工作不会影响到运行中的容器。
1.5 关于OCI
OCI(open container Initiative)开放容器倡议,定义了两个规范:
- 镜像规范
- 容器运行时规范
2. 关于k8s 1.24版本弃用dockershim的问题
k8s的CRI(Container Runtime Interface)与docker引擎的API并不兼容,为了支持Docker,在kubelet和Docker之间增加了一个适配器(dockershim)。
在k8s 1.24版本完全弃用了dockershim,kubelet直接与containerd通信,管理容器的生命周期,去掉了 dockershim 和 Docker Engine 这两个环节,更加简洁明了,性能更好。
弃用dockershim对K8s和Docker的影响不大,因为docker引擎也是通过containerd进行容器生命周期管理,原有的Docker镜像和容器仍然可以正常运行。唯一的变化是K8s绕过了docker,直接调用containerd。
K8s直接使用containerd来操作容器,那么它就是一个独立于Docker的工作环境,两者都无法访问对方管理的容器和镜像。换句话说,使用docker ps
命令将不会看到K8s中运行的容器。
由于容器镜像格式已经标准化(OCI规范,Open Container Initiative),Docker镜像在K8s中仍然可以正常使用,不需要改变原有的开发测试和CI/CD流程。我们仍然可以拉取 Docker Hub,或者编写一个 Dockerfile 来打包应用程序。
3. 参考
《深入浅出Docker》第五章