Docker 底层原理:Namespaces、Cgroups 与分层文件系统解析
DEV Community 技术博客发表文章,由 Michael Wolfenberger 撰写,深入解析 Docker 的底层工作原理。文章首先澄清了一个常见的误解:许多工程师认为 Docker 像轻量级虚拟机一样运行,但实际上并非如此。虚拟机通过 hypervisor 虚拟化物理硬件(CPU、内存控制器、网络适配器),在模拟硬件中运行完整的客户操作系统(包括独立的内核、init 系统、设备驱动和系统后台服务);而 Docker 容器直接运行在宿主 Linux 内核上,只是内核通过 Namespaces(命名空间)和 Cgroups(控制组)限制了进程能看到的、能消耗的和能修改的内容。本文基于该技术文章,系统解读 Docker 容器技术的核心原理。
背景:容器与虚拟机的本质区别
虚拟机的架构
虚拟机(Virtual Machine)的架构:
- Hypervisor 层:虚拟化物理硬件(CPU、内存控制器、网络适配器)
- 客户操作系统:在模拟硬件中运行完整的操作系统(独立内核、init 系统、设备驱动、后台服务)
- 应用层:运行在客户操作系统中
虚拟机特点:
- 每个虚拟机有完整独立的操作系统
- 内核级隔离
- 资源开销大(每个虚拟机都需要完整的 OS)
- 启动速度慢(需要启动完整操作系统)
- 隔离性强
容器的架构
容器(Container)的架构:
- 宿主 Linux 内核:容器直接运行在宿主内核上
- Root 文件系统:容器有自己的根文件系统,但没有独立内核
- Namespaces + Cgroups:内核通过它们限制容器的可见性、资源使用和文件修改
- 应用层:运行在根文件系统中
容器特点:
- 所有容器共享宿主内核
- 进程级隔离(不是内核级)
- 资源开销小
- 启动速度快(秒级)
- 隔离性弱于虚拟机
容器不是轻量级虚拟机
关键澄清:
- 当你运行
docker run -d nginx时,Nginx worker 是宿主机器进程树中一个普通进程 - 区别在于内核限制了该进程能看到的、能消耗的、能修改的内容
- 容器没有自己的内核
- 容器与宿主机共享内核
- 容器是"受限的进程",不是"轻量的虚拟机"
三大核心技术
1. 限制可见性:Linux Namespaces
什么是 Namespaces
Linux Namespaces 是内核提供的进程隔离机制,让进程拥有独立的系统资源视图。每个 Namespace 提供一种资源的隔离视图。
Namespaces 的类型
| Namespace | 隔离内容 | 容器中的作用 |
|---|---|---|
| PID Namespace | 进程 ID | 容器内 PID 从 1 开始,看不到宿主机其他进程 |
| Network Namespace | 网络栈 | 每个容器有自己的网络接口、IP、路由表 |
| Mount Namespace | 文件系统挂载点 | 容器有独立的文件系统视图 |
| UTS Namespace | 主机名和域名 | 容器有自己的主机名 |
| IPC Namespace | 进程间通信 | 容器有独立的 IPC 资源 |
| User Namespace | 用户和组 ID | 容器内的用户映射到宿主机的非特权用户 |
PID Namespace
- 容器内的第一个进程 PID 为 1
- 容器进程看不到宿主机和其他容器的进程
- 就像每个容器是一个独立的"系统"
- 进程隔离的基础
Network Namespace
- 每个容器有独立的网络接口
- 容器有自己的 IP 地址
- 容器有自己的路由表和防火墙规则
- 容器间网络通过 bridge 等机制互联
- 网络隔离和端口映射的基础
Mount Namespace
- 每个容器有独立的文件系统挂载视图
- 容器看到的根文件系统是镜像层叠加的结果
- 宿主机的挂载点对容器不可见(除非显式挂载)
- 文件系统隔离的基础
User Namespace
- 容器内的 root 用户映射为宿主机的非特权用户
- 容器内的高权限操作在宿主机上是低权限的
- 增强安全性
- 降低容器逃逸的风险
2. 限制资源消耗:Cgroups
什么是 Cgroups
Cgroups(Control Groups)是 Linux 内核提供的资源限制和监控机制,用于限制、记录和隔离进程组的资源使用。
Cgroups 的功能
- CPU 限制:限制进程组的 CPU 使用率
- 内存限制:限制进程组的内存使用量
- 磁盘 I/O 限制:限制进程组的磁盘读写速度
- 网络带宽限制:限制进程组的网络带宽
- 进程数限制:限制进程组可以创建的进程数
- 资源监控:监控进程组的资源使用情况
Cgroups 在容器中的应用
Docker 使用 Cgroups 实现资源限制:
--cpus:限制容器的 CPU 使用--memory:限制容器的内存使用--memory-swap:限制容器的交换内存--blkio-weight:限制容器的磁盘 I/O--pids-limit:限制容器的进程数
示例:
# 限制容器最多使用 1.5 个 CPU 核心
docker run --cpus=1.5 myapp
# 限制容器最多使用 512MB 内存
docker run --memory=512m myapp
# 同时限制 CPU 和内存
docker run --cpus=2 --memory=1g myapp
Cgroups 的意义
- 防止单个容器耗尽所有资源
- 保证多容器公平分配资源
- 防止"吵闹的邻居"问题
- 资源使用可预测
- 提升整体稳定性
3. 限制文件修改:分层文件系统
什么是分层文件系统
Docker 镜像由多层(Layer)组成,每层代表镜像构建过程中的一个步骤。容器运行时的文件系统是多层叠加的结果。
镜像分层
Dockerfile 的每个指令生成一层:
FROM ubuntu:24.04 # 基础层
RUN apt-get update # 层1:更新软件源
RUN apt-get install -y nginx # 层2:安装 nginx
COPY app.conf /etc/nginx/ # 层3:复制配置
EXPOSE 80 # 元数据(不产生层)
CMD ["nginx", "-g", "daemon off;"] # 元数据(不产生层)
每层的特点:
- 只读层:镜像层都是只读的
- 不可变性:镜像层一旦创建不可修改
- 共享性:相同层可以跨镜像共享
- 缓存性:构建时未变化的层可以复用缓存
写时复制(Copy-on-Write)
容器运行时在镜像层之上添加一个可写层:
- 容器对文件的修改写入可写层
- 读取文件时按层查找,找到第一个匹配的层
- 修改文件时先复制到可写层再修改(Copy-on-Write)
- 删除文件时在可写层添加删除标记(whiteout)
- 容器删除后可写层销毁,镜像不受影响
OverlayFS
Docker 默认使用 OverlayFS 实现分层:
- Lowerdir:镜像层(只读)
- Upperdir:容器可写层
- Merged:合并后的视图(容器看到的文件系统)
优点:
- 高效的层合并
- 按需复制
- 共享底层数据
- 快速的容器创建
分层文件系统的意义
- 镜像可以复用和共享
- 镜像体积优化(只存差异)
- 容器启动快速(不需要复制整个文件系统)
- 容器间隔离(各自的可写层独立)
- 镜像分发高效(只传输差异层)
容器完整生命周期
容器启动过程
docker run -d nginx
- 拉取镜像(如果本地没有):从 registry 拉取镜像层
- 创建容器:创建容器的配置和状态
- 创建 Namespaces:为容器创建独立的 PID、Network、Mount、UTS、IPC、User Namespaces
- 配置 Cgroups:创建容器的 Cgroup,设置资源限制
- 准备文件系统:使用 OverlayFS 合并镜像层和可写层
- 运行进程:在 Namespaces 和 Cgroups 的约束下启动容器进程
- 进程 PID 1:容器内第一个进程成为 PID 1(init 进程)
- 网络配置:为容器分配 IP 和网络接口
容器运行状态
容器运行时:
- 进程运行在宿主内核上
- 受到 Namespaces 的可见性限制
- 受到 Cgroups 的资源限制
- 文件系统是镜像层 + 可写层的叠加
- 可以通过
docker exec进入容器 - 可以通过
docker logs查看日志
容器停止和清理
docker stop nginx
docker rm nginx
- 停止容器:发送 SIGTERM/SIGKILL 给容器进程
- 删除容器:销毁可写层和容器配置
- 镜像保留:镜像层不受影响
- 数据持久化:挂载的卷(Volume)保留
安全考量
容器的安全边界
容器提供的是进程级隔离,不是安全边界:
- 共享内核意味着内核漏洞可能影响所有容器
- 需要额外安全机制(如 gVisor、Kata Containers)实现更强的隔离
- User Namespaces 可以降低权限提升风险
- Seccomp 可以限制系统调用
- AppArmor/SELinux 可以强制访问控制
安全最佳实践
- 使用非 root 用户运行容器
- 最小化镜像体积(减少攻击面)
- 只暴露必要的端口
- 使用只读文件系统(--read-only)
- 使用 User Namespaces 映射
- 启用 Seccomp 和 AppArmor
- 定期更新基础镜像
- 扫描镜像漏洞
总结
Docker 容器技术的核心是三个 Linux 内核机制:Namespaces(命名空间)、Cgroups(控制组)和分层文件系统。Namespaces 限制容器进程能看到的资源,提供 PID、Network、Mount、UTS、IPC、User 等六种隔离视图,让每个容器看起来像独立的系统;Cgroups 限制容器能消耗的资源,提供 CPU、内存、磁盘 I/O、网络带宽、进程数等限制,防止单个容器耗尽所有资源;分层文件系统限制容器能修改的文件,通过镜像分层和写时复制机制,让容器可以共享只读镜像层,在可写层进行修改,容器删除后镜像不受影响。这三者协同工作,实现了容器"轻量、快速、隔离"的特性,与虚拟机(完整客户 OS + Hypervisor)形成鲜明对比。理解这些底层原理对于正确使用 Docker 至关重要:可以帮助排查容器网络问题(Network Namespace)、优化资源分配(Cgroups)、理解镜像体积优化(分层文件系统)、设计安全策略(Namespaces 和内核安全机制)。同时,容器并非安全边界,共享内核的特性决定了需要额外的安全机制来加强隔离。随着云原生技术的普及,深入理解容器底层原理已经成为现代开发者和运维工程师的基本功。
来源:https://dev.to/michaelwolfenberger/docker-under-the-hood-namespaces-cgroups-and-layered-filesystems-explained-1414