Docker 并不是凭空创造出来的虚拟化技术,它本质上是对 Linux 内核已有特性的巧妙封装和易用化改造。理解 Docker 的架构,以及它到底依赖内核的哪些能力,是真正用好容器、排查线上问题的基础。
1. Docker 的整体架构
Docker 采用经典的客户端-服务端(C/S)架构,核心由三部分组成:
- Docker 客户端(docker CLI):用户在终端里敲的
docker run、docker build等命令,本质上是通过 REST API 向 Docker 守护进程发送请求。客户端本身不执行任何容器操作,只是一个命令行的“遥控器”。 - Docker 守护进程(dockerd):负责接收客户端指令,管理镜像、容器、网络、存储卷等所有对象。它是真正干活的后台服务,通常以 root 权限运行。
- 容器运行时与镜像仓库:早期 Docker 自带一个名为
containerd的高级运行时来实际创建和运行容器,底层则调用runc等 OCI 兼容运行时与内核交互。镜像仓库(如 Docker Hub)则负责存储和分发容器镜像。
一个 docker run nginx 指令的幕后流程大致是:客户端将请求发给 dockerd → dockerd 检查本地是否有 nginx 镜像,没有就从仓库拉取 → 调用 containerd 创建容器 → containerd 通过 runc 启动一个与宿主机共享内核但资源隔离的进程 → 容器就跑起来了。
2. 容器不是轻量级虚拟机
很多人用“轻量级虚拟机”来形容容器,这在直觉上没问题,实现上却有本质差异。虚拟机通过 Hypervisor 模拟完整硬件,每个虚拟机都运行一个独立的操作系统内核;而容器里的进程直接运行在宿主机的 Linux 内核之上,只是被内核机制“隔离”在一个受限的视图中。因此,容器启动不需要引导一个独立内核,几秒钟就能就绪,内存和 CPU 开销也远小于虚拟机。
这一特性决定了容器只能运行与宿主机 相同内核 的系统。即,在 Linux 宿主机上无法直接运行为 Windows 编译的容器,反之亦然(除非借助虚拟机)。但也正因如此,Linux 容器才能做到文件和进程级别的精细共享。
3. 容器的三大内核基石
Docker 隔离容器的能力,主要来自 Linux 内核的三个关键特性:命名空间、控制组和联合文件系统。
- 命名空间(namespace):实现“视野隔离”
内核提供了多种命名空间类型,每种负责隔离一类系统资源,让容器里的进程感觉自己独享整个系统:
- PID 命名空间:容器内进程的 PID 从 1 开始,看不到宿主机其他进程。
- 网络命名空间:容器拥有独立的虚拟网卡、IP 地址、路由表、防火墙规则,就像一台独立的网络设备。
- 挂载命名空间:容器看到的是自己独立的文件系统根目录,无法直接访问宿主机的
/etc、/proc等。 - UTS 命名空间:容器可以有自己的主机名和域名。
- IPC 命名空间:隔离进程间通信资源(如信号量、消息队列)。
- 用户命名空间:将容器内的 root 用户映射为宿主机上一个普通用户,增强安全性。
打一个比方:命名空间就像给每个容器分配了一间毛坯房,房里的人以为这就是全世界,实际上整栋楼共享同一块地基和供水供电系统。
- 控制组(cgroup):实现“资源限制”
命名空间只解决了“能看到什么”的问题,但每个容器能“用多少”宿主机资源,要靠 cgroup 来管。cgroup 可以对进程组进行精确的 CPU 配额、内存上限、磁盘 I/O 优先级、网络带宽等资源的分配。例如:
docker run -m 512m --cpus=1.5 myapp
这条命令会让内核的 cgroup 子系统将容器的内存使用限制在 512 MB 以内,CPU 使用不超过 1.5 个核心。一旦容器试图突破限制,内核会直接触发 OOM(内存溢出杀死进程)或限制 CPU 使用。正是 cgroup 防止了某个容器“吵闹邻居”的问题,保障同一宿主机上其他容器的稳定。
- 联合文件系统(UnionFS):实现“镜像分层与快速启动”
Docker 镜像之所以能分层构建、共享基础层,依赖于内核支持的联合挂载技术,如 OverlayFS、AUFS 等。联合文件系统可以将多个目录(或镜像层)按顺序叠加成一个统一的文件系统视图。镜像内部的不同层(比如 Ubuntu 基础层、安装 Nginx 层、添加配置层)都保持只读,而容器启动时,Docker 在顶层添加一个可写层。当容器需要修改文件时,联合文件系统会用“写时复制”的策略将文件拷贝到可写层再修改,下层镜像保持不变。
这样做的好处非常实在:
- 共享基础镜像层(如 Ubuntu 20.04 的底层文件),宿主机上无论跑多少个 Ubuntu 容器,磁盘上只需存储一份该层数据;
- 镜像构建和推送效率极高,只需传输变更的层;
- 容器销毁后,顶层可写层一删了事,不留垃圾。
4. 安全边界依赖于内核的正确配置
容器属于“进程级隔离”,与虚拟机的硬件级强隔离相比,内核攻击面相对更大。因此,生产环境中的 Docker 通常还会配合 Linux 内核的安全模块来加固:
- Capabilities:精细化控制进程权限,容器通常放弃
SYS_ADMIN、NET_RAW等危险能力。 - Seccomp:限制进程可调用的系统调用种类,降低内核漏洞被利用的风险。
- SELinux / AppArmor:为容器进程打上安全标签,限制其对文件和资源的不当访问。
总结
Docker 的成功,很大程度是因为它站在了 Linux 内核这些成熟特性的肩膀上。它用命名空间做出隔离的“房间”,用 cgroup 为房间分配水电配额,再用联合文件系统使得装修(构建镜像)和入住(启动容器)变得极快。理解这层依赖关系后,当容器出现权限异常、资源耗尽或文件系统损坏等问题时,你就不只是机械地用 Docker 命令重启,而是能深入到内核层去查看命名空间隔离是否生效、cgroup 限制是否被触发、存储驱动是否健康——这才是容器运维的“内功”。