Files
docker_practice/12_implementation/12.4_ufs.md
T
yeasy ef2625531d style: 含中文的括号统一为全角(336 处 / 110 文件)
全书此前是真·五五开:含中文的括号 336 处半角 : 336 处全角,而且**在同一文件
内混用**——`18_security/18.1_kernel_ns.md` 第 11/17/20 行写 `(如 runC)`,
第 31 行写 `(用户命名空间映射)`。这不是体例问题,是它自己跟自己不一致。

集群里其余 13 本都一边倒(llm_internals 5 : 1049、oc_guide 2 : 713、
forward_deployed_engineering_guide 0 : 108),blockchain_guide 已在 2026-08-05
按同一方向统一过 97 处,故取全角,而非另立新规。全角括号前后不留空格,
原先 `概念 (镜像、容器、仓库) 和` 两侧的空格一并收掉。

**纯拉丁内容的半角括号一个没动**——`新建一个层 (Layer)`、`项目 (project)`
保持原样:改动前后「半角且内容纯拉丁」的数量都是 **1163**,而「半角且含中文」
从 **336 变成 0**。

其余验证:改动前后各自抹掉全部空白与圆括号后**逐字节相同**(两侧均 19559 字符);
左右括号总数改动前后都是 399 : 399,证明没有吞括号也没有配错对。
`check_project_rules` 202/202、`check_emphasis` 202/202、37 tests OK、
根 `format_checker` 与 `validate_codeblocks` 干净、`git diff --check` 干净。
2026-08-24 13:13:23 -07:00

7.3 KiB
Raw Blame History

12.4 联合文件系统

联合文件系统 (UnionFS) 是 Docker 镜像分层存储的基础,它允许将多个目录挂载为同一个虚拟文件系统。

12.4.1 什么是联合文件系统

联合文件系统 (UnionFS) 是一种 分层、轻量级 的文件系统,它将多个目录 “联合” 挂载到同一个虚拟目录,形成一个统一的文件系统视图。

核心思想:将多个只读层叠加,最上层可写,形成完整的文件系统。

flowchart TD
    ContainerFS["容器看到的文件系统<br/>/bin /etc /lib /usr /var /app /data"]
    UnionFS["UnionFS 联合挂载"]

    ContainerLayer["容器层 (读写) : /app/data/log.txt (新写入)"]
    ImageLayer3["镜像层3 (只读) : /app/app.py"]
    ImageLayer2["镜像层2 (只读) : /usr/local/bin/python"]
    ImageLayer1["镜像层1 (只读) : /bin /etc /lib (基础系统)"]

    ContainerFS --> UnionFS
    UnionFS --> ContainerLayer --> ImageLayer3 --> ImageLayer2 --> ImageLayer1

12.4.2 为什么 Docker 使用联合文件系统

Docker 选择联合文件系统作为其存储驱动,主要基于以下几个核心优势。

1. 镜像分层复用

flowchart TD
    Nginx["nginx:alpine"] --> Alpine["alpine:3.19 (共享基础层)"]
    MyApp["myapp:latest"] --> Alpine

多个镜像共享相同的底层,节省磁盘空间。

2. 快速构建

每个 Dockerfile 指令创建一层,只有变化的层需要重建:

FROM node:22          # 层1:基础镜像
COPY package.json ./  # 层2:依赖定义
RUN npm install       # 层3:安装依赖
COPY . .              # 层4:应用代码

代码变化时,只需重建层 4,层 1-3 使用缓存。

3. 容器启动快

容器启动时不需要复制镜像,只需:

  1. 在镜像层上创建一个薄的可写层
  2. 联合挂载所有层

12.4.3 Copy-on-Write:写时复制

当容器修改只读层中的文件时:

flowchart LR
    subgraph Before ["修改前"]
        direction TB
        B_C["容器层 (空)"]
        B_I["镜像层<br/>/etc/nginx.conf"]
    end

    subgraph After ["修改后"]
        direction TB
        A_C["容器层<br/>/etc/nginx.conf ← 复制到容器层后修改"]
        A_I["镜像层<br/>/etc/nginx.conf (原文件仍在,但被遮蔽)"]
    end
    B_C --- B_I
    A_C --- A_I

流程:

  1. 从只读层读取文件
  2. 复制到容器的可写层
  3. 在可写层中修改
  4. 后续读取使用可写层的版本

12.4.4 Docker 支持的存储驱动

Docker 的存储驱动经历了从早期各式各样的机制(如 aufs, devicemapper),到被广泛使用的现代经典 graph driver (overlay2),再到当下(Engine v29.x 及以后)在新安装场景中默认启用的 containerd 镜像存储引擎(containerd image store) 的演进。

