Files
docker_practice/14_implementation/14.4_ufs.md
2026-02-21 10:47:37 -08:00

222 lines
5.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 14.4 联合文件系统
联合文件系统UnionFS Docker 镜像分层存储的基础它允许将多个目录挂载为同一个虚拟文件系统
### 什么是联合文件系统
联合文件系统UnionFS是一种**分层轻量级**的文件系统它将多个目录"联合"挂载到同一个虚拟目录形成一个统一的文件系统视图
> **核心思想**将多个只读层叠加最上层可写形成完整的文件系统
```mermaid
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
```
---
### 为什么 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. 联合挂载所有层
---
### Copy-on-Write写时复制
当容器修改只读层中的文件时
```mermaid
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. 后续读取使用可写层的版本
---
### 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
```
---
### overlay2 工作原理
overlay2 是目前最推荐的存储驱动
```mermaid
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 文件标记删除 |
---
### 查看镜像层
运行以下命令
```bash
## 查看镜像的层信息
$ 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. 减少镜像层数
```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
排除不需要的文件可以
- 减小构建上下文
- 避免创建不必要的层
---