## 12.4 联合文件系统 联合文件系统 (UnionFS) 是 Docker 镜像分层存储的基础,它允许将多个目录挂载为同一个虚拟文件系统。 ### 12.4.1 什么是联合文件系统 联合文件系统 (UnionFS) 是一种 **分层、轻量级** 的文件系统,它将多个目录 “联合” 挂载到同一个虚拟目录,形成一个统一的文件系统视图。 > **核心思想**:将多个只读层叠加,最上层可写,形成完整的文件系统。 ```mermaid flowchart TD ContainerFS["容器看到的文件系统
/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. 镜像分层复用 如下代码块所示,展示了相关示例: ```mermaid flowchart TD Nginx["nginx:alpine"] --> Alpine["alpine:3.19 (共享基础层)"] MyApp["myapp:latest"] --> Alpine ``` 多个镜像共享相同的底层,节省磁盘空间。 #### 2. 快速构建 每个 Dockerfile 指令创建一层,只有变化的层需要重建: ```docker FROM node:20 # 层1:基础镜像 COPY package.json ./ # 层2:依赖定义 RUN npm install # 层3:安装依赖 COPY . . # 层4:应用代码 ``` 代码变化时,只需重建层 4,层 1-3 使用缓存。 #### 3. 容器启动快 容器启动时不需要复制镜像,只需: 1. 在镜像层上创建一个薄的可写层 2. 联合挂载所有层 --- ### 12.4.3 Copy-on-Write (写时复制) 当容器修改只读层中的文件时: ```mermaid flowchart LR subgraph Before ["修改前"] direction TB B_C["容器层 (空)"] B_I["镜像层
/etc/nginx.conf"] end subgraph After ["修改后"] direction TB A_C["容器层
/etc/nginx.conf ← 复制到容器层后修改"] A_I["镜像层
/etc/nginx.conf (原文件仍在,但被遮蔽)"] end B_C --- B_I A_C --- A_I ``` **流程**: 1. 从只读层读取文件 2. 复制到容器的可写层 3. 在可写层中修改 4. 后续读取使用可写层的版本 --- ### 12.4.4 Docker 支持的存储驱动 Docker 可使用多种联合文件系统实现: | 存储驱动 | 说明 | 推荐程度 | |---------|------|---------| | **overlay2**| 现代 Linux 默认驱动,性能优秀 | ✅**推荐** | | **aufs** | 早期默认,兼容性好 | 遗留系统 | | **btrfs** | 使用 Btrfs 子卷 | 特定场景 | | **zfs** | 使用 ZFS 数据集 | 特定场景 | | **devicemapper** | 块设备级存储 | 遗留系统 | | **vfs** | 不使用 CoW,每层完整复制 | 仅测试 | #### 各发行版推荐 | Linux 发行版 | 推荐存储驱动 | |-------------|-------------| | Ubuntu 16.04+ | overlay2 | | Debian Stretch+ | overlay2 | | CentOS 7+ | overlay2 | | RHEL 8+ | overlay2 | | Fedora | overlay2 | #### 查看当前存储驱动 ```bash $ docker info | grep "Storage Driver" Storage Driver: overlay2 ``` --- ### 12.4.5 overlay2 工作原理 overlay2 是目前最推荐的存储驱动: ```mermaid flowchart TD Merged["merged (合并视图)
容器看到的完整文件系统"] OverlayFS["OverlayFS"] Upper["upper
(容器层)
读写"] Lower2["lower2
(镜像层)
只读"] Lower1["lower1
(基础层)
只读"] Merged --> OverlayFS OverlayFS --> Upper OverlayFS --> Lower2 OverlayFS --> Lower1 ``` - **lowerdir**:只读的镜像层 (可以有多个) - **upperdir**:可写的容器层 - **workdir**:OverlayFS 的工作目录 - **merged**:联合挂载后的视图 #### 文件操作行为 | 操作 | 行为 | |------|------| | **读取** | 从上到下查找第一个匹配的文件 | | **创建** | 在 upper 层创建 | | **修改** | 如果在 lower 层,先复制到 upper 层再修改 | | **删除** | 在 upper 层创建 whiteout 文件标记删除 | --- ### 12.4.6 查看镜像层 ```bash ## 查看镜像的层信息 $ docker history nginx:alpine IMAGE CREATED CREATED BY SIZE a6eb2a334a9f 2 weeks ago CMD ["nginx" "-g" "daemon off;"] 0B 2 weeks ago STOPSIGNAL SIGQUIT 0B 2 weeks ago EXPOSE map[80/tcp:{}] 0B 2 weeks ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B 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. 减少镜像层数 如下代码块所示,展示了相关示例: ```docker ## ❌ 每条命令创建一层 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 排除不需要的文件可以: - 减小构建上下文 - 避免创建不必要的层 ---