Files
docker_practice/13_implementation/ufs.md

9.0 KiB
Raw Blame History

联合文件系统

什么是联合文件系统

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

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

┌─────────────────────────────────────────────────────────┐
│                    容器看到的文件系统                    │
│    /bin  /etc  /lib  /usr  /var  /app  /data           │
└────────────────────────┬────────────────────────────────┘
                         │
         ┌───────────────┴───────────────┐
         │      UnionFS 联合挂载          │
         └───────────────┬───────────────┘
                         │
┌────────────────────────┴────────────────────────────────┐
│  容器层 (读写)     │ /app/data/log.txt (新写入)         │
├────────────────────┼────────────────────────────────────│
│  镜像层3 (只读)    │ /app/app.py                        │
├────────────────────┼────────────────────────────────────│
│  镜像层2 (只读)    │ /usr/local/bin/python              │
├────────────────────┼────────────────────────────────────│
│  镜像层1 (只读)    │ /bin /etc /lib (基础系统)          │
└────────────────────┴────────────────────────────────────┘

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

1. 镜像分层复用

nginx:alpine          myapp:latest
     │                     │
     └────────┬────────────┘
              │
              ▼
         alpine:3.19 (共享基础层)

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

2. 快速构建

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

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

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

3. 容器启动快

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

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

Copy-on-Write写时复制

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

修改前:                           修改后:
┌─────────────────────┐           ┌─────────────────────┐
│ 容器层 (空)          │           │ 容器层              │
├─────────────────────┤           │ /etc/nginx.conf  ←──┼── 复制到容器层后修改
│ 镜像层              │           ├─────────────────────┤
│ /etc/nginx.conf     │           │ 镜像层              │
└─────────────────────┘           │ /etc/nginx.conf     │ (原文件仍在,但被遮蔽)
                                  └─────────────────────┘

流程

  1. 从只读层读取文件
  2. 复制到容器的可写层
  3. 在可写层中修改
  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

查看当前存储驱动

$ docker info | grep "Storage Driver"
Storage Driver: overlay2

overlay2 工作原理

overlay2 是目前最推荐的存储驱动:

┌─────────────────────────────────────────────────────────────┐
│                    merged合并视图                        │
│    容器看到的完整文件系统                                    │
└─────────────────────────┬───────────────────────────────────┘
                          │
          ┌───────────────┴───────────────┐
          │         OverlayFS             │
          └───────────────┬───────────────┘
                          │
         ┌────────────────┼────────────────┐
         ▼                ▼                ▼
┌─────────────┐   ┌─────────────┐   ┌─────────────┐
│   upper     │   │   lower2    │   │   lower1    │
│  (容器层)   │   │  (镜像层)   │   │  (基础层)   │
│   读写      │   │   只读      │   │   只读      │
└─────────────┘   └─────────────┘   └─────────────┘
  • lowerdir:只读的镜像层(可以有多个)
  • upperdir:可写的容器层
  • workdirOverlayFS 的工作目录
  • merged:联合挂载后的视图

文件操作行为

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

查看镜像层

# 查看镜像的层信息
$ 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"
}

最佳实践

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

排除不需要的文件可以:

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

本章小结

概念 说明
UnionFS 将多层目录联合挂载为一个文件系统
Copy-on-Write 写时复制,修改时才复制到可写层
overlay2 Docker 默认推荐的存储驱动
分层好处 镜像复用、快速构建、快速启动

延伸阅读