一、用生活类比理解 overlay2 的本质
想象你有一叠透明的画画纸:
- 最下面一张是「基础画纸」(相当于 Docker 镜像),上面画着固定的图案(程序和系统文件)。
- 每次在上面盖一张新的「透明纸」(相当于容器的层),你可以在新纸上修改或添加图案,不会破坏下面的纸。
- overlay2 就是管理这叠纸的「文件夹」,让所有纸看起来像一张完整的画(容器运行时的文件系统)。
二、overlay2 的核心组成部分及作用
-
目录结构总览
/var/lib/docker/overlay2/ ├── l # 软链接文件夹,指向具体层的路径 ├── mnt # 容器挂载点,合并所有层的文件 ├── <hash1>/ # 镜像层(只读) │ ├── diff/ # 存储该层的文件变更 │ ├── link # 层的唯一标识 │ └── lower # 记录下层的路径 └── <hash2>/ # 容器层(可写) ├── diff/ # 存储容器运行时的修改 ├── link # 层的唯一标识 ├── lower # 记录所有下层镜像的路径 └── work # 临时工作目录,用于文件修改
-
关键部分详解
- 镜像层(只读层):
像图书馆的书,多个容器可以共享同一本书(镜像层),节省空间。 - 容器层(可写层):
像在书上贴便签,容器运行时的修改(如新建文件、修改内容)都存在这里,不影响原书。 - mnt 目录:
像「魔法桌面」,把所有层的文件合并显示,容器看到的就是这个合并后的文件系统。
- 镜像层(只读层):
三、overlay2 背后的工作原理(流程图)
┌──────────────────────────────────────────────────────────┐
│ 当启动容器时,overlay2 做了这些事: │
├───────────────────┬──────────────────────────────────────┤
│ 1. 找到镜像的所有只读层(如 A、B、C 层) │
│ ▼ │
│ 2. 创建一个新的可写层 D,用于存储容器的修改 │
│ ▼ │
│ 3. 在 mnt 目录中,用「联合文件系统」把 A+B+C+D 合并成一个 │
│ ▼ │
│ 4. 容器访问的就是这个合并后的文件系统,修改会存在 D 层 │
└──────────────────────────────────────────────────────────┘
联合文件系统原理:
就像叠透明纸,下面的层(镜像)是只读的,上面的层(容器)可以修改。当修改一个文件时:
- 先把原文件从下层「复制」到上层(称为「写时复制」)
- 在上层修改这个复制后的文件,下层文件保持不变
四、使用场景举例
-
多个容器共享同一镜像
- 场景:运行 10 个 Nginx 容器,每个容器共享同一个 Nginx 镜像层,只需要存储一次镜像文件。
- 好处:节省磁盘空间,像 10 个人看同一本漫画书,不用每人买一本。
-
容器快速创建和删除
- 场景:测试环境中频繁创建、删除容器(如 CI/CD 部署)。
- 原理:删除容器只需要删除可写层,镜像层不受影响,像撕掉便签,书还在。
-
数据持久化
- 场景:容器中数据库的数据需要保存,即使容器删除也不丢失。
- 做法:把数据目录挂载到宿主机(绕过 overlay2 的层机制),像把重要的便签单独贴在书桌抽屉里。
五、底层技术关键点(用代码比喻)
虽然 overlay2 是用 C 语言写的,但可以用 PHP 逻辑理解核心思想:
// 模拟 overlay2 的核心逻辑(非实际代码,仅用于理解)
class Overlay2 {
// 存储所有层的信息
private $layers = [];
// 添加一个镜像层(只读)
public function addImageLayer($layerHash, $parentLayers) {
$this->layers[$layerHash] = [
'type' => 'read-only', // 标记为只读
'parent' => $parentLayers, // 记录下层是谁
'path' => "/var/lib/docker/overlay2/$layerHash/diff" // 层的文件路径
];
echo "创建镜像层 {$layerHash},像添加一本新漫画书\n";
}
// 创建容器的可写层
public function createContainerLayer($containerId, $imageLayers) {
$layerHash = md5(uniqid()); // 生成唯一标识
$this->layers[$layerHash] = [
'type' => 'read-write', // 标记为可写
'parent' => $imageLayers, // 下层是所有镜像层
'path' => "/var/lib/docker/overlay2/$layerHash/diff",
'work_path' => "/var/lib/docker/overlay2/$layerHash/work" // 临时工作目录
];
// 创建软链接,方便快速找到层
symlink($this->layers[$layerHash]['path'], "/var/lib/docker/overlay2/l/$containerId");
echo "为容器 {$containerId} 创建可写层 {$layerHash},像准备一张空白便签\n";
}
// 合并所有层,生成容器看到的文件系统
public function mountContainer($containerId, $mountPath) {
// 获取所有下层(镜像层)的路径
$lowerPaths = [];
foreach ($this->layers as $hash => $layer) {
if (in_array($hash, $this->layers[$containerId]['parent'])) {
$lowerPaths[] = "lowerdir={$layer['path']}";
}
}
// 添加当前可写层的路径
$lowerPaths[] = "upperdir={$this->layers[$containerId]['path']}";
$lowerPaths[] = "workdir={$this->layers[$containerId]['work_path']}";
// 用联合文件系统挂载(实际是调用 Linux 的 mount 命令)
$command = "mount -t overlay overlay -o " . implode(',', $lowerPaths) . " $mountPath";
exec($command);
echo "把所有层合并到 {$mountPath},像把所有透明纸叠在一起\n";
}
// 当容器修改文件时(写时复制)
public function modifyFile($containerId, $filePath) {
$layer = $this->layers[$containerId];
$sourceLayer = $this->findFileSourceLayer($filePath, $layer['parent']);
if ($sourceLayer) {
// 从下层复制文件到当前可写层
$sourceFile = "{$sourceLayer['path']}/" . substr($filePath, 1);
$targetFile = "{$layer['path']}/" . substr($filePath, 1);
copy($sourceFile, $targetFile);
echo "从下层复制文件到可写层,像把书上的内容抄到便签\n";
} else {
// 文件是新建的,直接存在可写层
touch($targetFile);
echo "新建文件,存在便签上\n";
}
}
// 查找文件在哪个下层
private function findFileSourceLayer($filePath, $parentHashes) {
foreach ($parentHashes as $hash) {
$layerPath = "{$this->layers[$hash]['path']}/" . substr($filePath, 1);
if (file_exists($layerPath)) {
return $this->layers[$hash];
}
}
return false;
}
}
六、思维导图:overlay2 的全貌
/var/lib/docker/overlay2 的世界
├── 核心作用:管理容器和镜像的文件层,像叠透明纸
├── 组成部分:
│ ├── 镜像层(只读):多个容器共享的「基础画纸」
│ ├── 容器层(可写):每个容器独有的「便签纸」
│ ├── mnt 目录:合并所有层的「魔法桌面」
│ └── work 目录:临时修改文件的「草稿本」
├── 工作原理:
│ ├── 联合文件系统:把多层文件合并显示
│ ├── 写时复制:修改时先复制到可写层,不影响下层
│ └── 软链接:快速找到层的路径,像书签
├── 使用场景:
│ ├── 多容器共享镜像:节省空间,像多人看同一本书
│ ├── 快速创建容器:只加便签,不复制整本书
│ └── 数据持久化:重要数据单独存,不放在便签上
└── 底层技术:
├── Linux 内核的 overlay 或 overlay2 驱动
├── mount 命令实现文件系统挂载
└── 写时复制(Copy-on-Write)机制
七、总结:透过现象看本质
overlay2 就像一个「文件层管理员」,它的核心魔法是:
- 用「透明纸叠放」的方式管理镜像和容器的文件,让它们共享底层资源又互不干扰;
- 当你修改文件时,它偷偷把修改内容放到最上面的「便签纸」上,不破坏下面的「原书」;
- 最终呈现给容器的,是所有纸叠在一起的「完整画面」,但实际存储的是每一层的差异,节省了大量空间。