单体应用拆分成微服务后,确实能够实现快速开发迭代。但硬币的另一面是,因为拆解出来的小服务太多,反而导致了测试和部署的复杂度与成本直线上升。
想想看,在单体应用时代,一个 Spring Boot 工程,打包成一个 war 包,部署到 Linux 服务器的 Tomcat 里就完事了。而微服务架构下,哪怕只是修改一个业务需求,都可能牵扯到好几个微服务模块。这就意味着,所有被波及的代码都需要重新走一遍测试、打包、部署、上线的流程。对于现场运维的同事来说,这无疑是成倍增加的工作压力。
另一个现实问题是环境。微服务通常会在公有云上通过创建 ECS 实例进行扩容。但一个新的 ECS 实例,往往只包含最基本的操作系统,并没有运行 Java 程序所需的 JDK 环境。于是你不得不手动安装 JDK,更头疼的是,不同服务依赖的 JDK 版本还可能不一致。虽然多数团队会统一采用 JDK 8,但难免会遇到一些偏爱前沿技术的项目经理,想要尝试 JDK 21 这类长期稳定的大版本。

而容器技术,恰好就是为解决上述两个核心痛点(代码部署难、环境依赖不一致)而生的。时下最火的容器技术当属 Docker。许多开发者用了好几年,可能只知道它能将应用和依赖打包,然后发布到任何 Linux 服务器上运行,实现 “一次构建,到处运行” 。至于它内部究竟是如何工作的,与传统虚拟化有何不同,可能就知之甚少了。
一、深入理解 Docker 架构
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux 或 Windows 操作系统的机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。
简单来说,Docker 是对 Linux 容器技术的一种封装,提供了更简单易用的接口。它是目前最流行的 Linux 容器解决方案。
一个完整的 Docker 体系主要由以下七个核心部分组成:
- Docker Client 客户端:用户与 Docker 交互的入口。
- Docker Daemon 守护进程:提供 Docker Server,用于接收 Docker Client 的请求。Docker Server 通过路由(Router)与分发调度,找到相应的 Handler 来执行请求。
- Docker Image 镜像:包含 Distribution(分发)、Layer(层)、Image(镜像)、Registry(注册中心)、Reference(引用)。
- Docker Driver 驱动:通过与 Docker 守护进程交互,实现对 Docker 容器执行环境的定制和管理。
- Docker Graph 内部数据库:一种用于记录和跟踪 Docker 镜像和容器之间关系的数据结构。
- Libcontainer 函数库:Docker 的底层容器管理库,提供接口和函数用于创建和管理容器。它直接与内核交互,负责容器的命名空间、cgroup、网络设备等底层操作。
- Docker Container 容器:通过 Driver 和 Libcontainer 共同协作创建出来的容器实例。

二、Docker 架构工作流程详解
结合上图,我们可以梳理出 Docker 从接收命令到创建容器的完整工作流程:
- 用户使用 Docker Client 与 Docker Daemon 建立通信,并发送请求。客户端和守护进程之间通过 REST API 进行通信,可以使用 UNIX 套接字或网络接口。
- Docker Daemon 接收到请求后,其内部的 Docker Server 通过路由和分发,找到相应的 Handler 来处理。Docker Daemon 的核心是 Docker Engine,Engine 中的 Job 是最基本的工作执行单元,Docker 的每一项工作(如运行进程、创建容器)都被抽象为一个 Job。
- 当请求涉及镜像时(例如
docker run),流程会进入 Docker Image 模块。如果需要从远端获取镜像,则会与 Registry(如 Docker Hub)交互。
- 下载的镜像通过镜像管理驱动 graphdriver 以 Graph 的形式存储到本地。
- Docker Graph 作为内部数据库,记录了镜像的元数据和层级关系。一个 Registry 包含多个 Repository(仓库),一个 Repository 包含多个同类型但不同标签(Tag)的 Image。
- 容器的根文件系统(rootfs)由内核挂载为“只读”模式,然后通过“联合挂载”技术额外挂载一个“可写”层。
- 通过 networkdriver 完成 Docker 容器网络环境的配置,例如创建 Docker 网桥。
- execdriver 作为容器的执行驱动,负责创建容器运行命名空间,管理资源使用统计与限制,并真正启动容器内的进程。
- Libcontainer 是上述驱动的底层实现,它直接调用内核功能,完成命名空间隔离、cgroups 资源限制等操作。
- 最终,由 Driver 和 Libcontainer 共同协作,创建出可运行的 Docker Container 实例,实现服务交付。

1、Docker Client 客户端
Docker Client 是用户与 Docker 交互的主要命令行工具(CLI)。它的主要职责包括:
- 提供交互界面:让用户通过命令管理容器和镜像。
- 容器与镜像管理:执行创建、启动、停止、删除、查看等操作。
- 资源配置:允许用户为容器配置 CPU、内存等资源。
- 网络通信:作为客户端,与后端的 Docker Daemon 通信。
2、Docker Daemon 守护进程
Docker Daemon 是运行在宿主机上的后台服务,是 Docker 的核心。其主要作用包括:
- 管理容器生命周期:响应客户端请求,负责容器的创建、运行、监控和销毁。
- 镜像管理:处理镜像的拉取、构建、存储和删除。
- 管理网络与存储:配置容器网络、管理数据卷等。
3、镜像(Image)的构成要素
在 Docker 镜像体系中,有几个关键概念:
- Distribution(分发):指镜像的传输方式,例如从 Registry 下载或通过文件导入导出。
- Layer(层):镜像是分层构建的,每一层都是只读文件系统。这种设计使得镜像层可以被复用,极大提升了存储和传输效率。
- Image(镜像):一个只读的模板,包含了运行应用所需的文件系统、依赖和配置。
- Registry(注册中心):集中存储和分发镜像的服务,如 Docker Hub、阿里云镜像仓库等。
- Reference(引用):标识特定镜像的方式,通常由仓库名、标签或镜像 ID 组成。