存储后端 / 驱动 核心特性说明 推荐程度
containerd image store (v29.x 新一代默认后端,新装默认)基于 containerd 的 snapshotters,原生支持 OCI image index、多架构镜像与 Attestations 构建溯源元数据存储;启用 userns-remap 时不可用。 ✅强烈推荐(现代默认)
overlay2 (经典 Graph Driver)传统架构下的现代 Linux 默认驱动,性能优秀,但在处理复杂溯源元数据(索引)时受限。 ✅推荐(主要后备)
aufs 早期默认,已从现代 Docker Engine 移除 历史资料
btrfs/zfs 使用原生稳定文件系统快照能力 特定场景
devicemapper 块设备级存储,已从现代 Docker Engine 移除 历史资料
vfs 不使用 CoW,每层完整复制 仅测试

Classic Graph Drivers 与 Snapshotters 的核心差异

传统模型(如 overlay2)将镜像拉取解包的过程由 Docker 的 graph drivers 处理。而新的 containerd image store 则将这一职责彻底下放给了 containerd 自身的 snapshotters(底层在 Linux 发行版通常依然利用操作系统的 overlayfs)。这种架构改变带来了:

  1. 本地免拉取查看多平台镜像 index manifest 与 attestations (SBOM、Provenance)。
  2. 允许 Docker 使用 containerd snapshotters 管理镜像层,便于与现代 OCI 生态能力对齐。它不表示 kubelet/containerd 会自动复用 Docker 本地镜像;Kubernetes 镜像分发仍应以 registry、镜像拉取策略和运行时配置为准。

查看当前存储驱动与后端

## 查看默认存储驱动
$ docker info | grep "Storage Driver"
Storage Driver: overlay2

## 在 Engine v29.x 中,可以通过如下输出验证是否开启了 containerd 镜像后端:
$ docker info -f '{{ .DriverStatus }}'
[[driver-type io.containerd.snapshotter.v1]]

12.4.5 overlay2 工作原理

在经典 graph driver 体系下,overlay2 仍是 Linux 上的主要推荐驱动;Docker Engine 29 新安装场景默认走 containerd image store/snapshotter。

flowchart TD
    Merged["merged (合并视图)<br/>容器看到的完整文件系统"]
    OverlayFS["OverlayFS"]

    Upper["upper<br/>(容器层)<br/>读写"]
    Lower2["lower2<br/>(镜像层)<br/>只读"]
    Lower1["lower1<br/>(基础层)<br/>只读"]

    Merged --> OverlayFS
    OverlayFS --> Upper
    OverlayFS --> Lower2
    OverlayFS --> Lower1
  • lowerdir:只读的镜像层(可以有多个)
  • upperdir:可写的容器层
  • workdir:OverlayFS 的工作目录
  • merged:联合挂载后的视图

文件操作行为

操作 行为
读取 从上到下查找第一个匹配的文件
创建 在 upper 层创建
修改 如果在 lower 层,先复制到 upper 层再修改
删除 在 upper 层创建 whiteout 文件标记删除

12.4.6 查看镜像层

## 查看镜像的层信息

$ docker history nginx:alpine
IMAGE          CREATED       CREATED BY                                      SIZE
a6eb2a334a9f   2 weeks ago   CMD ["nginx" "-g" "daemon off;"]                0B
<missing>      2 weeks ago   STOPSIGNAL SIGQUIT                              0B
<missing>      2 weeks ago   EXPOSE map[80/tcp:{}]                           0B
<missing>      2 weeks ago   ENTRYPOINT ["/docker-entrypoint.sh"]            0B
<missing>      2 weeks ago   COPY 30-tune-worker-processes.sh /docker-ent…   4.62kB
...

## 查看层的存储位置

$ docker inspect nginx:alpine --format '{{json .GraphDriver.Data}}' | jq
{
  "LowerDir": "/var/lib/docker/overlay2/.../diff:/var/lib/docker/overlay2/.../diff",
  "MergedDir": "/var/lib/docker/overlay2/.../merged",
  "UpperDir": "/var/lib/docker/overlay2/.../diff",
  "WorkDir": "/var/lib/docker/overlay2/.../work"
}

12.4.7 最佳实践

为了构建高效、轻量的镜像,我们在使用联合文件系统时应注意以下几点。

1. 减少镜像层数

## ❌ 每条命令创建一层

RUN apt-get update
RUN apt-get install -y nginx
RUN rm -rf /var/lib/apt/lists/*

## ✅ 合并为一层

RUN apt-get update && \
    apt-get install -y nginx && \
    rm -rf /var/lib/apt/lists/*

2. 避免在容器中写入大量数据

容器层的写入性能低于直接写入。大量数据应使用:

  • 数据卷 (Volume)
  • 绑定挂载 (Bind Mount)

3. 使用 .dockerignore

排除不需要的文件可以:

  • 减小构建上下文
  • 避免创建不必要的层