在这一节,我们将一起揭开Docker引擎的神秘面纱。也许你会想,我真的需要了解这些底层的东西吗?当然,即使不了解引擎的内部构造,你也能顺利使用Docker。 但是,要想真正成为一名Docker高手,洞悉其底层原理是必不可少的。这就好比开车,你不需要成为一名机械工程师也能驾驶汽车,但了解引擎的工作原理能让你更好地驾驭它,并在出现问题时从容应对。
本部分将以纯理论的形式展开,没有动手练习,但相信我,这些知识会让你对Docker的理解提升一个全新的层次。

Docker引擎(Docker Engine)是一个开源的容器化平台核心组件,它负责接收和处理用户的Docker命令,实现镜像的构建、分发、容器的创建与运行等功能。
其本质是一个客户端-服务器架构,由后端守护进程(常为 dockerd)、与之通信的API以及前端命令行工具(Docker CLI)共同组成。
现代Docker引擎采用高度模块化的架构,各核心组件分工明确、协同运作,提升了系统的可维护性与可扩展性。主要包括容器生命周期管理的dockerd、负责容器管理及运行任务的containerd,以及直接负责容器生成和运行的低层运行时runc。
各部分通过标准接口(如gRPC、OCI规范)解耦,有效支撑了Docker在性能、兼容性和安全性方面的业界领先地位。
Docker并非一开始就拥有现在这样精巧的结构。它的架构经历了一场深刻的演变,从一个什么都干的“大单间”演变成了一套分工明确的“模块化公寓”。
在最初的版本中,Docker引擎的核心——Docker Daemon(守护进程),是一个庞大而笨重的“大单间”。它几乎包含了所有的功能:从API接口,到镜像构建,再到容器的运行和管理,所有事情都由它一肩挑。 它依赖一个叫做LXC的Linux工具来与操作系统内核打交道,从而创建容器。
这种设计很快就暴露出了问题。首先,LXC是Linux独有的,这给Docker实现跨平台的目标带来了巨大障碍。 其次,所有功能都耦合在一个巨大的程序里,导致创新困难、运行缓慢,就像一个臃肿的机构,难以快速响应变化。
为了解决这些问题,Docker公司开启了一项宏大的重构计划,目标就是将这个庞大的守护进程拆分,把不同的功能模块化。 这个思路遵循了经典的Unix哲学:构建小而美的专业工具,然后将它们组合起来,共同完成复杂的任务。
这场重构的核心成果,就是将容器的运行和管理代码完全从主守护进程中剥离出来,形成了一系列独立的、标准化的组件。 这个过程也恰逢开放容器倡议(OCI)正在制定行业标准,Docker积极参与其中,并让自己的新架构完全符合OCI标准。

如今的Docker引擎,就像一套设计精良的“模块化公寓”,每个房间(组件)都有自己明确的用途,并通过标准的接口(gRPC API)进行通信。让我们来认识一下这几位核心成员。
dockerd:总指挥dockerd,也就是我们常说的Docker守护进程,是整个引擎的总指挥。它负责接收来自用户的指令(例如 docker run 命令),并理解这些指令的意图。
它掌管着Docker的API,管理着镜像、网络和存储卷等高级功能。然而,它现在已经不再亲自下场去创建容器了,而是把这个任务交给了更专业的手下。
containerd:项目经理当dockerd接到一个创建容器的指令后,它会把它转交给containerd。containerd就像一个项目经理,它的核心职责是管理容器的整个生命周期,包括启动、停止、暂停、删除等。
它不关心具体的“建造”细节,只负责监督和管理。它从dockerd那里接收任务,然后将Docker镜像转换成一个标准的OCI包,再交给runc去执行。
containerd最初由Docker开发,后来捐赠给了云原生计算基金会(CNCF),如今已成为一个独立且成熟的明星项目,在Kubernetes等许多其他项目中也扮演着重要角色。
runc:真正的建造者runc是真正干脏活累活的“建造者”。它是一个轻量级的命令行工具,是OCI容器运行时标准的参考实现。它的任务只有一个:创建容器。
containerd将准备好的OCI包交给runc后,runc就会与操作系统内核进行通信,利用内核提供的命名空间(namespaces)和控制组(cgroups)等功能,像搭积木一样把容器的隔离环境搭建起来,并启动容器内的第一个进程。
runc干完活就走,一旦容器进程启动,runc就会退出,不会常驻在内存中。
shim:守护神runc退出后,谁来照看新生的容器进程呢?这就是shim进程登场的时候了。containerd为每个运行的容器都会创建一个containerd-shim进程。这个shim进程会接替runc,成为容器进程的父进程,像一个忠诚的守护神。
它的主要职责是保持与容器的输入输出流的连接,并向containerd报告容器的状态,比如容器是否已经退出。
现在我们把所有组件串起来,看看当你输入docker run命令后,到底发生了什么。
docker run -it alpine sh。dockerd守护进程。dockerd(总指挥) 收到请求。它一看,是要创建一个容器。于是它调用containerd的API,说:“伙计,帮我启动一个基于alpine镜像的容器。”containerd(项目经理) 接到指令。它不会自己动手创建,而是将Docker镜像解压成一个符合OCI标准的“容器蓝图”(OCI bundle),然后召唤runc。runc(建造者) 拿到蓝图,开始与操作系统内核交互,创建出所有必要的隔离环境,然后启动容器内的sh进程。runc的任务完成,它随即退出。shim(守护神) 进程接管容器,负责监控容器状态,并将容器的输入输出流传回给你。sh提示符,一个容器就这样诞生了!模块化的巨大优势:守护进程的“热升级”
这种模块化架构带来一个巨大的好处,我们称之为“无守护进程的容器”(Daemonless Containers)。因为管理容器生命周期的containerd和容器进程本身,都与dockerd主进程是分离的。这意味着我们可以随时重启甚至升级dockerd,而不会影响到任何正在运行的容器!在过去那个“大单间”的架构里,重启Docker守护进程会导致主机上所有的容器全部死掉,这在生产环境中是绝对无法接受的。而现在,多亏了shim进程的守护,容器们可以安然无恙。
Docker引擎是一个由多个小型、专业化组件构成的精密系统。它通过dockerd提供丰富的用户接口,通过containerd管理容器生命周期,并通过runc与操作系统内核交互来创建容器。这种分层和模块化的架构,不仅遵循了开放的OCI标准,也使得Docker本身更加稳定、灵活和强大,能够从容应对生产环境的严苛挑战。理解了这套协作机制,你就掌握了Docker真正的核心。