三、Docker run 的流程与传统虚拟化对比
1、docker run 的完整流程
当你执行 docker run hello-world 时,背后发生了一系列事情:

- Docker 客户端将
run 命令发送给守护进程。
- 守护进程首先在本地查找名为
hello-world:latest 的镜像。
- 如果本地没有,则去配置的 Registry(默认 Docker Hub)查找并下载。
- 下载成功后,以该镜像为模板,创建并启动一个新的容器。
- 容器执行预设的命令,输出结果,并通过守护进程流回客户端,显示在你的终端上。
2、与传统虚拟化方式对比
这是 Docker 性能远优于传统虚拟机(VM)的根本原因。两者架构对比如下:

核心差异:
- 抽象层:Docker 比虚拟机少了 Guest OS 这一层。它利用的是宿主机的内核,而每个 VM 都需要一个完整的客户机操作系统。
- 性能与资源:更少的抽象层意味着更少的资源开销和更快的启动速度。Docker 容器是进程级别的隔离,而 VM 是操作系统级别的隔离。
- 体积:Docker 镜像只包含应用及其依赖,体积通常以 MB 计;而 VM 镜像包含整个 OS,体积往往以 GB 计。
这种轻量级特性,使得 Docker 在实现 微服务 的快速部署和弹性伸缩方面具有天然优势。
四、Docker 安装实战(以 CentOS 为例)
1、卸载旧版本(如有)
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
2、安装必要工具包
yum install -y yum-utils
3、设置稳定的镜像仓库
推荐使用国内镜像源以加速下载:
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
更新 YUM 软件包索引:
yum makecache fast
4、安装 Docker 引擎
Docker CE 是社区免费版,EE 是企业版。这里安装 CE 版:
yum install docker-ce docker-ce-cli containerd.io

5、启动 Docker 服务
systemctl start docker
6、验证安装
使用 docker version 命令检查客户端和服务器端版本,确认安装成功。

7、运行测试容器
执行著名的 hello-world 镜像,这将拉取镜像并运行一个测试容器,输出欢迎信息。
docker run hello-world

如果看到 “Hello from Docker!”,恭喜你,Docker 已经安装并运行正常。
五、Docker 常用命令速查表
为了方便日常使用,以下汇总了 Docker 的核心命令及其简要说明:
| 常用命令 |
命令含义 |
| attach |
连接到正在运行容器的标准输入/输出 |
| build |
通过 Dockerfile 构建镜像 |
| commit |
将容器的更改提交为新的镜像 |
| cp |
在容器和宿主机之间拷贝文件 |
| create |
创建一个新容器但不启动 |
| diff |
检查容器文件系统的更改 |
| events |
获取服务器的实时事件 |
| exec |
在运行中的容器内执行命令 |
| export |
将容器文件系统导出为 tar 包 |
| history |
显示镜像的历史记录 |
| images |
列出本地镜像 |
| import |
从 tar 包导入内容创建镜像 |
| info |
显示系统范围的信息 |
| inspect |
获取容器/镜像的详细信息 |
| kill |
强制停止一个运行中的容器 |
| load |
从 tar 包或 STDIN 加载镜像 |
| login |
登录到 Docker 仓库 |
| logout |
从 Docker 仓库登出 |
| logs |
获取容器的日志 |
| pause |
暂停容器中的所有进程 |
| port |
列出容器的端口映射 |
| ps |
列出容器 |
| pull |
从仓库拉取镜像或仓库 |
| push |
推送镜像或仓库到仓库 |
| rename |
重命名容器 |
| restart |
重启容器 |
| rm |
删除一个或多个容器 |
| rmi |
删除一个或多个镜像 |
| run |
创建并运行一个新容器 |
| save |
将镜像保存为 tar 包 |
| search |
在 Docker Hub 中搜索镜像 |
| start |
启动一个或多个已停止的容器 |
| stats |
动态显示容器资源使用统计 |
| stop |
停止一个运行中的容器 |
| tag |
为镜像创建标签 |
| top |
显示容器内运行的进程 |
| unpause |
恢复暂停的容器 |
| version |
显示 Docker 版本信息 |
| wait |
阻塞直到容器停止,然后打印退出代码 |
理解 Docker 的原理,能帮助我们在日常开发和运维中更好地使用它,定位问题,并设计出更合理的容器化方案。从单体应用到微服务,再到容器化部署,是现代软件交付演进的重要路径。希望这篇对 Docker 架构和原理的剖析,能让你在“会用”的基础上,更进一步地“懂它”。
如果你想与其他开发者交流更多关于容器化、运维自动化 的经验,欢迎来 云栈社区 一起探讨。