13
2025
06
16:25:47

/var/lib/docker/overlay2到底是干什么的?

一、用生活类比理解 overlay2 的本质

想象你有一叠透明的画画纸:

  • 最下面一张是「基础画纸」(相当于 Docker 镜像),上面画着固定的图案(程序和系统文件)。

  • 每次在上面盖一张新的「透明纸」(相当于容器的层),你可以在新纸上修改或添加图案,不会破坏下面的纸。

  • overlay2 就是管理这叠纸的「文件夹」,让所有纸看起来像一张完整的画(容器运行时的文件系统)。

二、overlay2 的核心组成部分及作用
  1. 目录结构总览

    /var/lib/docker/overlay2/
    ├── l   # 软链接文件夹,指向具体层的路径
    ├── mnt # 容器挂载点,合并所有层的文件
    ├── <hash1>/ # 镜像层(只读)
    │   ├── diff/ # 存储该层的文件变更
    │   ├── link # 层的唯一标识
    │   └── lower # 记录下层的路径
    └── <hash2>/ # 容器层(可写)
        ├── diff/ # 存储容器运行时的修改
        ├── link # 层的唯一标识
        ├── lower # 记录所有下层镜像的路径
        └── work # 临时工作目录,用于文件修改
  2. 关键部分详解

    • 镜像层(只读层)
      像图书馆的书,多个容器可以共享同一本书(镜像层),节省空间。

    • 容器层(可写层)
      像在书上贴便签,容器运行时的修改(如新建文件、修改内容)都存在这里,不影响原书。

    • mnt 目录
      像「魔法桌面」,把所有层的文件合并显示,容器看到的就是这个合并后的文件系统。

三、overlay2 背后的工作原理(流程图)
┌──────────────────────────────────────────────────────────┐
│ 当启动容器时,overlay2 做了这些事:                        │
├───────────────────┬──────────────────────────────────────┤
│ 1. 找到镜像的所有只读层(如 A、B、C 层)                  │
│   ▼                                                          │
│ 2. 创建一个新的可写层 D,用于存储容器的修改                │
│   ▼                                                          │
│ 3. 在 mnt 目录中,用「联合文件系统」把 A+B+C+D 合并成一个  │
│   ▼                                                          │
│ 4. 容器访问的就是这个合并后的文件系统,修改会存在 D 层      │
└──────────────────────────────────────────────────────────┘

联合文件系统原理
就像叠透明纸,下面的层(镜像)是只读的,上面的层(容器)可以修改。当修改一个文件时:

  1. 先把原文件从下层「复制」到上层(称为「写时复制」)

  2. 在上层修改这个复制后的文件,下层文件保持不变

四、使用场景举例
  1. 多个容器共享同一镜像

    • 场景:运行 10 个 Nginx 容器,每个容器共享同一个 Nginx 镜像层,只需要存储一次镜像文件。

    • 好处:节省磁盘空间,像 10 个人看同一本漫画书,不用每人买一本。

  2. 容器快速创建和删除

    • 场景:测试环境中频繁创建、删除容器(如 CI/CD 部署)。

    • 原理:删除容器只需要删除可写层,镜像层不受影响,像撕掉便签,书还在。

  3. 数据持久化

    • 场景:容器中数据库的数据需要保存,即使容器删除也不丢失。

    • 做法:把数据目录挂载到宿主机(绕过 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 就像一个「文件层管理员」,它的核心魔法是:

  1. 用「透明纸叠放」的方式管理镜像和容器的文件,让它们共享底层资源又互不干扰;

  2. 当你修改文件时,它偷偷把修改内容放到最上面的「便签纸」上,不破坏下面的「原书」;

  3. 最终呈现给容器的,是所有纸叠在一起的「完整画面」,但实际存储的是每一层的差异,节省了大量空间。




推荐本站淘宝优惠价购买喜欢的宝贝:

image.png

本文链接:https://zblog.hqyman.cn/post/11590.html 非本站原创文章欢迎转载,原创文章需保留本站地址!

分享到:
打赏





休息一下~~


« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

请先 登录 再评论,若不是会员请先 注册

您的IP地址是: